Configuration · Référence
AGENTS.md
Project configuration and coding guidelines for AI agents, covering i18n, service patterns, Effect schemas, HTTP API groups, optimistic atoms, and lint/typecheck commands.
Dernière mise à jour le 11 octobre 2026
Overview
agents installs the AGENTS.md file that guides AI agents working in a KrakStack project. It covers i18n conventions, code architecture, Effect service patterns, schema and API documentation rules, and the commands used to validate changes.
Install
bunx shadcn@latest add @krak-stack/agentsSections
- i18n — Paraglide.js conventions, message file ownership, and English/French requirements
- Code Architecture — Frontend (TanStack Start, shadcn, Effect atoms) and backend (Effect SQL, PostgreSQL) structure
- Patterns — Server services, frontend client components, SQL schemas and migrations, API composition, schema annotations, and documentation rules
- Examples — Effect schema, API group, API builder, form dialog, data table, optimistic atom, Effect SQL service, migrations, and API entry point code
- Preferences — Arrow functions, no type assertions
- Checks —
bun check,bun lint,bun fmt
Usage
Place AGENTS.md at the project root so agent tooling can read it automatically:
npx shadcn@latest add https://krakstack.net/r/agents.jsonAfter installation, open AGENTS.md and adjust the folder structure, patterns, and check commands to match your project.
Preview
AGENTS
Core Rules
- If
PROJECT_AGENTS.mdexists in the repository root, read it immediately and treat it as additional mandatory project instructions. - Prefer the smallest correct change that fits the existing architecture.
- Keep frontend and backend concerns separated unless a feature explicitly spans both.
- Use project conventions already present in nearby files before introducing new patterns.
- Do not edit generated or registry-managed files unless explicitly requested.
i18n
- English and French are required for all public-facing strings.
- Translate public-facing strings with paraglide.js and the Vite plugin.
- Let the Paraglide Vite plugin compile translations automatically during development and builds. Do not run manual Paraglide compilation commands (such as
paraglide-js compile) or rewritesrc/paraglide/; doing so can conflict with the running Vite server and trigger reload loops. - Store translations in
src/messages/en.jsonandsrc/messages/fr.json. - Import generated messages from
src/paraglide/messagesinstead of hardcoding UI copy.
Architecture
The application is divided into two areas: frontend and backend.
Frontend
- React with TanStack Start and TanStack Router.
- shadcn UI primitives and installed registry components.
- Effect service state for data loading and mutations.
- Prefer existing app form, table, dialog, and data-fetching patterns before adding new abstractions.
Client State And Mutations
- Define server queries with Effect Atom and keep authoritative query atoms wrapped in
Atom.optimistic(...)when previous successful data should remain visible during refresh. - Default to
Atom.optimisticFn(...)for create, update, and delete operations when the expected client-side result can be calculated safely. - Do not expose a plain
ApiClient.mutation(...)directly to a form when an existing query atom can be updated optimistically and rolled back on failure. - Reconcile optimistic state with authoritative server data after a successful mutation by invalidating the relevant queries.
- Keep invalidation policy beside the mutation definition rather than repeating domain dependencies in form submit handlers when practical.
- Use a plain mutation when the result cannot be predicted safely, including imports, generated results, complex server transformations, and destructive operations with unknown side effects.
- For paginated, filtered, sorted, or localized query families, do not guess which cached results a mutation affects. Scope the optimistic mutation to an explicit query instance, or introduce a canonical entity store when updates must appear across every query.
- Name query-retention and mutation-optimism concepts precisely:
Atom.optimistic(queryAtom)preserves provisional or previous query state, whileAtom.optimisticFn(...)applies an optimistic mutation reducer with rollback. - Use
src/agent-examples/service/atom.tsin the KrakStack reference repository as the required starting pattern for optimistic CRUD atoms, then adapt it to the application's realAtomHttpApiquery and mutation atoms.
Backend
- Effect application services.
- Effect Postgres with
SqlClientandSqlSchemafor database access. - Effect HttpApi, HttpServer, OpenAPI, and OpenTelemetry for API and runtime concerns.
- Effect durable workflows for long-running, retryable, or resumable orchestration.
Folder Structure
public/contains static assets.scripts/contains build and utility scripts.tmp/contains local temporary files that should not be committed.src/components/contains React components.src/components/ui/contains shadcn-managed primitives. Do not edit directly.src/db/contains the Effect SQL migration runner.src/db/migrations/contains versioned SQL migrations.src/hooks/contains shared React hooks.src/lib/contains shared utilities.src/messages/contains i18n source files.src/paraglide/contains generated i18n runtime. Do not edit directly.src/routes/contains TanStack Start file-based routes.src/routes/docs/contains documentation pages.src/services/contains Effect service definitions, API handlers, schemas, and client state.src/api.tsdefines the root Effect API.
Code Practices
- Prefer arrow functions
() => voidover function expressionsfunction () {}except where Effect generator APIs requirefunction*. - Avoid
as any,as Type, andas unknownunless absolutely necessary. - Use Effect
Schemafor validation. Do not use Zod or other validation libraries. - Prefer Effect-native integrations over ad hoc boundaries: use
FetchHttpClient/HttpClientinstead of rawfetch, EffectSchemacodecs such asSchema.fromJsonString(...)andHttpClientResponse.schemaBodyJson(...)instead of manualJSON.parseor custom validation, and typed Effect errors instead of broadtry/tryPromisewrappers. - Use
Effect.tryorEffect.tryPromiseonly when wrapping a non-Effect API that has no suitable Effect adapter; keep the boundary as small as possible and map failures into domain-specific errors. - Annotate schemas with
.annotate({ identifier: "Name" }). - Use
Schema.toStandardSchemaV1(...)when integrating Effect schemas with form validators. - Use
Effect.fnfor service methods when practical. - Expose
SqlSchemaoperations directly when they already provide the complete service method; do not add pass-throughEffect.fnwrappers. - Add OpenTelemetry through Effect runtime patterns where relevant.
Schema
- Use Effect
Schemafor all parsing, decoding, validation, and type-safe boundary checks. - Define reusable schemas in the nearest
schema.tsfile and annotate them with.annotate({ identifier: "Name" }). - Validate untrusted inputs at boundaries using Effect schema decoders, including API payloads, query params, route params, form inputs, external API responses, environment variables, JSON blobs, and persisted data.
- Do not write custom runtime validation such as
typeof value === "object",Array.isArray(value), manual property checks, custom type guards, or ad hocJSON.parsevalidation when an EffectSchemacan express the shape. - Prefer Effect codecs and helpers such as
Schema.decodeUnknown,Schema.decodeUnknownSync,Schema.fromJsonString(...), andHttpClientResponse.schemaBodyJson(...)over manual parsing. - Keep validation failures typed and explicit. Map schema parse errors into domain-specific errors where needed instead of throwing broad errors.
- Use custom predicates only inside Effect schema refinements or filters, and only when the rule cannot be represented with built-in schema combinators.
Services
Use service-based design for CRUD, features, integrations, and related domain concerns.
A typical service should use this structure:
src/services/<name>/schema.tsdefines Effect schemas, payload schemas, route params, and standard schema exports.src/services/<name>/index.tsimplements the EffectContext.Serviceand exposes production and test layers where needed.src/services/<name>/workflows.tsdefines versioned durable workflows and their activity orchestration.src/services/<name>/api.group.tsdefines the HttpApiGroup contract.src/services/<name>/api.builder.tswires the service into the root API with auth and error mapping.src/services/<name>/client/atom.tsdefines query and mutation atoms.src/services/<name>/client/form.tsxdefines reusable create/edit forms.src/services/<name>/client/table.tsxdefines data tables and row actions.
Service methods should accept object inputs, scope by the current user or tenant where applicable, and avoid exposing cross-tenant data.
Database
- Depend directly on
SqlClient.SqlClientin persistence services instead of wrapping it in an application database service. - Define reusable SQL request and result schemas in the service's
schema.ts, then useSqlSchema.findAll,findOne, orfindOneOptionfor request encoding, result decoding, and row cardinality. - Prefer explicit Effect schemas for straightforward select, create, and update representations. Introduce
Model.Classonly when database, insert, update, and JSON variants differ enough to justify it. - Write parameterized SQL explicitly and include the current user or tenant in every applicable read, update, and delete predicate.
- Do not use an ID-only generated repository for tenant-owned records when it cannot enforce the tenant predicate.
- Define database-agnostic migration loaders with
Migrator.fromRecord(...)and write their effects againstSqlClient.SqlClient. - Prefer the generic
MigratorandSqlClientAPIs when no PostgreSQL-specific capability is needed. UsePgMigrator,PgClient, or another dialect module only for its runner, layer, or dialect-specific behavior. - Run PostgreSQL migration loaders through
PgMigrator.run(...)during application layer acquisition before dependent services are exposed. - Treat migration names and completed migrations as compatibility boundaries. Add a new migration instead of editing one that may have run in another environment.
Workflows
Use Effect durable workflows for operations that must survive interruption, retry individual steps, resume later, or coordinate multiple services over time.
- Define the shared
ClusterWorkflowEnginelayer insrc/services/workflow.tsand provide its database and runner dependencies at the application composition root. - Define service-owned workflows in
src/services/<name>/workflows.tswithWorkflow.make(...)and export theirtoLayer(...)implementation. - Version workflow and activity names, such as
CreateExampleV1andPersistExampleV1. Treat persisted workflow names and payload schemas as compatibility boundaries. - Give every workflow a stable idempotency key derived from a caller-provided request or domain identifier.
- Wrap side effects in
Activity.make(...)and define serializable Effect schemas for workflow payloads, successes, and typed errors. - Keep HTTP, authentication, and request-specific concerns at the API boundary. Pass validated actor or tenant identifiers into the workflow payload.
- Keep durable orchestration in
workflows.ts; keep reusable domain and persistence operations in the service. - Compose each workflow layer with its service dependencies, then provide the shared workflow engine layer at the application root.
API
- Define the root API in
src/api.ts. - Merge service API groups into the root API with
.add(...). - Keep OpenAPI annotations on the root API.
- OpenAPI documentation is served at
/api/docs. - MCP server support is served at
/api/mcp. Register Effect toolkits withMcpServer.registerToolkitand compose the transport withMcpServer.layerHttp; use@krak-stack/httpapi-toolkitto generate tools from an HttpApi. Select exposed operations explicitly and keep authentication and authorization application-owned. - CLI support should use
@krak-stack/httpapi-cli.
Tooling
- Use KrakStack Components where possible and keep installed registry components current.
- Install KrakStack registry items with shadcn using the
@krak-stackregistry alias configured incomponents.json; do not copy registry item files manually unless explicitly requested. - Before creating a custom component, check the shadcn MCP server for a compatible component or registry item.
- Use shadcn through the registry workflow. If needed, initialize MCP with
bunx --bun shadcn@latest mcp init --client opencode.
Testing
- Test meaningful behavior and regression risks: business rules, authorization, tenant isolation, persistence, state transitions, validation, and error handling. Include focused regression tests for bug fixes when practical.
- Do not add or run tests solely for copy, translations, documentation, styling, layout, or static markup unless explicitly requested. Test UI changes only when meaningful behavior changes.
- Reuse existing coverage and prefer the smallest test at the lowest effective layer; do not duplicate cases across unit, integration, and end-to-end tests.
- Use Vitest with
@effect/vitest, importingdescribe,expect, anditfrom it. Keep*.test.ts/*.test.tsxbeside code; useit.effectandEffect.provide(...)for Effect programs. - Isolate mutable state with fresh test layers or explicit resets.
- Persistence tests must use real Postgres through
TEST_DATABASE_URL, neverDATABASE_URL; pure logic needs no database. Run migrations first and useSqlClientwith parameterized SQL for setup and cleanup.
End-to-End Testing
- Use Playwright selectively for critical journeys requiring a browser or frontend-to-API integration; keep specs in
e2e/*.spec.tsand lower-level edge cases in Vitest. - Assert observable outcomes with accessible locators. Avoid incidental prose, CSS classes, DOM structure, and presentation snapshots unless they are explicit requirements.
- Use unique, deterministic data, isolate sessions, and clean up persistent state. Database-backed tests must use
TEST_DATABASE_URL, neverDATABASE_URL. - E2E tests are slow: run them as final validation after implementation, not iteratively after each edit; rerun only to verify fixes for E2E failures. Use focused coverage with
bun run test:e2e -- e2e/<name>.spec.tsorbun run test:e2e -- --grep "<test name>"; reserve the full suite for broad changes, insufficient focused coverage, or explicit requests. - Diagnose failures with traces or screenshots; do not weaken assertions, add arbitrary sleeps, or increase timeouts without investigating.
Checks
- Match checks to risk; do not run everything by default. Run focused tests for behavior changes and broader suites only when needed.
- Documentation-only changes need no application tests or type checks. For copy or presentation changes, use formatting, compilation checks, or visual review only when useful.
- Use Bun 1.4.3's
bun checkcommand for TypeScript changes when practical; scopebun lintandbun fmtto changed files when supported and avoid unrelated churn. - Report checks run and blockers; never imply unrun checks passed.
Examples
KrakStack examples are the canonical architecture reference for this project. Prefer the configured krakstack project reference. If it is unavailable, use https://github.com/krakcons/krakstack/tree/main/src/agent-examples.
Before implementing or substantially refactoring one of the areas below, read the corresponding KrakStack example and the nearest equivalent implementation in this repository.
| Task | Required KrakStack reference |
|---|---|
| Effect service | src/agent-examples/service/service.ts |
| Effect schemas | src/agent-examples/service/schema.ts |
| Effect SQL migrations | src/agent-examples/database/migrations.ts |
| Durable workflows | src/agent-examples/service/workflows.ts |
| Workflow engine layer | src/services/workflow.ts |
| HttpApi contract | src/agent-examples/service/api.group.ts |
| HttpApi handlers | src/agent-examples/service/api.builder.ts |
| Root API registration | src/agent-examples/service/api-entry.ts |
| Client atoms | src/agent-examples/service/atom.ts |
| Forms | src/agent-examples/service/form.tsx |
| Tables | src/agent-examples/service/table.tsx |
<!-- intent-skills:start -->
Skill Loading
Before substantial work:
- Skill check: run
npx @tanstack/intent@latest list, or use skills already listed in context. - Skill guidance: if one local skill clearly matches the task, run
npx @tanstack/intent@latest load <package>#<skill>and follow the returnedSKILL.md. - Monorepos: when working across packages, run the skill check from the workspace root and prefer the local skill for the package being changed.
- Multiple matches: prefer the most specific local skill for the package or concern you are changing; load additional skills only when the task spans multiple packages or concerns.
<!-- intent-skills:end -->