AGENTS.md@packages/db · git:20261006.8886800 · 2026-10-06 · sha256 832c5c225c64002e
AGENTS.md@packages/db git:20261006.8886800A
Immutable. This exact content never changes and is served at /api/v1/blob/832c5c225c64002e.
# AGENTS.md — Database Package Rules These rules apply to `packages/db`. Read `packages/db/README.md`, then the owning schema, query module and tests before changing persistence behavior. ## Ownership - This package owns the shared platform D1 schema, Drizzle client factories, relations, query statements, persistence result types, and D1-parity tests. - Applications own authentication, input validation, orchestration, and API contract normalization. Do not construct transport objects in query modules. - Direct D1 access is allowed only for the exact checked owners in `scripts/db-access-exceptions.json`. Drizzle's bound `sql` template inside a query owner is not direct-D1 access. ## Layout And Imports - A small domain uses `src/queries/<domain>.ts`. - A domain with independent capabilities uses leaf files under `src/queries/<domain>/<capability>.ts`. - Do not create `queries/index.ts`, a domain `index.ts`, or a sibling facade that re-exports query files. Consumers import the exact leaf subpath. - Schema files are grouped by relational domain under `src/schema`. Every model must be exported through a Drizzle Kit schema input. A table needs a Relations v2 definition only when it is queried through `db.query.*`. - Use direct schema imports in leaf modules. The whole-schema surface exists for Drizzle initialization and genuinely aggregate schema work. ## Query Contract - Query functions that access D1 take the narrowest package client first: `DbClient` when they use Relations v2 (`db.query.*`), otherwise `DbQueryClient` for Core API builders. Applications must create either client with the matching `@tedix/db` factory; they do not initialize Drizzle locally. Keep tenant scope, ordering, pagination, chunking, and database result types inside the query owner. - Prefer typed builders and Relations v2. Use bound `sql` expressions when the builder cannot express the operation clearly. - Use `build<Action>Statement()` for an unexecuted statement intended for `db.batch()` composition. - D1 batches are sequential, non-concurrent transactions. Every statement must be constructible before execution; application branching cannot occur between statements. <!-- codex:rule slug=no-d1-transaction level=MUST owner=database state=advisory --> - Never call `db.transaction()` on D1. Use a preconstructed batch, one conditional/CAS statement, constraint-conflict handling, or a serialized DO/Workflow coordinator as the operation requires. - Every joined SQL output name must be unique. Direct RC.4 selects use array mode, but D1 batch results use object rows and collapse duplicate names before Drizzle maps them. Use `prefixedColumns()` or an explicit alias. - Use `getColumns()`; `getTableColumns()` is deprecated in Drizzle v1. ## Types And Names - SQL identifiers are snake_case; TypeScript table and column properties are camelCase. Preserve existing physical index names unless a real schema change requires migration; naming-only D1 migrations are not cleanup. - Infer table rows and inserts from `$inferSelect` and `$inferInsert`. Do not hand-copy a table shape. - Name raw table types `<Entity>Row` / `New<Entity>Row` when a transport type of the same entity exists. Query-specific projections use `<Operation>Result`. - Name required query inputs `<Operation>Params`; reserve `Options` for optional behavior such as pagination, ordering, or feature switches. - Persisted JSON uses an explicit domain/contract type. Truly open JSON uses `Record<string, JsonValue>`, never `Record<string, unknown>`. - Use validators from `drizzle-orm/zod`; do not add the standalone `drizzle-zod` package. ## Validation - Migration history is append-only. Wrangler reads `drizzle/*/migration.sql` directly and records that relative path in `tedix_drizzle_migrations`; never rename, reorder, remove, or edit an entry already recorded in `migration-integrity.json`. - Generate with `db:migrate:generate`; do not hand-create a timestamp or invoke download-on-demand Drizzle/Wrangler binaries. `db:migrate:check` must pass the Drizzle history gate and the complete empty-D1 chain rehearsal. - Live migrations are separate operator work, not local development. Use the authorized installation release procedure, capture its recovery point, and verify the target ledger and schema. Never infer a database target from an example or use a direct apply to bypass the release procedure. Run the narrowest relevant set, then the package gates: ```sh bun run --cwd packages/db type-check bun run --cwd packages/db test:run bun run lint:db-access bun run lint:d1 bun run lint:exports ``` For schema changes also run `bun run db:migrate:generate -- --name <change>`, review the generated SQL, and run `bun run db:migrate:check`.