diff --git a/docs/orm-docs-audit/README.md b/docs/orm-docs-audit/README.md new file mode 100644 index 0000000000..0c54744868 --- /dev/null +++ b/docs/orm-docs-audit/README.md @@ -0,0 +1,19 @@ +# Prisma ORM 8 docs audit + +Working documents behind the Prisma ORM 8 docs corrections (prisma/web #8236, #8237, #8238) and the proposed restructure. Written September 2026 against `@prisma/orm-postgres@8.0.0-rc.9`; source paths refer to the prisma/orm monorepo at that tag (`packages/`, `docs/releases/`, `examples/`, `skills/prisma-8/`) and to prisma/create-prisma. Site paths are relative to `apps/docs/content/docs/`. + +Citations in `audit/` use the worktree layout the audit was run in: `wip/prisma-src/` is prisma/orm (formerly prisma/prisma; the old name still redirects) at `v8.0.0-rc.9`, `wip/create-prisma-src/` is prisma/create-prisma, and `wip/docs-audit/` is this directory. Strip the prefix to get the path in the named repository. + +Read in this order: + +1. `personas.md` — who reads the ORM docs: the Newcomer, the Upgrader, the Builder, the Agent. +2. `journeys.md` — ten jobs those readers come to do, with an entry point, a done criterion, the path through today's pages, and a verdict. +3. `mental-model.md` — the standalone explanation of Prisma ORM 8 (contract, emit, signature, plans, migrations and refs) that pages get written from. +4. `ia.md` — the proposed sidebar trees and `/orm` page structure, then the current state and the reasons. +5. `changes.md` — every proposed change by kind: restructure (A), corrections to existing pages (B; #8236 and #8237 are merged, #8238 is open as of 2026-09-11), new pages (C), ORM changes (D). Leads with the suggested order. +6. `naming.md` — the "Prisma ORM" naming rule. `plain-language.md` — the rule every page must follow so it reads as written by a person for an ordinary user, with the banned-word list and the reader-review step. +7. `slice-corrections.md` — how the section B corrections were sliced, and the decisions taken. +8. `brief-db-ref-on-adoption.md`, `brief-skills-opt-out.md`, `brief-skill-staleness.md` — three ORM-side changes handed to other agents; `brief-intermediate-contracts.md` — a docs page to write (C25) (adoption sets the `db` ref; a skills opt-out in create-prisma; bringing the `prisma-8` agent skill up to rc.9). +9. `audit/` — the page-by-page audit findings that section B and much of C and D were built from. `INSTRUCTIONS.md` and `AREAS.md` describe how the audit was run; the seven area files hold the findings with source citations. The audit is a snapshot of the site on 2026-09-10; the guides pages it flags as Prisma 7 content were rewritten for Prisma ORM 8 in #8242 on 2026-09-11. + +Prose is a draft throughout; the structure and the cited facts are what has been verified. diff --git a/docs/orm-docs-audit/audit/AREAS.md b/docs/orm-docs-audit/audit/AREAS.md new file mode 100644 index 0000000000..361243012b --- /dev/null +++ b/docs/orm-docs-audit/audit/AREAS.md @@ -0,0 +1,24 @@ +# Audit areas and page lists + +Site paths are relative to `apps/docs/content/docs/`. + +## orm-client +orm/index.mdx, orm/core-concepts.mdx, orm/fundamentals/{reading-data,writing-data,relations-and-joins,advanced-queries,transactions}.mdx, orm/reference/{index,orm-client,transactions-and-runtime,error-reference}.mdx + +## builders +orm/reference/{sql-query-builder,pipeline-builder,raw-queries}.mdx, orm/extensions/using-extensions.mdx, orm/middleware/{how-middleware-works,authoring-custom-middleware,built-in-lints,built-in-budgets,built-in-cache}.mdx + +## contract +orm/contract-authoring/{the-data-contract,the-contract-artifact,psl-syntax,typescript-schema-builder,capabilities}.mdx, orm/data-modeling/{index,relational-databases,mongodb}.mdx, cli/{contract-emit,contract-infer}.mdx + +## migrations-cli +orm/migrations/*.mdx (6), cli/{index,global-flags,configuration,error-reference,init,orm-init,skills,dev,git}.mdx, cli/db-{init,update,migrate,sign,verify,schema}.mdx, cli/migration-{new,plan,ref,show,status}.mdx. Platform commands (auth, branch, bucket, deploy, feedback, postgres, project, service, telemetry) out of scope. + +## start-guides +(index)/{index,getting-started}.mdx, (index)/prisma-orm/{index,create-prisma}.mdx, (index)/prisma-orm/quickstart/{postgresql,mongodb}.mdx, (index)/prisma-orm/add-to-existing-project/{postgresql,mongodb}.mdx, (index)/prisma-postgres/quickstart/prisma-orm.mdx, guides/index.mdx, guides/upgrade-prisma-orm/{postgresql,mongodb}.mdx, guides/switch-to-prisma-orm/*.mdx (3), guides/database/*.mdx (3) + +## gaps +Part 1: guides/frameworks/*.mdx (10), guides/deployment/*.mdx (6), guides/runtimes/*.mdx (2), guides/integrations/{github-actions,vercel-deployment,ai-sdk}.mdx. Part 2: reverse audit of skills/prisma-8/ and docs/glossary.md and the public export maps against the whole ORM 8 site. + +## extras +(index)/full-stack-tutorial.mdx, ai/tools/skills.mdx, cli/telemetry.mdx, plus the targeted gap questions from the first session's gaps brief (RLS, expression indexes, Postgres extensions, JSON filtering, Decimal, SQLite, Vite plugin, @@control, soft delete, read replicas, await using, streaming, native provider, docs/reference/*, extension packages). diff --git a/docs/orm-docs-audit/audit/INSTRUCTIONS.md b/docs/orm-docs-audit/audit/INSTRUCTIONS.md new file mode 100644 index 0000000000..fa0b1f3f92 --- /dev/null +++ b/docs/orm-docs-audit/audit/INSTRUCTIONS.md @@ -0,0 +1,74 @@ +# Audit instructions (shared by every area agent) + +You are auditing the Prisma ORM docs site against the Prisma 8 monorepo at rc.9. The goal is a list of things on the site that are wrong, out of date, or missing, with evidence. + +## Where things are + +Work only inside the docs worktree. Do not edit any file except your own output file. Put scratch files in `wip/docs-audit/audit/scratch-/`. + +- Docs site source: `apps/docs/content/docs/` (this worktree is the `prisma/web` repo). Cite site paths relative to that directory, for example `orm/fundamentals/reading-data.mdx:42`. Prisma ORM 8 pages are under `orm/` (not `orm/v6`, `orm/v7`), `(index)/prisma-orm/`, `(index)/getting-started.mdx`, `cli/` (not `cli/v7`), and the ORM parts of `guides/`. +- The monorepo (the truth) is checked out at rc.9 under `wip/prisma-src/`. Inside it: `packages/` for source, `docs/releases/v8.0.0-rc.*.md` and `docs/releases/v0.1[5-7].0.md` for what changed and when, `docs/glossary.md` for terms, `skills/prisma-8/` for the shipped agent skill (`SKILL.md` plus `references/*.md`). The skill is well maintained and a good second source, but it uses `@internal/*` import paths that users must read as `@prisma/orm-postgres/*`; do not report that, it is known. +- The public package: `packages/9-public/@prisma/orm-postgres/package.json` lists the real export paths users import. +- create-prisma source (what the scaffold generates): `wip/create-prisma-src/`. +- Worked examples that compile against rc.9: `wip/prisma-src/examples/`. + +## How to work + +- Read every page in your area in full. Do not skim headings. +- For every API name, method, flag, command, config key, file path, import path, type name, and error code on a page, check it exists in the source with that name and shape. Grep `packages/` and `skills/prisma-8/references/`. Check `docs/releases/` for renames. +- For every code sample, ask: would this typecheck and run on rc.9? You do not need to run it, but you must be able to point at the source that says yes or no. +- Verify before reporting. A finding must cite a file in the monorepo or a release note. If you cannot find evidence either way, report it under "Unverified" with what you looked for. +- Also look the other way: what does the source or skill support in your area that the site never mentions? Report those under "Missing". +- The product is called "Prisma ORM"; do not report pages that say "Prisma 8", that is a known site-wide naming issue. +- Do not report the items already known (list below). Do not report prose style, tone, or the "written for AI" complaint. Do not report `@internal/*` in the skill. + +## Already known, do not re-report + +- `.take()`/`.skip()` should be `.limit()`/`.offset()` (renamed rc.7). +- `createCount`/`updateCount`/`deleteCount` should be `createAndCount` etc. (renamed 0.17). +- The existing-project PostgreSQL page uses `db.orm.User` and `db.sql.user` instead of `db.orm.public.User` / `db.sql.public.user`. +- `@db.*` attributes were removed in 0.17; `psl-syntax.mdx` still shows `String @db.Uuid`. +- `orm/index.mdx` says SQLite is planned; it ships as `@prisma/orm-sqlite`. +- The upgrade guide is pinned to rc.6 / rc.4. +- `db sign` does not set the `db` ref, so `migration plan` after adoption plans from empty. +- No page covers: a Prisma 7 to 8 API mapping, model/result types (`Models`, `Shape<>`, in open PRs), `createMany`/`skipDuplicates`, `firstOrThrow`, atomic increment, case-insensitive filters, editor setup, RLS outside Supabase, functional indexes, pg_trgm, money/Decimal, the skills opt-out on getting-started pages, the npm `latest` tag / release status. +- `DATABASE_URL` from `.env` vs the environment is inconsistent across getting-started pages; `db.connect()` vs `db.runtime()` is used interchangeably; `db.ts` is imported but never shown on getting-started pages; Node version claims differ. + +## Output + +Write your findings to `wip/docs-audit/audit/.md` (the area name is given in your prompt). Use exactly this structure. Markdown, no hard-wrapped lines, plain English, no invented jargon. + +``` +# Audit: + +Pages read: + +## Breaks copy-paste +Things a user copies that fail: wrong name, removed API, wrong flag, wrong path, wrong import. + +- `:` — — evidence: `` + +## Misleading +True-ish statements that lead the user wrong: stale defaults, changed behaviour, wrong claims about what is supported. + +- (same format) + +## Missing +Things the source or skill supports in this area that the site never mentions, or that a user in this area would need and cannot find. + +- — evidence it exists: `` — where on the site it belongs + +## Unverified +Things that looked wrong but you could not confirm either way. + +- `:` — + +## Skill vs site +Places where the shipped skill (`skills/prisma-8/references/`) and the site disagree, beyond the items above. Say which one matches the source. + +- (same format) +``` + +Keep each bullet to the facts. Line numbers are from the site file. When one defect repeats across many lines, give the count and the first line, not every line. + +When finished, return a two-paragraph summary: how many findings in each section, and the three most serious. diff --git a/docs/orm-docs-audit/audit/builders.md b/docs/orm-docs-audit/audit/builders.md new file mode 100644 index 0000000000..f5f76a774e --- /dev/null +++ b/docs/orm-docs-audit/audit/builders.md @@ -0,0 +1,70 @@ +# Audit: builders + +Pages read: `orm/reference/sql-query-builder.mdx`, `orm/reference/pipeline-builder.mdx`, `orm/reference/raw-queries.mdx`, `orm/extensions/using-extensions.mdx`, `orm/middleware/how-middleware-works.mdx`, `orm/middleware/authoring-custom-middleware.mdx`, `orm/middleware/built-in-lints.mdx`, `orm/middleware/built-in-budgets.mdx`, `orm/middleware/built-in-cache.mdx` + +## Breaks copy-paste + +- `orm/reference/sql-query-builder.mdx:147` (and about 30 more on this page: 218, 230, 244, 278, 308, 330, 375, 405, 415, 425, 444, 480, 512, 545, 620, 632, 667, 689, 690, 711, 788, 821, 833, 857, 897, 947, 956, 981, 1003) — `const rows = await runtime.execute(plan)` reads rows from `execute()` — since rc.2 `runtime.execute(plan)` resolves `Promise` (`{ affectedRows }`); rows come from `runtime.query(plan)`, which returns `AsyncIterableResult`. Every row-returning example on the page is wrong at the type level and at runtime — evidence: `packages/2-sql/5-runtime/src/sql-runtime.ts:309-322`, `docs/releases/v8.0.0-rc.2.md` ("Runtime row queries and non-returning writes are separate calls"), `examples/prisma-8-demo/src/queries/get-users.ts:5`. +- `orm/reference/sql-query-builder.mdx:977-981` — "Run a plan with `runtime.execute(plan)` … It resolves to an array of rows (`Row[]`)" — `execute()` is the non-returning write call; `query()` is the row call, and it resolves to `AsyncIterableResult` (awaitable to an array, or `for await`) — evidence: `packages/2-sql/5-runtime/src/sql-runtime.ts:309-322`. +- `orm/reference/raw-queries.mdx:42` (also 57, 74, 90, 112, 126) — `const rows = await runtime.execute(plan)` — same defect; must be `runtime.query(plan)` — evidence: as above, plus `examples/prisma-8-demo/src/queries/raw-sql-demo.ts:36`. +- `orm/extensions/using-extensions.mdx:86` — `await db.runtime().execute(plan)` for a similarity search — the demo this is copied from uses `db.runtime().query(plan)` — evidence: `examples/prisma-8-demo/src/queries/similarity-search.ts:14`. +- `orm/middleware/built-in-cache.mdx:49` — `const users = await db.runtime().execute(plan)` — must be `.query(plan)`; the cache middleware only acts on the query lifecycle (`interceptQuery`), so a cached read through `execute()` would neither return rows nor be cached — evidence: `examples/prisma-8-demo/src/queries/get-users-cached.ts:26`, `packages/3-extensions/middleware-cache/src/cache-middleware.ts:238-243`. +- `orm/reference/pipeline-builder.mdx:117` (and about 50 more on this page, every `db.execute(plan)`), `orm/reference/pipeline-builder.mdx:122-126` — "`db.execute(plan)` is the client-level executor" — the Mongo client has no `execute` method; its surface is `orm`, `query`, `raw`, `contract`, `enums`, `context`, `connect()`, `runtime()`, `close()`. Plans run through `(await db.runtime()).query(plan)`; note `runtime()` returns a Promise on Mongo. The rc.2 note says explicitly "the row-executing `db.execute` facade method is gone" — evidence: `packages/3-extensions/mongo/src/runtime/mongo.ts:28-39`, `docs/releases/v8.0.0-rc.2.md`, `examples/mongo-demo/test/query-builder-writes.test.ts:57`. +- `orm/reference/pipeline-builder.mdx:126` — "`runtime.execute(plan)` is the equivalent lower-level call" — on Mongo `runtime.execute()` also returns `Promise`; the row call is `runtime.query()` — evidence: `packages/2-mongo-family/7-runtime/src/mongo-runtime.ts:98-99`. +- `orm/reference/raw-queries.mdx:146-147` (and every `db.execute(...)` in the MongoDB section: 167, 181, 191, 208, 221, 231, 235, 257-259, 271, 283) — `db.execute(plan)` — does not exist; see above. Write plans (`insertOne`, `deleteMany`, `findOneAndUpdate`) also run through `runtime.query(plan)` and yield their result envelope as the single row — evidence: `examples/mongo-demo/test/query-builder-writes.test.ts:50-60, 84-87`. +- `orm/reference/raw-queries.mdx:121` — ``db.raw`now()`.returns('pg/timestamptz@1')`` — `db.raw` is a lane object with one key; the tag is ``db.raw.sql`...` ``. Calling `db.raw` as a template tag throws — evidence: `packages/2-sql/4-lanes/sql-builder/src/types/raw-query.ts:153-166` (`RawLane = { readonly sql: RawTagFor }`), `packages/3-extensions/postgres/src/runtime/postgres.ts:63` (`readonly raw: RawLane`), `examples/prisma-8-demo/src/queries/raw-query-demo.ts:35`. +- `orm/middleware/how-middleware-works.mdx:39-73`, `orm/middleware/authoring-custom-middleware.mdx:152-160` — "There are five hooks": `beforeCompile`, `beforeExecute`, `intercept`, `onRow`, `afterExecute` — there is no `intercept` hook. Since rc.2 the lifecycle is split by operation: row queries run `beforeCompile → beforeQuery → interceptQuery → onRow → afterQuery`; non-returning writes run `beforeCompile → beforeExecute → interceptExecute → afterExecute`. A middleware object with an `intercept` key is an excess-property type error and is never called — evidence: `packages/2-sql/5-runtime/src/middleware/sql-middleware.ts:28-128`, `docs/releases/v8.0.0-rc.2.md` ("Runtime middleware splits the same way, into `beforeQuery` / `interceptQuery` / `afterQuery` and `beforeExecute` / `interceptExecute` / `afterExecute` … There is no operation discriminator, compatibility alias, or generic fallback hook"). +- `orm/middleware/authoring-custom-middleware.mdx:31-46, 107-129` — the query logger implements only `afterExecute` and reads `result.rowCount` — `afterExecute` fires only for `runtime.execute()` (non-returning writes). Every ORM read, every ORM `create` (which returns rows), and every SQL-builder `query()` goes through `afterQuery`, so the logger as written never logs the two queries the page shows at lines 95-99. Also `AfterExecuteResult` has no `rowCount` field (it carries `stats.affectedRows`), so line 41 is a type error. The shipped equivalent uses `afterQuery` — evidence: `packages/1-framework/1-core/framework-components/src/execution/runtime-middleware.ts:114-126` (`AfterQueryResult.rowCount` vs `AfterExecuteResult.stats`), `packages/2-sql/5-runtime/src/middleware/sql-middleware.ts:88-128`, `examples/prisma-8-demo/src/prisma/slow-query-warning.ts:24`. +- `orm/middleware/authoring-custom-middleware.mdx:56-58, 157`, `orm/middleware/how-middleware-works.mdx:53-61` — `beforeExecute(plan)` as the place to validate or block a query — `beforeExecute` runs only on the execute (non-returning write) path. To block reads a middleware needs `beforeQuery`; the built-ins register the same function under both names — evidence: `packages/2-sql/5-runtime/src/middleware/lints.ts:176-181` (`beforeQuery: beforeOperation, beforeExecute: beforeOperation`), `packages/2-sql/5-runtime/src/middleware/budgets.ts:118-137`. +- `orm/middleware/built-in-lints.mdx:68` — "The options type also accepts `unindexedPredicate`" — `LintsOptions.severities` has exactly five keys (`selectStar`, `noLimit`, `deleteWithoutWhere`, `updateWithoutWhere`, `readOnlyMutation`); `unindexedPredicate` is an excess-property type error — evidence: `packages/2-sql/5-runtime/src/middleware/lints.ts:13-23`. + +## Misleading + +- `orm/reference/raw-queries.mdx:19` ("There is no way to run a bare raw SQL string on its own"), `orm/reference/raw-queries.mdx:130` ("`db.raw` is not a way to execute a standalone raw SQL statement") — whole-query raw SQL exists since rc.2: ``db.raw.sql`SELECT ...`.returnsRow({ col: 'pg/text@1', ... }).build()`` produces a plan for `runtime.query()`, and ``db.raw.sql`UPDATE ...`.affectedCount().build()`` produces one for `runtime.execute()`. Row-returning raw queries interpolate into other raw templates as subqueries and CTEs — evidence: `docs/releases/v8.0.0-rc.2.md` ("Whole-query raw SQL replaces the classic `$queryRaw` / `$executeRaw` use case"), `packages/2-sql/4-lanes/sql-builder/src/types/raw-query.ts:120-150`, `examples/prisma-8-demo/src/queries/raw-query-demo.ts`. +- `orm/reference/raw-queries.mdx:95`, `orm/reference/sql-query-builder.mdx:925` — "Bare-scalar interpolation is currently broken … the codec inferer emits unversioned codec ids (`pg/int4`)" — the Postgres raw codec inferer emits versioned ids (`pg/int4@1`, `pg/int8number@1`, `pg/float8@1`, `pg/int8@1`, `pg/text@1`, `pg/bool@1`, `pg/bytea@1`); the source comment says "The ids are versioned because that is how the codec registry keys them" — evidence: `packages/3-targets/6-adapters/postgres/src/core/adapter.ts:112-140`. +- `orm/reference/sql-query-builder.mdx:588-590, 621, 633, 668, 691-692` — "`COUNT()` … `SUM()` / `AVG()` over an integer column decode as JavaScript strings … Call `Number(...)`" — since rc.1 aggregates in both the ORM and the SQL builder resolve through the contract's `AggregateTypes`, and since rc.2 `count()`, integer `sum()`, and integer `avg()` return `number` (throwing `RUNTIME.DECODE_FAILED` outside the safe range); lossless `countBigInt()`, `sumBigInt()`, `avgDecimal()` sit beside them. The string-decoding advice describes pre-rc.1 behaviour — evidence: `docs/releases/v8.0.0-rc.1.md` ("`contract.d.ts` gains an `AggregateTypes` block that both the ORM and the SQL builder resolve result types from"), `docs/releases/v8.0.0-rc.2.md` ("The default aggregates are JavaScript numbers again"), `packages/2-sql/4-lanes/sql-builder/src/expression.ts:145-200`, `packages/3-targets/3-targets/postgres/src/core/aggregates.ts:146-192`. +- `orm/middleware/built-in-budgets.mdx:46, 48` — "`severities.latency` … not read by the middleware in v0.14; latency behavior follows the runtime mode" — it is read: default is `'warn'`, and a latency overrun blocks when `severities.latency === 'error'` or the runtime is in strict mode — evidence: `packages/2-sql/5-runtime/src/middleware/budgets.ts:97, 113-125`. +- `orm/middleware/built-in-budgets.mdx:45, 48, 54, 74`, `orm/middleware/authoring-custom-middleware.mdx:203` — repeated references to "permissive mode" as a reachable state — the `postgres(...)` client never forwards a `mode`, so a Postgres runtime is always `'strict'` (`mode ?? 'strict'`) and every "warn in permissive mode" path is unreachable from the public client. Only `mongo(...)` accepts `mode` — evidence: `packages/3-extensions/postgres/src/runtime/postgres.ts:86-94, 276-282` (options and `PostgresRuntimeImpl` construction pass only `verifyMarker` and `middleware`), `packages/2-sql/5-runtime/src/sql-runtime.ts:187`, `packages/3-extensions/mongo/src/runtime/mongo.ts:44`. +- `orm/middleware/built-in-cache.mdx:32, 79` — "`beforeExecute` hooks still run before a cache hit … `afterExecute` still fires" — the cache implements `interceptQuery`, `onRow`, `afterQuery`; the hooks that run around a hit are `beforeQuery` and `afterQuery` — evidence: `packages/3-extensions/middleware-cache/src/cache-middleware.ts:238-243`, `examples/prisma-8-demo/src/prisma/db.ts:16-22`. +- `orm/middleware/how-middleware-works.mdx:65, 73, 78`, `orm/middleware/authoring-custom-middleware.mdx:158, 192` — "Return `{ rows }` from `intercept`" and "`afterExecute` receives `result.rowCount`" — `interceptQuery` returns `{ rows }`, `interceptExecute` returns `{ stats }`; `afterQuery` carries `rowCount`/`completed`/`source`/`latencyMs`, `afterExecute` carries `stats.affectedRows`/`completed`/`source`/`latencyMs` — evidence: `packages/1-framework/1-core/framework-components/src/execution/runtime-middleware.ts:93-136`. +- `orm/middleware/built-in-lints.mdx:72-77` — "Raw SQL … `lints` falls back to text heuristics for raw plans … Control the fallback with `fallbackWhenAstMissing`" — a whole-query raw plan from `db.raw.sql` has an AST of kind `'raw-query'`; for it the structural rules are skipped and the text heuristics always run, regardless of `fallbackWhenAstMissing`. That option only matters when `plan.ast` is absent entirely — evidence: `packages/2-sql/5-runtime/src/middleware/lints.ts:100-104, 155-168`. +- `orm/reference/sql-query-builder.mdx:14` — "PostgreSQL is supported today" for the SQL builder — SQLite ships as a public package with its own adapter (including a raw codec inferer) — evidence: `packages/9-public/@prisma/orm-sqlite/package.json`, `packages/3-targets/6-adapters/sqlite/src/core/adapter.ts`. +- `orm/reference/pipeline-builder.mdx:96` — "Passing an unknown root throws synchronously: `Error: Unknown root …`. This is an `Error`, not a `TypeError`" — it is a structured ORM error with code `ORM.MODEL_UNKNOWN`; users should match on the code — evidence: `packages/2-mongo-family/5-query-builders/query-builder/src/state-classes.ts:646`. +- `orm/reference/sql-query-builder.mdx:906-917` — "The complete built-in set is fixed … Aggregate: `count`, `sum`, `avg`, `min`, `max`" — the aggregate helpers are not a fixed list; they are derived from the contract's aggregate map, which on PostgreSQL also carries `countBigInt`, `sumBigInt`, `avgDecimal`, and `count()` has a zero-argument form — evidence: `packages/2-sql/4-lanes/sql-builder/src/expression.ts:183-200`, `packages/3-targets/3-targets/postgres/src/core/aggregates.ts`, `docs/releases/v8.0.0-rc.2.md`. + +## Missing + +- Whole-query raw SQL lane (``db.raw.sql`...`.returnsRow(spec)`` / `.affectedCount()`, raw subqueries and CTEs by interpolating a `RawRowQuery`, and the `.returns` column-ref record added in rc.5) — evidence it exists: `packages/2-sql/4-lanes/sql-builder/src/types/raw-query.ts`, `docs/releases/v8.0.0-rc.2.md`, `docs/releases/v8.0.0-rc.5.md`, `examples/prisma-8-demo/src/queries/raw-query-demo.ts` — belongs on `orm/reference/raw-queries.mdx` (it currently says the opposite). +- `runtime.query(plan)` vs `runtime.execute(plan)` (rows vs `{ affectedRows }`), and `tx.query` / `tx.execute` inside `db.transaction(...)` — evidence: `packages/2-sql/5-runtime/src/sql-runtime.ts:309-322`, `packages/3-extensions/postgres/src/runtime/postgres.ts:346-372` — belongs under "Compiling and executing" on `orm/reference/sql-query-builder.mdx`. +- The two middleware lifecycles (`beforeQuery`/`interceptQuery`/`onRow`/`afterQuery` and `beforeExecute`/`interceptExecute`/`afterExecute`), `interceptExecute` returning `{ stats }`, `AfterExecuteResult.stats.affectedRows`, and the `params: SqlParamRefMutator` third argument on both before-hooks — evidence: `packages/2-sql/5-runtime/src/middleware/sql-middleware.ts` — belongs on `orm/middleware/how-middleware-works.mdx` and `orm/middleware/authoring-custom-middleware.mdx`. +- `ctx.signal` (per-operation `AbortSignal`, `RUNTIME.ABORTED { phase }`) on the middleware context — evidence: `packages/1-framework/1-core/framework-components/src/execution/runtime-middleware.ts:53-59` — belongs in "The context object" on `orm/middleware/authoring-custom-middleware.mdx`. +- `MongoMiddleware` (`familyId?: 'mongo'`) from `@prisma/orm-mongo/family-runtime`, and the `mode: 'strict' | 'permissive'` option on `mongo(...)` — evidence: `packages/2-mongo-family/7-runtime/src/mongo-middleware.ts:45-46`, `packages/3-extensions/mongo/src/runtime/mongo.ts:44-48`, `examples/mongo-demo/src/db.ts:2` — belongs on `orm/middleware/authoring-custom-middleware.mdx` step 5. +- `createInMemoryCacheStore` (exported, lets you share one store between clients or set your own `clock`) and the `middleware.cache.hit` / `middleware.cache.miss` / `middleware.cache.store` debug log events — evidence: `packages/3-extensions/middleware-cache/src/exports/index.ts`, `packages/3-extensions/middleware-cache/src/cache-middleware.ts:262, 275, 305` — belongs on `orm/middleware/built-in-cache.mdx`. +- Lossless aggregates `countBigInt()`, `sumBigInt()`, `avgDecimal()` and zero-argument `count()` on `fns` — evidence: `docs/releases/v8.0.0-rc.2.md`, `packages/3-targets/3-targets/postgres/src/core/aggregates.ts` — belongs under "Built-in functions" on `orm/reference/sql-query-builder.mdx`. +- `fns.cosineSimilarity` from pgvector (alongside `cosineDistance`) — evidence: `packages/3-extensions/pgvector/src/types/operation-types.ts:17-32` — belongs on `orm/extensions/using-extensions.mdx` step 5. +- `db.prepare(declaration, (sql, params) => plan)` prepared statements; `limit()`/`offset()` accept a prepared bind site (`PaginationValue`) — evidence: `packages/3-extensions/postgres/src/runtime/postgres.ts:67-70`, `packages/2-sql/4-lanes/sql-builder/src/types/shared.ts:63-64`, `docs/releases/v8.0.0-rc.2.md` ("Prepared statements split by their declared result") — belongs on `orm/reference/sql-query-builder.mdx`. +- Driver-free static client (`@prisma/orm-postgres/static`, `@prisma/orm-mongo/static`) exposing the SQL builder, `raw`, and `enums` without a connection — evidence: `packages/9-public/@prisma/orm-postgres/package.json` (`./static`), `docs/releases/v0.15.0.md` ("Client-safe static surface") — belongs under "Entry points" on `orm/reference/sql-query-builder.mdx`. +- `raw` is a reserved storage namespace (`ORM.NAMESPACE_RESERVED`) — evidence: `docs/releases/v8.0.0-rc.2.md` — belongs on `orm/reference/raw-queries.mdx`. +- `RUNTIME.MISSING_EXTENSION_PACK` is the error raised when the contract needs an extension the client did not register — evidence: `packages/2-sql/5-runtime/src/sql-context.ts:229-232` — belongs under "Capabilities" on `orm/extensions/using-extensions.mdx:101`, which describes the failure without naming the code. +- `runtime.query()` returns `AsyncIterableResult`, so SQL-builder and raw plans can be streamed with `for await` — evidence: `packages/2-sql/5-runtime/src/sql-runtime.ts:309-313` — belongs under "Streaming vs. collecting" on `orm/reference/sql-query-builder.mdx:1008`, which sends users to the ORM client instead. + +## Unverified + +- `orm/reference/sql-query-builder.mdx:589, 936` — a raw `EXTRACT(...)` annotated `.returns('pg/int4@1')` "still decodes as a string" — I could not find where a raw expression's declared codec is or is not applied on the decode path; given rc.2 made codec ids in raw fragments contract-checked, the claim needs re-testing against rc.9. +- `orm/reference/pipeline-builder.mdx:190-194` — "`_id` equality inside `match()` never matches any document" — the only `f._id.eq(...)` tests in the source check plan shape in the contract-free builder (`packages/2-mongo-family/5-query-builders/query-builder/test/contract-free-collection.test.ts:37-147`); no live test confirms or refutes the claim. +- `orm/reference/pipeline-builder.mdx:128, 346` — a `lookup()` sub-document's `_id` "comes back as a raw driver `ObjectId`" — `docs/releases/v0.15.0.md` says reshaping reads decode through codecs; I found no source for lookup sub-document decoding either way (`pipeline-result-shape.ts` has no lookup handling). +- `orm/reference/pipeline-builder.mdx:937` — pipeline `insertMany` result shown as `{ insertedIds: { '0': …, '1': … } }` — the declared `InsertManyResult.insertedIds` is `ReadonlyArray` (`packages/2-mongo-family/4-query/query-ast/src/result-types.ts:16`); I could not confirm which shape the pipeline builder path yields on rc.9. +- `orm/reference/raw-queries.mdx:245-263` — raw `findOneAndUpdate` "always uses the driver's `'before'` default", so the first upsert call returns an empty result — the wrapper does pass `returnDocument` as `undefined` (`packages/2-mongo-family/5-query-builders/orm/src/raw-collection.ts:81-88`), but I could not find where the wire layer resolves an undefined `returnDocument`. +- `orm/extensions/using-extensions.mdx:76` — "If `db init` reports a contract-space layout violation … run `migration plan` once: it writes the extension's baseline migration under `migrations//`" — I searched `packages/2-sql/3-tooling` and `packages/3-extensions/postgres` for "layout violation" and "contract-space" and found no such message; only `skills/prisma-8/references/contract.md:53` describes the `migrations//` layout. +- `orm/extensions/using-extensions.mdx:116` — "ParadeDB and Supabase are experimental" — neither README nor either public `package.json` carries an experimental marker. The ParadeDB `key_field`-only restriction is confirmed (`packages/3-extensions/paradedb/README.md:9`). +- `orm/reference/sql-query-builder.mdx:917` — "`ilike` is registered by the Postgres adapter for textual columns" as an `fns` helper — I found no `ilike` operation contributed to the SQL builder's function bag in `packages/3-targets/6-adapters/postgres` or `packages/3-targets/3-targets/postgres`; `ilike` exists on the ORM field proxy (`skills/prisma-8/references/queries-postgres.md`). + +## Skill vs site + +- `skills/prisma-8/references/queries-postgres.md:255-300` and `skills/prisma-8/references/queries.md:144, 149` use `db.runtime().execute(plan)` to read rows; `skills/prisma-8/references/queries-mongo.md` uses `(await db.runtime()).execute(plan)`. The site does the same. Both are wrong: the source uses `query()` for rows (`packages/2-sql/5-runtime/src/sql-runtime.ts:309-322`, `packages/2-mongo-family/7-runtime/src/mongo-runtime.ts:98-99`, `examples/prisma-8-demo/src/queries/*.ts`, `examples/mongo-demo/test/query-builder-writes.test.ts`). +- `skills/prisma-8/references/queries-postgres.md:392, 415` and `queries.md:143` spell the raw lane ``db.raw.sql`…` ``; the site (`orm/reference/raw-queries.mdx:121`) uses ``db.raw`…` ``. The skill matches the source (`packages/2-sql/4-lanes/sql-builder/src/types/raw-query.ts:164-166`). +- `skills/prisma-8/references/queries.md:143` says whole-query raw SQL exists with `.returnsRow()` / `.affectedCount()`; the site (`orm/reference/raw-queries.mdx:19, 130`) says there is no way to run a standalone raw statement. The skill matches the source. +- `skills/prisma-8/references/runtime.md` ("What Prisma Next doesn't do yet") says lints and budgets must be imported from `@internal/sql-runtime` because the facade has no middleware subpath; the site imports them from `@prisma/orm-postgres/family-runtime`. The site matches the source: the export map has `./family-runtime` and the demo imports from it (`packages/9-public/@prisma/orm-postgres/package.json`, `examples/prisma-8-demo/src/prisma/db.ts:3`). +- `skills/prisma-8/references/queries-postgres.md:258` shows `db.sql.user.insert({ email })` with a single object; the site (`orm/reference/sql-query-builder.mdx:725`) says `insert()` always takes an array. The site matches the source (`packages/2-sql/4-lanes/sql-builder/src/types/table-proxy.ts:122-123`, `rows: ReadonlyArray<...>`). +- `skills/prisma-8/references/queries.md:146` says there is no streaming surface; the site (`orm/reference/sql-query-builder.mdx:1008`) says streaming exists on the ORM client's `AsyncIterableResult`. The source returns `AsyncIterableResult` from `runtime.query()` on both the ORM and SQL-builder paths, so the site is closer, but neither says a SQL-builder plan can be streamed directly. +- `skills/prisma-8/references/runtime.md` ("Lints take `severities` (with the five keys above)") lists the five real lint severity keys; the site (`orm/middleware/built-in-lints.mdx:68`) adds a sixth, `unindexedPredicate`. The skill matches the source (`packages/2-sql/5-runtime/src/middleware/lints.ts:13-23`). diff --git a/docs/orm-docs-audit/audit/contract.md b/docs/orm-docs-audit/audit/contract.md new file mode 100644 index 0000000000..45a3e3cd08 --- /dev/null +++ b/docs/orm-docs-audit/audit/contract.md @@ -0,0 +1,60 @@ +# Audit: contract + +Pages read: `orm/contract-authoring/the-data-contract.mdx`, `orm/contract-authoring/the-contract-artifact.mdx`, `orm/contract-authoring/psl-syntax.mdx`, `orm/contract-authoring/typescript-schema-builder.mdx`, `orm/contract-authoring/capabilities.mdx`, `orm/data-modeling/index.mdx`, `orm/data-modeling/relational-databases.mdx`, `orm/data-modeling/mongodb.mdx`, `cli/contract-emit.mdx`, `cli/contract-infer.mdx` + +## Breaks copy-paste + +- `orm/contract-authoring/typescript-schema-builder.mdx:66` (also 165, 167, 241, 247) — `defineContract({ extensionPacks: { pgvector } }, ...)` — the option is `extensions: { pgvector }`; `extensionPacks` was renamed in 0.17 and "the old key fails loudly", so the PostgreSQL contract on this page does not build — evidence: `wip/prisma-src/docs/releases/v0.17.0.md` ("`extensionPacks` config key renamed to `extensions` — in ... the TS builder ..."), `wip/prisma-src/packages/3-extensions/postgres/src/contract/define-contract.ts` (`readonly extensions?: Extensions`), `wip/prisma-src/packages/2-sql/2-authoring/contract-ts/README.md` (`extensions: { pgvector }`). The config-side `extensions: [pgvector]` at line 256 is correct. + +## Misleading + +- `orm/contract-authoring/the-contract-artifact.mdx:49` (also 81, 98, 119, 121) — hashes shown as `"sha256:…"` and `StorageHashBase<"sha256:9f49…">` — content hashes are bare hex since 0.17 and "loaders reject the prefixed form"; emitted files carry `StorageHashBase<'8abaa…'>` with no prefix — evidence: `wip/prisma-src/docs/releases/v0.17.0.md` ("Content hashes are bare hex"), `wip/prisma-src/examples/prisma-8-demo/src/prisma/contract.d.ts:38-43`, `wip/prisma-src/examples/prisma-8-demo/src/prisma/contract.json` (`"profileHash": "3916f…"`). +- `orm/contract-authoring/the-contract-artifact.mdx:100` (also 111) — contract.json top-level key `"extensionPacks": {}` — the emitted key is `extensions` (renamed 0.17, and it is inside the hashed bytes); emitted files also carry `meta` and a `_generated` notice block the page's outline omits — evidence: `wip/prisma-src/docs/releases/v0.17.0.md`, `wip/prisma-src/examples/prisma-8-demo/src/prisma/contract.json` (top-level keys: `schemaVersion, targetFamily, target, profileHash, roots, domain, storage, execution, capabilities, extensions, meta, _generated`). +- `orm/data-modeling/relational-databases.mdx:81` — "The mirror field on the other model (`profile Profile?` on `User`) is not supported yet" — a bare optional model-typed field with no `@relation` is accepted as the 1:1 back side (it only requires the FK to be unique, else `PSL_NON_UNIQUE_BACKRELATION`) — evidence: `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/test/interpreter.relations.test.ts:78` ("accepts a bare model-typed optional field with no @relation as the 1:1 back side"), `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/src/psl-relation-resolution.ts:462`, `wip/prisma-src/docs/releases/v0.16.0.md` ("A singular back-relation over a non-unique foreign key is rejected at emit"). +- `orm/data-modeling/relational-databases.mdx:192` — "pass the discriminator value explicitly when you create through a variant ... it is not filled in automatically yet" — creating through the variant-narrowed collection (`db.orm.public.Task.variant('Bug').create({...})`) injects the discriminator automatically; the page never mentions `.variant()` at all and instead points at `db.orm.public.Bug` — evidence: `wip/prisma-src/packages/3-extensions/sql-orm-client/src/collection.ts:406` ("Insert under a variant — discriminator is injected automatically"), `wip/prisma-src/examples/prisma-8-demo/src/orm-client/collections.ts:48`, `wip/prisma-src/skills/prisma-8/references/queries-mongo.md:22`. +- `orm/data-modeling/relational-databases.mdx:463` — "Referential actions ... are covered in the relations reference as it lands" (implies they are not available) — `onDelete`/`onUpdate` with `NoAction`, `Restrict`, `Cascade`, `SetNull` are parsed and lowered today — evidence: `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/src/psl-relation-resolution.ts:19-22`, `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/src/interpreter.ts:1255`, `wip/prisma-src/skills/prisma-8/references/contract.md:92-99`. +- `orm/data-modeling/index.mdx:159` — scalar table lists `Json` as "Arbitrary JSON value" — on PostgreSQL `Json` binds to native `json` since 0.17 and `Jsonb` is the separate scalar for `jsonb` (which is what the TS builder's `field.json()` and value objects use); the table never mentions `Jsonb`, `Decimal`, or `Bytes` — evidence: `wip/prisma-src/docs/releases/v0.17.0.md` ("`Json` re-binds to native `json` storage, with a new `Jsonb` scalar"), `wip/prisma-src/packages/3-targets/6-adapters/postgres/src/exports/control.ts:15` (`valueObjectStorageType: 'Jsonb'`), `wip/prisma-src/packages/3-targets/3-targets/postgres/src/core/authoring.ts:721-727` (`json` preset emits `pg/jsonb@1`). +- `orm/contract-authoring/psl-syntax.mdx:173-181` — the "Named types" section teaches `types { Uuid = String @db.Uuid }` as the way to get a `uuid` column (the `@db.Uuid` part is the known item) — the replacement is that `Uuid` is a built-in type name in type position and needs no alias: `id Uuid @id @default(uuid())`; the page should show an alias over a real type (for example `ShortName = VarChar(35)`) — evidence: `wip/prisma-src/examples/prisma-8-demo/src/prisma/contract.prisma` (`id Uuid @id @default(uuid())`), `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/README.md` ("Resolve Postgres native storage types from bare names and constructor calls in type position (`Char`, `VarChar`, `Numeric`, `Uuid`, ...)"). +- `cli/contract-infer.mdx:16` (also 23) — usage and option table present `--db` as required — `--db` falls back to `db.connection` from `prisma.config.ts`; only when neither is set does the command fail — evidence: `wip/prisma-src/packages/1-framework/3-tooling/cli/src/orm/contract/infer.ts` (`const dbConnection = args.flags.db ?? ctx.config.db?.connection`). +- `orm/contract-authoring/typescript-schema-builder.mdx:169` — "The MongoDB builder is simpler: it exports `field`, `model`, and `rel` directly, and its `defineContract` takes a single definition object" — the Mongo builder also exports `index`, `valueObject`, `member`, `enumType`, and `defineContract` has the same factory overload as PostgreSQL — evidence: `wip/prisma-src/packages/3-extensions/mongo/src/exports/contract-builder.ts`, `wip/prisma-src/packages/3-extensions/mongo/src/contract/define-contract.ts` (overload 2, "factory form"). + +## Missing + +- Native PostgreSQL types in type position (`Uuid`, `VarChar(255)`, `Char`, `Numeric`, `Inet`, `SmallInt`, `Real`, `Timestamp`, `Timestamptz`, `Date`, `Time`, `Timetz`, `Json`, `Jsonb`, `BigIntNumber`, `UnboundedInt`) — evidence: `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/README.md`, `wip/prisma-src/docs/releases/v0.17.0.md` — belongs on `psl-syntax.mdx` (Models and fields / Named types) and the scalar table on `data-modeling/index.mdx`. +- `@@index` options: `expression:`, `where:` (partial), `unique:`, `type:`/`options:`, and `name:` vs `map:` naming; `@@unique([...])` composite uniques — evidence: `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/README.md` ("`@@index` parameter surface"), `wip/prisma-src/skills/prisma-8/references/contract.md:101-108` — belongs on `psl-syntax.mdx` (Models and fields). The relational page's agent prompt asks for a composite unique, but no page shows the syntax. +- `@@check(expression:, name: | map:)` hand-written CHECK constraints and the `sql.checkConstraint` capability (present in the demo's emitted capabilities but absent from the page's excerpt) — evidence: `wip/prisma-src/skills/prisma-8/references/contract.md:269-290`, `wip/prisma-src/examples/prisma-8-demo/src/prisma/contract.json` (`"checkConstraint": true`) — belongs on `psl-syntax.mdx` and `capabilities.mdx`. +- `@noCheck` / `@noCheck(membership)` / `@noCheck(elementNotNull)` to waive generated CHECK constraints on enum and list columns (TS: `.noCheck(...)`) — evidence: `wip/prisma-src/skills/prisma-8/references/contract.md:265`, `wip/prisma-src/packages/2-sql/2-authoring/contract-ts/src/contract-dsl.ts:294` — belongs on `psl-syntax.mdx` (Enums). +- Scalar lists (`String[]`) are supported on PostgreSQL and rejected only on targets without `sql.scalarList` (SQLite); the data-modeling overview shows `tags String[]` but `psl-syntax.mdx` never says lists exist, which database supports them, or that list columns get a generated not-null-element CHECK — evidence: `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/src/psl-field-resolution.ts:497-500`, `wip/prisma-src/examples/prisma-8-demo/src/prisma/contract.json` (`"scalarList": true`) — belongs on `psl-syntax.mdx`. +- `@@control(managed | tolerated | external | observed)` per-model control policy and `defaultControlPolicy` — evidence: `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/README.md` ("Model-level control policy"), `wip/prisma-src/skills/prisma-8/references/contract.md:333-346` — belongs on `psl-syntax.mdx` and `the-contract-artifact.mdx`. +- `namespace { ... }` blocks for non-default PostgreSQL schemas, and cross-contract-space relation targets (`supabase:auth.AuthUser`) — evidence: `wip/prisma-src/packages/1-framework/2-authoring/psl-parser/src/parse.ts:502`, `wip/prisma-src/skills/prisma-8/references/contract.md:292-331` — belongs on `psl-syntax.mdx`. The artifact page says everything is "grouped by namespace" without saying how to author one. +- `native_enum` blocks (real `CREATE TYPE ... AS ENUM`) with `@@map` for the Postgres type name, and the TS `nativeEnum(...)` / `pg.enum(handle)` helpers, as the alternative to `@@type`-coded enums — evidence: `wip/prisma-src/packages/3-targets/3-targets/postgres/src/core/authoring.ts` (`keyword: 'native_enum'`), `wip/prisma-src/packages/3-extensions/postgres/src/contract/native-enum.ts` — belongs on `psl-syntax.mdx` (Enums) and `typescript-schema-builder.mdx` (Enums). +- `@@rls`, `policy_select` / `policy_insert` / `policy_update` / `policy_delete` / `policy_all` blocks and `role` blocks in PSL; `rlsEnabled`, `policy*`, `role` handles in the TS builder — evidence: `wip/prisma-src/packages/3-targets/3-targets/postgres/src/core/authoring.ts:121-125`, `wip/prisma-src/packages/3-extensions/postgres/src/exports/contract-builder.ts` — belongs on `psl-syntax.mdx` / `typescript-schema-builder.mdx` (RLS outside Supabase is a known site-wide gap; the authoring syntax is the part that belongs in this area). +- The `@default(...)` generator surface: `uuid(4)`, `uuid(7)`, `cuid(2)`, `ulid()`, `nanoid()`, `nanoid()`, `dbgenerated("...")`, and that `cuid()` is rejected with a hint to use `cuid(2)` — evidence: `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/README.md` ("Supported `@default(...)` surface") — belongs on `psl-syntax.mdx` (Models and fields). +- Timestamp presets `temporal.createdAt()` / `temporal.updatedAt()` in PSL type position, and that Prisma 7's `@updatedAt` is rejected (`PSL_UNSUPPORTED_FIELD_ATTRIBUTE` with a hint) — evidence: `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/README.md` ("Supported timestamp authoring surface") — belongs on `psl-syntax.mdx`; the TS page shows `field.temporal.updatedAt()` but the PSL page has no way to express an updated-at field. +- `onDelete` / `onUpdate` on `@relation` and `@relation("Name")` disambiguation when several relations join the same two models — evidence: `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/src/psl-relation-resolution.ts:19-22`, `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/README.md` ("Matching may use `@relation("Name")`") — belongs on `relational-databases.mdx` and `psl-syntax.mdx` (Relations). +- Extension types inline in field position (`embedding pgvector.Vector(length: 1536)?`) without a `types {}` alias, and the `PN-CLI-4011`-style "missing extension" failure mode is now a dotted structured code — evidence: `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/README.md`, `wip/prisma-src/docs/releases/v0.17.0.md` — belongs on `psl-syntax.mdx` (Extension types). +- TS builder field helpers beyond the four listed: `field.int()`, `field.bigint()`, `field.float()`, `field.decimal()`, `field.boolean()`, `field.bytes()`, `field.dateTime()`, `field.id.uuidv7String()`, `field.id.nanoid({ size })`, `field.uuidNative()` / `field.id.uuidv4Native()` / `field.id.uuidv7Native()`, `type.BigIntNumber()`, `type.UnboundedInt()`; and that scalar helpers exist only inside the factory callback (outside it only `field.column`, `field.generated`, `field.namedType`) — evidence: `wip/prisma-src/packages/3-targets/3-targets/postgres/src/core/authoring.ts:679-793`, `wip/prisma-src/packages/2-sql/2-authoring/contract-ts/README.md` ("Helper Notes") — belongs on `typescript-schema-builder.mdx` (Fields). +- TS builder model-level features: `.attributes(({ fields, constraints }) => ({ id, uniques }))` for composite keys and uniques, `constraints.index(...)` under `.sql({ indexes })`, `check(...)` under `.sql({ checks })`, `.noCheck()`, `rel.belongsTo(...).sql({ fk })`, and the root-level `foreignKeyDefaults` / `naming` options — evidence: `wip/prisma-src/packages/2-sql/2-authoring/contract-ts/README.md` ("Constraint Placement", "Indexes", "Foreign Key Defaults"), `wip/prisma-src/packages/2-sql/2-authoring/contract-ts/src/contract-dsl.ts:1207-1215` — belongs on `typescript-schema-builder.mdx` (Storage mapping). The page shows no way to declare a composite primary key in TypeScript. +- Mongo TS builder: `valueObject(...)` + `field.valueObject(Address)` for embedded documents, `index({...}, { unique })` and `collectionOptions`, `field.vector()`, `enumType`/`member`, and discriminator polymorphism via `base` / `discriminator` / `variants` — evidence: `wip/prisma-src/packages/2-mongo-family/2-authoring/contract-ts/README.md` — belongs on `typescript-schema-builder.mdx`; the MongoDB tab cannot express the embedded `Address` the PSL tab on `psl-syntax.mdx` shows. +- Mongo PSL index syntax beyond `@@index([f])`: sort order per field (`@@index([createdAt(sort: Desc), authorId])`), `@@unique`, `@@textIndex`; and that per-index `collation`, `partialFilterExpression`, `wildcardProjection` need the TS builder — evidence: `wip/prisma-src/examples/mongo-demo/src/contract.prisma`, `wip/prisma-src/packages/2-mongo-family/2-authoring/contract-psl/README.md` — belongs on `mongodb.mdx` / `psl-syntax.mdx` (MongoDB tab). +- `contract infer` is SQL-only (fails with `CONTRACT.INFER_UNSUPPORTED` on MongoDB), its default output is `contract.prisma` next to the emitted `contract.json`, and it captures indexes, `@@check` (as `map:`), `@@rls` and policy blocks, and 1:1 relations from unique indexes — evidence: `wip/prisma-src/packages/1-framework/3-tooling/cli/src/orm/contract/infer.ts`, `wip/prisma-src/packages/1-framework/3-tooling/cli/src/orm/contract/paths.ts`, `wip/prisma-src/docs/releases/v0.17.0.md` ("`contract infer` reaches full fidelity") — belongs on `cli/contract-infer.mdx` (What to review). +- The config `output` option on `ormConfig({ contract, output })` (a directory; `contract.json` and `contract.d.ts` land there instead of next to the source), which `create-prisma` sets to `./src/prisma/generated` for TypeScript authoring — evidence: `wip/prisma-src/packages/3-extensions/postgres/src/config/define-config.ts` (`join(options.output, 'contract.json')`), `wip/create-prisma-src/templates/create/_shared/prisma.config.ts.hbs` — belongs on `typescript-schema-builder.mdx` (Point the config at the contract file) and `cli/contract-emit.mdx`. +- The Vite plugin that re-emits on save (`@prisma/orm-postgres/vite-plugin-contract-emit`) as the alternative to running `contract emit` by hand — evidence: `wip/prisma-src/packages/9-public/@prisma/orm-postgres/package.json` (`./vite-plugin-contract-emit`), `wip/prisma-src/skills/prisma-8/references/contract.md:12` — belongs on `cli/contract-emit.mdx` / `the-contract-artifact.mdx` (Version control). +- `contract emit` prints the three hashes and returns them in `--json` (`storageHash`, `executionHash`, `profileHash`, `outDir`, `files`) — evidence: `wip/prisma-src/packages/1-framework/3-tooling/cli/src/orm/contract/emit.ts` (`EmitDocument`) — belongs on `cli/contract-emit.mdx` (Options / `--json`). +- Querying polymorphic variants with `.variant('Bug')` (narrows reads and injects the discriminator on create), on both SQL and Mongo — evidence: `wip/prisma-src/packages/3-extensions/sql-orm-client/src/collection.ts:395-410`, `wip/prisma-src/skills/prisma-8/references/queries-mongo.md:57` — belongs on `relational-databases.mdx` and `mongodb.mdx` (Polymorphic sections). +- `AddressInput` alongside `AddressOutput`, plus `FieldOutputTypes`, `TypeMaps`, `QueryOperationTypes`, `AggregateTypes` in `contract.d.ts` — evidence: `wip/prisma-src/examples/prisma-8-demo/src/prisma/contract.d.ts` — belongs on `the-contract-artifact.mdx` (Inside `contract.d.ts`), which only shows the output type. + +## Unverified + +- `orm/data-modeling/relational-databases.mdx:192` — "Variants appear as their own models in queries (`db.orm.public.Bug` ...)" — the runtime facet enumerates every model in `domain.namespaces[ns].models`, which includes variants, so the property resolves; I could not confirm that creating through it (rather than through `.variant('Bug')`) sets the discriminator, or that the site's `{ title, severity, type: "bug" }` shape is accepted there. Checked `wip/prisma-src/packages/3-extensions/sql-orm-client/src/orm.ts:155-185`, `storage-resolution.ts:42-48`, and the demo, which only ever uses `.variant(...)`. +- `orm/data-modeling/relational-databases.mdx` (whole page) — the default referential action when `onDelete` is omitted; the skill says `Restrict` but I could not find the default in the interpreter or planner. Checked `psl-relation-resolution.ts`, `interpreter.ts:1255-1304`, and grepped the postgres target and sql family for a fallback. +- `orm/contract-authoring/the-contract-artifact.mdx:142-145` — `postgres({ contractJson })` with no `url`; the runtime accepts `url?` and the demo omits it, so a connection presumably comes from `db.connect(...)` or `prisma.config.ts`; how the URL is resolved is outside this area. Checked `wip/prisma-src/packages/3-extensions/postgres/src/runtime/postgres.ts:88,312`. + +## Skill vs site + +- `orm/data-modeling/mongodb.mdx:126-148` and `psl-syntax.mdx:281` show `@@discriminator` / `@@base` in PSL for MongoDB; the skill says these attributes "are SQL-only" and Mongo polymorphism needs the TS builder (`contract.md:221`). The site matches the source: the Mongo attribute registry includes both, and `examples/mongo-demo/src/contract.prisma` uses them — evidence: `wip/prisma-src/packages/2-mongo-family/2-authoring/contract-psl/README.md`, `wip/prisma-src/examples/mongo-demo/src/contract.prisma`. +- `orm/data-modeling/index.mdx:167` shows `tags String[]`; the skill (`contract.md:123`) and the contract-psl README say scalar lists "are rejected by the SQL interpreter". The site matches the source: lists are gated on `sql.scalarList`, which PostgreSQL declares — evidence: `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/src/psl-field-resolution.ts:497`, `wip/prisma-src/examples/prisma-8-demo/src/prisma/contract.json`. +- `typescript-schema-builder.mdx:66` uses `extensionPacks`; the skill (`contract.md:51,386`) uses `extensions: { pgvector }`. The skill matches the source — evidence: `wip/prisma-src/packages/3-extensions/postgres/src/contract/define-contract.ts`, `wip/prisma-src/docs/releases/v0.17.0.md`. +- The site's config samples use `definePrismaConfig({ orm: ormConfig({...}) })`; the skill (`contract.md:47,164-170`) still shows the flat `defineConfig({ contract, extensions })` from `@internal/postgres/config` as the whole file. The site matches the source: the flat shape was removed in rc.4 and every example and the create-prisma template use the envelope — evidence: `wip/prisma-src/docs/releases/v8.0.0-rc.4.md` ("The deprecated config fallbacks are gone"), `wip/prisma-src/examples/prisma-8-demo/prisma.config.ts`, `wip/create-prisma-src/templates/create/_shared/prisma.config.ts.hbs`. +- The skill (`contract.md:28,62-66`) routes on `PN-CLI-4002` / `PN-CLI-4003` / `PN-CLI-4011`; those codes were replaced by dotted structured codes in 0.17. Neither the site nor the skill lists the current codes for a failed emit — evidence: `wip/prisma-src/docs/releases/v0.17.0.md` ("Every published error is a structured envelope with a dotted code"). +- `psl-syntax.mdx:302` declares the pgvector type via a `types {}` alias with a positional argument (`pgvector.Vector(1536)`); the skill (`contract.md:179`) writes it inline on the field with a named argument (`pgvector.Vector(length: 1536)`). Both forms are accepted; the demo uses the site's spelling — evidence: `wip/prisma-src/examples/prisma-8-demo/src/prisma/contract.prisma`, `wip/prisma-src/packages/2-sql/2-authoring/contract-psl/README.md`. diff --git a/docs/orm-docs-audit/audit/extras.md b/docs/orm-docs-audit/audit/extras.md new file mode 100644 index 0000000000..9acb7d2303 --- /dev/null +++ b/docs/orm-docs-audit/audit/extras.md @@ -0,0 +1,112 @@ +# Audit: extras + +Pages read: `(index)/full-stack-tutorial.mdx`, `ai/tools/skills.mdx`, `cli/telemetry.mdx` (in full). Also consulted for cross-checks: `cli/skills.mdx`, `cli/init.mdx`, `cli/configuration.mdx`, `orm/extensions/using-extensions.mdx`, `orm/data-modeling/index.mdx`, `orm/reference/transactions-and-runtime.mdx`, `orm/fundamentals/reading-data.mdx`, `guides/database/multiple-databases.mdx`. + +Sources used: `wip/create-prisma-src/` (what the `hono` scaffold generates), `wip/prisma-src/packages/1-framework/3-tooling/cli/` (orm init, migration plan, telemetry hooks), `wip/prisma-src/packages/1-framework/3-tooling/cli-telemetry/`, `wip/prisma-src/skills/`, `wip/prisma-src/docs/Telemetry.md`, `wip/prisma-src/docs/reference/*`, and the packages named per finding. The `skills sync` and `telemetry status|enable|disable` command implementations are not in the ORM monorepo (`skills sync` appears only in a test name; `telemetryCommandGroup` is imported from the external `@prisma/cli-engine` package in `packages/1-framework/3-tooling/cli/src/orm/cli.ts:4`), so claims about those commands' exact behaviour are checked against `skills/README.md` and `docs/Telemetry.md` only. + +## Breaks copy-paste + +- `ai/tools/skills.mdx:102` — says `orm init --skip-skills` skips skill installation — no such flag exists. `orm init` takes `--target`, `--authoring`, `--schema-path`, `--write-env`, `--probe-db`, `--strict-probe`, `--skip-install`, `--keep-previous-facade` and does not touch skills at all — evidence: `packages/1-framework/3-tooling/cli/src/orm/init.ts:92-110`; `packages/1-framework/3-tooling/cli/src/commands/init/skill-sources.ts:1-5` ("Skills setup belongs to the family-level `prisma init` command; the only skill work left in `orm init` is deleting the retired directories"); `skills/README.md` ("`prisma orm init` does not touch them"). +- `ai/tools/skills.mdx:111-112` — lists `prisma-next-upgrade` and `prisma-8-extension-upgrade` as installable Prisma 8 skills — both names are retired; only one skill, `prisma-8`, ships, and upgrading is a branch inside it (`references/upgrade-app.md`, `references/upgrade-extension.md`). A user running `npx skills add ... --skill prisma-next-upgrade` gets nothing, and `prisma init` actively deletes directories with those names — evidence: `wip/prisma-src/skills/` contains only `prisma-8/`; `skills/README.md` ("One installable skill"); `packages/1-framework/3-tooling/cli/src/commands/init/skill-sources.ts:24-38` (`RETIRED_SKILL_NAMES` includes `prisma-next-upgrade` and `prisma-8-extension-upgrade`). + +## Misleading + +- `(index)/full-stack-tutorial.mdx:193-203` — shows the scaffold's `User` model with `createdAt DateTime @default(now())` and `updatedAt temporal.updatedAt()`, and tells the user to "add one line" — the `hono` scaffold's contract is written by `prisma orm init` and actually contains `createdAt TimestamptzString @default(now())` and `updatedAt temporal.updatedAtString()`. Both spellings are valid PSL, but the user's file will not match the page, and the two store different runtime types (`Temporal` object vs. string) — evidence: `packages/1-framework/3-tooling/cli/src/commands/init/templates/code-templates.ts:171-193` (`starterSchemaPslPostgres`); `wip/create-prisma-src/src/tasks/prisma-setup/commands.ts:50-63` (create-prisma runs `orm init --schema-path src/prisma/contract.prisma`); `packages/2-sql/2-authoring/contract-psl/README.md:18,70` (both `DateTime @default(now())` and `temporal.updatedAt()` are valid). +- `(index)/full-stack-tutorial.mdx:51` and `:28` — say the scaffold "added the `postinstall` hook that keeps [skills] current" and that `init` repairs it — create-prisma adds a `skills:sync` npm script (`prisma skills sync || exit 0`) and runs `prisma init --yes` once; it does not write a `postinstall` hook. Whether the family-level `prisma init` adds one cannot be checked in the ORM monorepo — evidence: `wip/create-prisma-src/src/tasks/install.ts:56`; `wip/create-prisma-src/src/tasks/prisma-setup/commands.ts:65-72`. +- `ai/tools/skills.mdx:102` — "Projects scaffolded with `create-prisma` or `orm init` install them automatically" — `create-prisma` does (it runs the family-level `prisma init --yes`), `orm init` does not — evidence: `wip/create-prisma-src/src/tasks/prisma-setup/commands.ts:65-72`; `packages/1-framework/3-tooling/cli/src/commands/init/skill-sources.ts:1-5`. +- `ai/tools/skills.mdx:114` — "`orm init` applies this pinning for you" (pinning `npx skills add ...#v`) — `orm init` does not install skills from GitHub at all; the version match comes from the skill shipping inside the installed `@prisma/orm-*` tarball and `prisma skills sync` copying it — evidence: `skills/README.md` (Install and Versioning sections); `packages/9-public/@prisma/orm-postgres/package.json` (`"files": ["dist", "skills"]`, `prepack` runs `sync-package-skills.ts`). +- `ai/tools/skills.mdx:105` and `:19-20` — present `npx skills add prisma/prisma/skills` as the way to add Prisma 8 skills to an existing project — the monorepo README calls that "interoperability, not the recommended path: nothing keeps a copy installed this way up to date", and the recommended path is `prisma init` / `prisma skills sync`, which the page only mentions in passing at line 36 — evidence: `skills/README.md` ("Manual fallback — install from GitHub"). +- `ai/tools/skills.mdx:114` — "The two upgrade skills are intentionally unpinned; the latest revision covers every prior transition" — there are no separate upgrade skills; the per-transition instructions live under `prisma-8/upgrading/app/upgrades/-to-/` inside the single skill, and "The version you upgrade *to* carries the instructions" — evidence: `skills/README.md`; `wip/prisma-src/skills/prisma-8/upgrading/`. +- `guides/database/multiple-databases.mdx` (whole page, e.g. `:63`, `:109`) — sits in the non-versioned `guides/` tree and is the only ORM 8 page matching "multiple databases", but it is a Prisma 7 guide (`@prisma/client@7.10.0`, `schema.prisma`, `PrismaClient`). A Prisma 8 user searching for multi-database or replica guidance lands on v7 instructions with no banner — evidence: `guides/database/multiple-databases.mdx:63,93,109`; ORM 8 has no multi-database routing (`skills/prisma-8/references/runtime.md:323`). +- `cli/telemetry.mdx:9` — "Telemetry is on by default" with no mention that CI environments never send events and never see the first-run notice, so a user reading only this page may add opt-out env vars to CI unnecessarily. Not wrong, but incomplete — evidence: `packages/1-framework/3-tooling/cli/src/orm/telemetry/reporting.ts:75-78`; `docs/Telemetry.md` ("CI environments"). + +## Missing + +Ranked by how likely a user is to need it, most likely first. Items marked (known) are in the instructions' already-known list but are included here because the task asked for evidence for each. + +1. Telemetry: how to opt out without the CLI, and what is sent — `cli/telemetry.mdx` never mentions `DO_NOT_TRACK=1` or `PRISMA_NEXT_DISABLE_TELEMETRY=1`, the per-user config file (`~/.config/prisma-next/config.json` / `%APPDATA%\prisma-next\config.json`, `enableTelemetry` field), that CI is always silent, the first-run stderr notice, that `telemetry` itself sends nothing, that the preference is per-user not per-project, agent detection via `@vercel/detect-agent` / `AI_AGENT`, or the event fields. Note the field list in `docs/Telemetry.md` is itself stale: the rc.9 sender also reports `exitCode` ("No outcome data" in the doc is no longer true) — evidence: `packages/1-framework/3-tooling/cli-telemetry/src/gating.ts:62-72`, `user-config.ts:23-51`, `payload.ts:101-116`, `enrich.ts:205-223`; `packages/1-framework/3-tooling/cli/src/orm/telemetry/reporting.ts:24-31,75-92`; `docs/Telemetry.md` — belongs on `cli/telemetry.mdx`. +2. Row level security and policies on plain Postgres (known) — supported without Supabase: PSL `policy_select` / `policy_insert` / `policy_update` / `policy_delete` / `policy_all` blocks with `target`, `roles`, `using`, `withCheck`, `permissive = false`, `@@map`; `@@rls` on the model; roles declared as contract entities; TS builder `policySelect` … `policyAll`, `rlsEnabled(Model)`, `role('name')` from `@prisma/orm-postgres/contract-builder`; `contract infer` captures live policies; the planner emits `ENABLE ROW LEVEL SECURITY` and `CREATE POLICY` — evidence: `packages/3-extensions/postgres/src/contract/rls.ts` (role, enablement and policy handles), `packages/3-targets/3-targets/postgres/src/core/postgres-rls-policy.ts`, `postgres-rls-enablement.ts`, `postgres-role.ts`, `migrations/operations/rls.ts`, `psl-infer/infer-policy-blocks.ts`; `skills/prisma-8/references/contract.md:378`; `skills/prisma-8/references/supabase.md` ("Contract: FK into auth.users + RLS policies") — the site only mentions RLS in error codes (`orm/reference/error-reference.mdx:382-392,436`) — belongs in `orm/contract-authoring/` (a "Row level security" page) with a note that the Supabase pack only supplies the roles. +3. Expression (functional) and partial indexes (known) — `@@index(expression: "lower(email)", name: "users_email_lower")`, `@@index([...], where: "...")`, `unique:`, `type:` (btree, hash, gin, gist, spgist, brin) and `options:`, `name:` vs `map:`; TS builder `constraints.index({ expression, ... })` — evidence: `packages/2-sql/1-core/contract/src/ir/sql-index.ts:12-26,45-60`; `packages/3-targets/3-targets/postgres/src/core/index-types.ts`; `skills/prisma-8/references/contract.md:101-108` — the site shows only `@@index([cols])` (`orm/contract-authoring/psl-syntax.mdx:97,137`; `orm/reference/orm-client.mdx:161-162`) — belongs on `orm/contract-authoring/psl-syntax.mdx`. +4. `@@control()` and `defaultControlPolicy` — a model can declare `managed`, `tolerated`, `external`, or `observed` to say whether Prisma owns its DDL; a contract-wide default goes on `prismaContract(path, { defaultControlPolicy })` — evidence: `packages/2-sql/2-authoring/contract-psl/README.md:104`; `packages/2-sql/2-authoring/contract-psl/src/interpreter.ts:341`, `psl-field-resolution.ts:418-422`; `packages/9-public/@prisma/orm-postgres/package.json` export `./contract/apply-specifier-default-control-policy`; `skills/prisma-8/references/contract.md:333-346` — no ORM 8 page contains `@@control` — belongs on `orm/contract-authoring/psl-syntax.mdx` and `orm/migrations/how-migrations-work.mdx`. +5. Vite plugin — `@prisma/orm-postgres/vite-plugin-contract-emit` exports `prismaVitePlugin('prisma.config.ts', { debounceMs, logLevel })`, re-emits the contract on save in `vite dev` (Vite 7 and 8 only; no build-time emit; no Next.js / Webpack / esbuild / Rollup / Turbopack plugin) — evidence: `packages/9-public/@prisma/orm-postgres/package.json` (`"./vite-plugin-contract-emit"`); `packages/1-framework/3-tooling/vite-plugin-contract-emit/`; `skills/prisma-8/references/build.md` — the only site hits for "vite" are TanStack's own Nitro plugin (`guides/frameworks/tanstack-start.mdx:86`) — belongs in `orm/contract-authoring/` or a new "Build tools" page, and in `guides/frameworks/tanstack-start.mdx`. +6. `Decimal` (known) — PSL `Decimal` maps to Postgres `numeric` with optional `precision` (1..1000) and `scale` params; runtime value is a decimal string (`CANONICAL_NUMERIC_TEXT`); `@default` accepts numeric text; aggregates: `sum` over `numeric` returns a decimal `string | null`, `avgDecimal(field)` gives an exact decimal string, `sumBigInt` / `countBigInt` for integers, and `count()` / `sum` over integers throw `RUNTIME.DECODE_FAILED` past 2^53-1 — evidence: `packages/3-targets/3-targets/postgres/src/core/codecs.ts:121-175,547`; `codec-type-map.ts:73`; `default-normalizer.ts:18,221`; `psl-infer/infer-enum-blocks.ts:19`; `skills/prisma-8/references/queries-postgres.md:233-255,386,407` — `orm/data-modeling/index.mdx:152-160` omits `Decimal` from the scalar table and `:182` says to store money as integer minor units; nothing documents `avgDecimal` / `sumBigInt` / the overflow error — belongs on `orm/data-modeling/index.mdx` and `orm/fundamentals/advanced-queries.mdx` (aggregates). +7. SQLite target (known) — `@prisma/orm-sqlite` ships at rc.9 with `sqlite(...)` runtime, `[Symbol.asyncDispose]`, and its own capability limits (no `checkConstraint`, no `scalarList`, no `enums`, no `lateral`, no `avgDecimal`); the skill routes to it throughout — evidence: `packages/9-public/@prisma/orm-sqlite/package.json`; `packages/3-extensions/sqlite/src/runtime/sqlite.ts:85,354`; `skills/prisma-8/references/runtime.md:333`, `queries-postgres.md:255`, `contract.md` (`@@check` on SQLite) — site mentions are only `orm/reference/error-reference.mdx:598`, `cli/skills.mdx:38` and capability examples in `orm/contract-authoring/capabilities.mdx:46-70`; there is no getting-started, quickstart, or runtime section for SQLite — belongs in `(index)/prisma-orm/quickstart/`, `orm/reference/transactions-and-runtime.mdx`, `orm/index.mdx`. +8. Enabling a Postgres extension that has no pack (`pg_trgm`, `citext`, …) (known) — the migration authoring API has a structured `createExtension('')` op (renders `CREATE EXTENSION IF NOT EXISTS`), so a user can add it to a hand-written or edited `migration.ts`; there is no contract-level "requires extension" declaration, and no `pg_trgm` pack ships even though `docs/reference/capabilities.md:97-103` reserves a `pg_trgm` capability namespace — evidence: `packages/3-targets/3-targets/postgres/src/core/migrations/op-factory-call.ts:1443-1465` (`CreateExtensionCall`, `renderTypeScript` → `createExtension("...")`); `migrations/operations/dependencies.ts:7-22`; `packages/3-extensions/` has no `pg_trgm` — the site only mentions `CREATE EXTENSION` for packs that ship their own migration (`orm/extensions/using-extensions.mdx:76`) — belongs on `orm/migrations/editing-a-migration.mdx`. +9. Streaming is cursor-backed on Postgres — the site documents `for await` over `.all()` (`orm/fundamentals/reading-data.mdx:280-313`) but never says the Postgres driver reads through a server-side cursor in batches (`pg-cursor`, `cursorBatchSize`) nor that the serverless client takes `cursor` options; the skill wrongly says "No `.stream()` cursor today" — evidence: `packages/3-targets/7-drivers/postgres/src/postgres-driver.ts:24,374-384`, `named-cursor.ts`; `packages/3-extensions/postgres/src/runtime/postgres-serverless.ts:31` (`PostgresServerlessCursorOptions`); `packages/2-sql/5-runtime/src/sql-runtime.ts:387-399` — belongs on `orm/fundamentals/reading-data.mdx` and `orm/reference/transactions-and-runtime.mdx`. Overruled: the driver has the cursor, but the `postgres()` facade constructs it with `cursor: { disabled: true }`, so on the long-lived client the whole result is fetched before iteration; only the serverless facade reads through a cursor. See `orm-client.md` (Misleading, first bullet), `slice-corrections.md` decision 3 (measured), and D8 in `changes.md`. +10. `create-prisma` flags used by the tutorial — the tutorial uses `create ... --template hono --provider postgres --yes`, and `--authoring`, `--package-manager`, `--deploy`, `--workspace`, `--force`, `--json`, `--verbose` also exist; the tutorial says "Answer the prompts for contract authoring style and package manager" without naming the flags that skip them — evidence: `wip/create-prisma-src/src/index.ts:39-86` — belongs on `(index)/full-stack-tutorial.mdx:45` (one line) and `(index)/prisma-orm/create-prisma.mdx`. + +## Unverified + +- `(index)/full-stack-tutorial.mdx:213` — "a Composer deploy never sets the `db` ref that `migration plan` chains from by default" — Composer is not in the ORM monorepo, so this cannot be checked. What is verifiable: `--from` accepts "hash, prefix, ref name, migration dir name, `^`, `@empty`, or `./path`" (`packages/1-framework/3-tooling/cli/src/orm/migration/plan.ts:204-208`), so `--from _init` is a valid spelling, and the directory format is `_` (`packages/1-framework/3-tooling/migration/src/io.ts:456-470`). +- `(index)/full-stack-tutorial.mdx:218` — the shown DDL `ALTER TABLE "public"."user" ADD COLUMN "role" text DEFAULT 'member' NOT NULL` — the column is rendered through `renderColumnDdl` → `contractFree.col(name, type, { notNull, default })` (`packages/3-targets/3-targets/postgres/src/core/migrations/column-ddl-rendering.ts:64-78`); I did not trace the final clause ordering inside `contractFree.col`, so the exact text is unconfirmed. Table name `user` is consistent with the known `db.sql.public.user` item. +- `ai/tools/skills.mdx:19,102` and `cli/skills.mdx:9,48-54` disagree with each other and with the monorepo on the agent directory list. `ai/tools/skills.mdx` says Claude Code, Cursor, Codex, Windsurf (matches `skills/README.md` and `packages/1-framework/3-tooling/cli/src/commands/init/skill-sources.ts:44-49`: `.claude`, `.cursor`, `.agents`, `.windsurf`). `cli/skills.mdx:53`, `cli/init.mdx:29`, `cli/configuration.mdx:94` say `claude`, `cursor`, `agents`, `devin` (matches the create-prisma scaffold's `prisma.config.ts`, `wip/create-prisma-src/templates/create/_shared/prisma.config.ts.hbs`: `agents: ["claude", "cursor", "agents", "devin"]`). The `skills sync` implementation is not in the monorepo, so I cannot say which list is current; one of the two pages is wrong. +- `ai/tools/skills.mdx:19,102` — links "prisma/prisma" text to `https://github.com/prisma/orm`, and the install command is `npx skills add prisma/prisma/skills` — `skills/README.md` uses `prisma/prisma/skills`, but `packages/9-public/@prisma/orm-postgres/package.json` `repository.url` is `https://github.com/prisma/orm.git`. Could not determine which GitHub path the `skills` CLI must be given. +- `cli/telemetry.mdx` — the rc.9 first-run notice points users to `https://prisma-next.dev/docs/cli/telemetry` (`packages/1-framework/3-tooling/cli/src/orm/cli.ts:47`; `reporting.ts:24-31`), not to this page's URL. Source-side issue, noted because users will arrive from that link. + +## Skill vs site + +- `skills/prisma-8/references/queries.md:146` says "Streaming large result sets. No `.stream()` cursor today" and recommends holding a `pg.Client` to stream; the site (`orm/fundamentals/reading-data.mdx:280-313`) says `for await` over `.all()` streams without buffering. The source matches the site: `collection.ts:998-1022` exposes the async-iterable read terminal and the Postgres driver iterates via `pg-cursor` (`packages/3-targets/7-drivers/postgres/src/postgres-driver.ts:374-384`). The skill is stale. Overruled on the memory claim: `postgres()` disables the driver cursor (`packages/3-extensions/postgres/src/runtime/postgres.ts:245-247`), so the result is buffered before `for await` starts; the skill is closer to the truth there. See `orm-client.md` and `slice-corrections.md` decision 3; the reading-data page was corrected in #8238. +- `skills/prisma-8/references/runtime.md:11,135-158,315,322` tells users to import `createTelemetryMiddleware` from `@internal/middleware-telemetry`; no such package exists in the rc.9 tree (only comments and old plan docs mention it). The site's "Three middleware ship with Prisma 8 today" (`orm/middleware/how-middleware-works.mdx:82`: lints, budgets, cache) matches the source (`packages/2-sql/5-runtime/src/middleware/{lints,budgets}.ts`, `packages/3-extensions/middleware-cache/`). The skill is stale; also `runtime.md` never mentions the cache middleware the site documents. +- `skills/prisma-8/references/contract.md:25,29,49` uses `cipherstash.EncryptedString(...)` as a canonical extension example; no cipherstash package exists in the rc.9 tree (`wip/prisma-src/CHANGELOG.md:1040` records `@prisma-next/extension-cipherstash` being dropped in favour of CipherStash's own `@cipherstash/prisma-next`, `README.md:82`). The site does not mention cipherstash, which is correct. +- `skills/prisma-8/references/contract.md:179` writes `pgvector.Vector(length: 1536)`; the site (`orm/extensions/using-extensions.mdx`) and `examples/prisma-8-demo/src/prisma/contract.prisma:4` use positional `pgvector.Vector(1536)`. The examples compile against rc.9, so the site matches. +- `skills/prisma-8/SKILL.md` and `skills/README.md` describe one skill (`prisma-8`) synced from installed packages by `prisma init` / `prisma skills sync`; `ai/tools/skills.mdx:100-114` describes three skills installed by `orm init` from GitHub. The source matches the skill (see Breaks copy-paste and Misleading above). +- `docs/Telemetry.md` says "No outcome data. Phase 1 does not collect success/failure, exit code"; the rc.9 sender includes `exitCode` (`packages/1-framework/3-tooling/cli-telemetry/src/payload.ts:115`, `enrich.ts:222`; `reporting.ts:86-99` fires from `onSettled` with `summary.exitCode`). The monorepo doc is stale; the site says nothing either way. + +## Part 2: targeted gap questions + +Each item: is it supported at rc.9, where the evidence is, and whether the ORM 8 part of the site documents it. Site greps covered `orm/` (not v6/v7), `cli/` (not v7), `(index)/prisma-orm/`, `(index)/getting-started.mdx`, `guides/` (not v7). + +### Supported and undocumented (these are the Missing items above) + +- Row level security outside Supabase — supported (Missing item 2). Site: no page; only error codes. +- Expression and functional indexes — supported (Missing item 3). Site: none. +- Enabling `pg_trgm` and other extensions from a migration — supported via `createExtension()` op in `migration.ts`; not declarable in the contract (Missing item 8). Site: none beyond packs that ship their own migration. +- `Decimal` precision and scale, `@default` on Decimal, aggregate return types — supported (Missing item 6). Site: `Decimal` is not even in the scalar table (`orm/data-modeling/index.mdx:152-160`). +- SQLite target — ships (Missing item 7). Site: `orm/index.mdx:36` still says planned (known); no runtime or quickstart coverage. +- Vite plugin — ships (Missing item 5). Site: none. +- `@@control` policies — supported (Missing item 4). Site: none. +- Streaming — `for await` is supported and documented, but on `postgres()` it does not stream: the facade disables the driver cursor and the result is buffered first (Missing item 9, overruled note; `slice-corrections.md` decision 3). + +### Supported and documented + +- `await using` on the client — `[Symbol.asyncDispose]` on the Postgres, SQLite, Mongo, Supabase and serverless clients (`packages/3-extensions/postgres/src/runtime/postgres.ts:73,381`, `sqlite/src/runtime/sqlite.ts:85,354`, `mongo/src/runtime/mongo.ts:40,237`, `supabase/src/runtime/supabase.ts:111,491`, `postgres-serverless.ts:181`). Site: `orm/reference/transactions-and-runtime.mdx:148-164,328-343` documents it, including the block-scope warning that matches `skills/prisma-8/references/runtime.md:318`. +- Streaming results — `for await` over `.all()` / `.createAll()` / update / delete terminals (`packages/3-extensions/sql-orm-client/src/collection.ts:998-1022,1313-1329,1932-1947,2100-2113`). Site: `orm/fundamentals/reading-data.mdx:280-313`, including the "read once" rule. + +### Not supported at rc.9 + +- Filtering on JSON columns by path or containment — not supported. The `jsonb` codec declares only the `equality` trait, so the ORM `where` shorthand allows whole-value `eq` / `in` on a `Json` field and nothing else; the SQL builder has no JSON path, `->>`, or `@>` functions (`packages/3-targets/3-targets/postgres/src/core/codecs.ts:1417-1421`; no `json` hits in `packages/2-sql/4-lanes/sql-builder/src/` or `packages/3-targets/3-targets/postgres/src/core/ast/`). The only JSON-aware builder pieces are `jsonObject` / `jsonArrayAgg` used internally for includes (`sql-orm-client/src/where-binding.ts:72-75`). Instead: use `db.raw.sql\`...\`` or `fns.raw` in a builder `.select(...)` (`skills/prisma-8/references/queries.md:143`), or `@prisma/orm-extension-arktype-json` for typed storage (not filtering). Site: `orm/data-modeling/index.mdx:159` lists `Json` with no note on filter limits — worth a one-line "Misleading by omission" fix there. +- Soft delete, model validations, lifecycle callbacks — not supported (`skills/prisma-8/references/contract.md:392-394`). Instead: `deletedAt DateTime?` plus explicit filters or a middleware; validate with arktype in app code; use middleware for hooks. Site: no ORM 8 page claims these exist (the `validation` / `callback` grep hits are unrelated uses of the words). +- Custom `Collection` subclasses — not a supported extension point. `Collection` is exported as a type-level surface from `@prisma/orm-postgres/orm-client` (`packages/3-extensions/sql-orm-client/src/exports/index.ts:1`), but the implementation is a `CollectionImpl` class with private `#clone` / `#createSelf` methods (`collection.ts:216,2434-2495`) that cannot be subclassed to add behaviour. Instead: wrap queries in plain functions or write middleware. Site: nothing claims otherwise. +- Read replicas and multi-database routing — not supported (`skills/prisma-8/references/runtime.md:323`; `grep -ri replica packages/` finds only the Mongo replica-set note in `packages/1-framework/3-tooling/cli/src/commands/init/templates/env.ts:44-47` and the cache store). Instead: construct one client per database in separate `db.ts` modules and pick in app code. Site: `guides/database/multiple-databases.mdx` is the only match and is a v7 guide (see Misleading). +- Prisma Composer and a Prisma Postgres native provider — absent from the ORM monorepo. No `@prisma/composer*` code or `prisma+postgres://` handling exists under `packages/` (`grep -ri composer packages/` hits one test; `grep -ri 'prisma+postgres\|accelerate' packages/` hits nothing). Prisma Postgres is reached through the ordinary `pg` driver with a `postgres://` URL (`packages/9-public/@prisma/orm-postgres/package.json` dependencies: `pg`). The scaffold wires Composer's `service.load().database.client` ahead of `DATABASE_URL` (`wip/create-prisma-src/templates/create/_shared/src/prisma/db.ts.hbs`). Instead: nothing needed; the site's Composer coverage lives outside the ORM section and could not be audited against source. +- `pg_trgm` as an extension pack — no pack ships (`packages/3-extensions/`: arktype-json, middleware-cache, paradedb, pgvector, postgis, supabase, plus the postgres / sqlite / mongo façades and sql-orm-client). Instead: `createExtension('pg_trgm')` in a migration plus `@@index(..., type: "gin", ...)` for the index; there are no trigram operators in the builder, so similarity queries go through `db.raw`. + +### Extension packages vs site + +| Package | Public name | Site coverage | +| --- | --- | --- | +| `3-extensions/pgvector` | `@prisma/orm-extension-pgvector` (`/control`, `/pack`, `/runtime`, `/column-types`, `/codec-types`, `/operation-types`) | Worked example throughout `orm/extensions/using-extensions.mdx`; correct | +| `3-extensions/postgis` | `@prisma/orm-extension-postgis` (adds `/geojson`) | Catalog row only (`orm/extensions/using-extensions.mdx:111`) plus error codes; no page for the `Geometry` type or its 7 operations | +| `3-extensions/paradedb` | `@prisma/orm-extension-paradedb` (`/index-types`, `/operation-types`) | Catalog row only (`:112,116`); `key_field`-only caveat matches the README | +| `3-extensions/supabase` | `@prisma/orm-extension-supabase` (`/pack`, `/runtime`, `/contract`; no `/control`) | Catalog row only (`:113`). The page's generic 5-step recipe (import `.../control`, pass to `postgres({ extensions })`) does not work for this package: it is wired via `extensions: [supabasePack]` from `/pack` and the client is built with `supabase()` from `/runtime` (`packages/9-public/@prisma/orm-extension-supabase/package.json`; `skills/prisma-8/references/contract.md:348-367`, `supabase.md`). Report this as Misleading on `orm/extensions/using-extensions.mdx` | +| `3-extensions/arktype-json` | `@prisma/orm-extension-arktype-json` (`arktypeJson(schema)` column factory) | Catalog row only (`:114`); `arktypeJson` never shown | +| `3-extensions/middleware-cache` | `@prisma/orm-extension-middleware-cache` | Dedicated page `orm/middleware/built-in-cache.mdx`; correct | +| lints, budgets (no own package) | `@prisma/orm-postgres/family-runtime` | Dedicated pages `orm/middleware/built-in-lints.mdx`, `built-in-budgets.mdx`; import path matches `packages/0-shared/publish-surface/src/shells.ts:219` | +| cipherstash | none at rc.9 (dropped; see `CHANGELOG.md:1040`) | Correctly absent | +| telemetry middleware | none at rc.9 | Correctly absent (the skill still names it) | + +One more site-side issue from that inventory: every catalog row links to `github.com/prisma/orm/tree/main/packages/3-extensions/#readme`, and those READMEs instruct `pnpm add @internal/extension-`, a private name users cannot install (`packages/3-extensions/pgvector/README.md`, `paradedb/README.md`, `postgis/README.md`). The site's package names are right; its links lead to wrong install commands. + +### Monorepo reference docs vs site + +Written from the sub-agent's report (docs under `wip/prisma-src/docs/`). Only user-facing facts are listed; `mongodb-feature-support-priorities.md`, `mongodb-idioms.md`, `mongodb-primitives-reference.md`, `mongodb-user-journey.md`, `mongodb-user-promise.md`, and `framework-gaps.md` are internal planning or contributor docs and describe intent, not shipped behaviour; treat their claims as unverified unless the source confirms them. + +- `Serverless Deployment Guide.md` — two facades: `postgres()` (long-lived, cursors disabled) and `postgresServerless()` (per-request, cursors enabled by default); `await using runtime = await db.connect({ url })`; Cloudflare Hyperdrive as the tested path (`nodejs_compat`, `wrangler hyperdrive create`, `WRANGLER_HYPERDRIVE_LOCAL_CONNECTION_STRING_*`); migrations run on Node against the origin URL, never through Hyperdrive; one runtime per `connect()` and no crossing runtimes inside a transaction; Workers isolate memory is 128 MiB so `limit()` is the memory cap; bundle about 254 KiB gzipped. Production caveat at lines 97 and 274: against real Hyperdrive the default cursor path hangs (`Protocol Error: Unexpected protocol code: C`, SQLSTATE 58000, request killed at 30 s with Cloudflare error 1101), workaround `cursor: { disabled: true }`. None of this is on the site; the only Cloudflare guide is Prisma 7. The guide's own import paths are `@internal/postgres/*`; the public path is `@prisma/orm-postgres/serverless`. +- `Supported Versions.md` — Node 24, Bun 1.2, Deno 2.0, PostgreSQL 15, MongoDB 8.0, TypeScript 5.9 (optional peer), `tsconfig` must set `moduleResolution: "bundler"` and `strict: true`, `prisma orm init --merge-tsconfig`, ESM-only with no CommonJS. Site: Bun guide says 1.1; PostgreSQL floor stated nowhere; ESM-only and `strict` stated nowhere; `--merge-tsconfig` does not exist in `orm/init.ts` at rc.9 (doc is stale there); the upgrade guide presents `module: nodenext` as the example setting; the doc's Node 24 disagrees with the scaffold's `^22.18.0 || >=24.11.0`. +- `reference/integer-representation-types.md` — `BigInt` (default, `bigint`), `BigIntNumber` (`number`, guarded to 2^53-1), Postgres-only `UnboundedInt`; PSL and TS authoring; the aggregate result matrix per column type; SQLite has no `avgDecimal`. Site documents only the runtime errors, never the types as features. +- `reference/aggregate-descriptor-guide.md` — Postgres contributes `count`, `countBigInt`, `sum`, `sumBigInt`, `avg`, `avgDecimal`, `min`, `max`; `min`/`max` output the input's own type (site says numeric-only); the aggregate map lives in `contract.d.ts` only. +- `reference/codec-authoring-guide.md` — two user-facing caveats buried in a contributor doc: `pg/geometry@1` is exempt from the canonical JSON round-trip guarantee (PostGIS users, no warning on the site), and float codecs need `extra_float_digits >= 1` on the session with nothing enforcing it; `NaN` and infinities are rejected by `sql/float@1`, accepted by `pg/numeric@1`. +- `reference/postgres-native-enums.md` — why a contract `enum` is a CHECK constraint (no in-transaction `ADD VALUE`, no remove or reorder, full-table rewrite on retype); externally managed native enums are supported; self-managed native enums get `ADD VALUE` only from `migration plan`. Site states the CHECK fact and nothing else; `orm-client.mdx:478` documents native-enum ordering without ever saying how to get one. +- `reference/capabilities.md` — disagrees with `orm/contract-authoring/capabilities.mdx` on where `lateral` / `returning` / `jsonAgg` live (`sql.*` vs `postgres.*`), on pack key nesting (`pgvector.cosine` vs `postgres.pgvector.cosine`), on the error code (`E_CAPABILITY_MISSING` vs the site's `ORM.CAPABILITY_MISSING`), and on whether capabilities are negotiated at connect time or fixed at emit time. The site's keys (`sql.scalarList`, `postgres.distinctOn`) do not appear in the doc. Unverified which is current; check `packages/3-targets/3-targets/postgres/src` for the real key strings before touching the site page. The handover notes that `docs/architecture docs/subsystems/` is stale; `docs/reference/` may be too. +- `reference/Mongo Pipeline Builder.md` — the doc is older than the site: it shows `mongoQuery()` from an internal path (site: `db.query.from(...)`), bare single-operation updaters (site says they must return an array and names "older internal material" as the source of the mistake), and `build()` executing (site: terminals return a plan). Site is closer to the source; add `merge()`'s `on` / `whenMatched` / `whenNotMatched` keys and the `null` row type on find-and-modify terminals. +- `mongodb-user-promise.md` claims cascade / restrict / setNull referential integrity for MongoDB as the "strongest differentiator"; `grep onDelete packages/2-mongo-family` (non-test) is empty, so it is not implemented at rc.9. Same doc says budgets and lints work on Mongo; the site says `familyId: 'sql'` and that matches the source. diff --git a/docs/orm-docs-audit/audit/gaps.md b/docs/orm-docs-audit/audit/gaps.md new file mode 100644 index 0000000000..f4815723ff --- /dev/null +++ b/docs/orm-docs-audit/audit/gaps.md @@ -0,0 +1,66 @@ +# Audit: gaps + +Pages read: guides/frameworks/astro.mdx, guides/frameworks/elysia.mdx, guides/frameworks/hono.mdx, guides/frameworks/nestjs.mdx, guides/frameworks/nextjs.mdx, guides/frameworks/nuxt.mdx, guides/frameworks/react-router-7.mdx, guides/frameworks/solid-start.mdx, guides/frameworks/sveltekit.mdx, guides/frameworks/tanstack-start.mdx, guides/deployment/docker.mdx, guides/deployment/cloudflare-workers.mdx, guides/deployment/cloudflare-d1.mdx, guides/deployment/turborepo.mdx, guides/deployment/pnpm-workspaces.mdx, guides/deployment/bun-workspaces.mdx, guides/runtimes/bun.mdx, guides/runtimes/deno.mdx, guides/integrations/github-actions.mdx, guides/integrations/vercel-deployment.mdx, guides/integrations/ai-sdk.mdx. For the reverse audit: `skills/prisma-8/SKILL.md`, every file in `skills/prisma-8/references/`, `docs/glossary.md`, and the `exports` maps of every package under `packages/9-public/@prisma/`. + +Verification sources for the Prisma 8 framework and runtime pages were the create-prisma templates under `wip/create-prisma-src/templates/create/` and its CLI source under `wip/create-prisma-src/src/`, since those pages document what the scaffold generates. + +## Breaks copy-paste + +Ten of the twenty-one pages in this area are Prisma ORM 7 content sitting under a Prisma ORM 8 path. Every install command, config file, schema block, CLI command, and query on them is Prisma 7. None of them has a copy under `guides/v7/` (`guides/v7/` only contains `frameworks/` and `runtimes/`, and neither `react-router-7` nor `solid-start` is in `guides/v7/frameworks/`), so the Prisma 7 content has no v7 home either. One bullet per page; the count is of distinct Prisma 7 constructs on that page. + +- `guides/frameworks/react-router-7.mdx:49` (12 constructs, through line 647) — `npm install prisma@7.10.0 …`, `@prisma/client@7.10.0 @prisma/adapter-pg`, `npx prisma init --output`, `generator client { provider = "prisma-client" }`, `prisma migrate dev`, `prisma generate`, `prisma db seed`, `prisma studio`, `new PrismaClient({ adapter })`, `prisma.user.findMany()`, `findUnique({ where })`, `create({ data })` — Prisma ORM 8 installs `@prisma/orm-postgres` and `prisma`, scaffolds with `prisma orm init` or `create-prisma`, emits with `contract emit`, applies with `db init` / `db update` / `migration plan` + `db migrate`, and queries through `db.orm.public.User.…`; there is no `generate`, `migrate dev`, `db seed`, or `studio` command — evidence: `packages/1-framework/3-tooling/cli/src/orm/{contract,db,migration}/` (the complete command set: `contract emit|infer`, `db init|update|sign|verify|schema`, `migration plan|new|show|status|list|log|graph|check`, `migrate`), `create-prisma-src/src/tasks/install.ts:47-58` (the scripts a scaffold gets), `skills/prisma-8/references/quickstart.md:387-391` (no Studio, no push-style sync), `skills/prisma-8/references/migrations.md:488` (no `db seed` equivalent). +- `guides/frameworks/solid-start.mdx:70` (11 constructs, through line 322: `import { User, Post } from "./generated/prisma/client"`) — same Prisma 7 flow as above — same evidence. +- `guides/deployment/docker.mdx:69` (10 constructs, through line 519) — `prisma@7.10.0`, `@prisma/client@7.10.0`, `prisma init --output`, generator block, `require("./generated/prisma_client/client")`, `"db:deploy": "npx prisma migrate deploy && npx prisma generate"` (line 201), `prisma migrate dev`, `prisma generate`, `npx prisma studio --port 5555` (line 519), and the Alpine `musl` engine note at line 347 — Prisma ORM 8 has no query engine binary to download; the Postgres runtime is the `pg` driver in-process, deployment runs `contract emit` at build time and `db migrate` (or `db init`) at start, and there is no Studio — evidence: `packages/3-extensions/postgres/README.md:8-10` (façade-owned `pg.Pool` / `pg.Client`), `skills/prisma-8/references/build.md:421-435` (`prebuild` emit), `skills/prisma-8/references/migrations.md:487` (run `db migrate` from the deploy pipeline). +- `guides/deployment/cloudflare-workers.mdx:49` (9 constructs, through line 344) — `prisma@7.10.0`, `runtime = "cloudflare"` generator field (line 106), `prisma migrate dev`, `prisma generate`, per-request `new PrismaClient({ adapter })` (line 243), `prisma.user.create({ data })`, `prisma.user.count()` — the Prisma ORM 8 per-request surface is `@prisma/orm-postgres/serverless`: `postgresServerless({ contractJson })` at module scope and `await using runtime = await db.connect({ url })` inside `fetch` — evidence: `packages/9-public/@prisma/orm-postgres/package.json` export `./serverless`, `packages/3-extensions/postgres/README.md:41-60,126-136`. Lines 416 and 418 already link to `/guides/v7/frameworks/hono` and `/orm/v7/...`, confirming the page is v7 content. +- `guides/deployment/cloudflare-d1.mdx:40` (9 constructs, through line 264) — `npx prisma@7.10.0 init --datasource-provider sqlite`, `@prisma/adapter-d1`, `runtime = "cloudflare"`, `prisma migrate diff --from-empty` / `--from-local-d1` (lines 180, 223), `prisma generate`, `new PrismaD1(env.DB)`, `prisma.$disconnect()` — Prisma ORM 8's SQLite target is `@prisma/orm-sqlite` (`sqlite({ contractJson, path })`); there is no D1 driver in the public package set and no `migrate diff` command — evidence: `packages/9-public/@prisma/` directory listing (no `adapter-d1` or D1 driver), `packages/9-public/@prisma/orm-sqlite/package.json` exports, `skills/prisma-8/references/runtime.md:285-296`, CLI command list above. +- `guides/deployment/turborepo.mdx:68` (10 constructs, through line 359) — `prisma@7.10.0`, `@prisma/client@7.10.0`, `prisma init`, generator block, `db:generate`/`db:migrate`/`db:deploy` scripts (`prisma generate`, `prisma migrate dev`, `prisma migrate deploy`), `npx turbo run db:migrate -- --name init`, "In Prisma 7, `migrate dev` no longer runs `prisma generate`" (line 231), `new PrismaClient`, `prisma.user.findFirst()` — Prisma ORM 8's shared package holds `prisma.config.ts`, `src/prisma/contract.prisma`, the emitted `contract.json` / `contract.d.ts`, `db.ts`, and `migrations/app/`; the scripts are `contract:emit`, `db:update`, `migration:plan`, `migrate` — evidence: `skills/prisma-8/references/quickstart.md:127-159` (canonical layout), `create-prisma-src/src/tasks/install.ts:47-58`. +- `guides/deployment/pnpm-workspaces.mdx:43` (10 constructs, through line 305) — `catalogs: prisma: 7.10.0`, `pnpm dlx prisma@7.10.0 init` (line 110), generator block, `prisma generate`/`migrate dev`/`migrate deploy`/`studio` scripts, `new PrismaClient({ adapter })`, `prisma.user.findFirst({ select })` — same as the Turborepo bullet — same evidence. +- `guides/deployment/bun-workspaces.mdx:61` (11 constructs, through line 224) — `prisma@7.10.0`, `@prisma/client@7.10.0`, `prisma init`, generator block, `prisma db seed` script (line 131), `prisma.user.createMany({ data, skipDuplicates: true })` (lines 198-204), `prisma.$disconnect()`, `seed: "bun prisma/seed.ts"` under `migrations` in `prisma.config.ts` (line 224) — Prisma ORM 8 has no `db seed` and no `seed` config key; the bulk insert is `createAll`; a seed is an ordinary script that imports `db` (the scaffolds put it at `src/prisma/seed.ts` and use `upsert` with `conflictOn`) — evidence: `skills/prisma-8/references/migrations.md:488`, `create-prisma-src/templates/create/_shared/src/prisma/seed.ts.hbs`, `skills/prisma-8/references/queries-mongo.md:327` (`createAll`). +- `guides/integrations/github-actions.mdx:46` (9 constructs, through line 445) — `prisma@7.10.0`, `@prisma/client@7.10.0`, `prisma init --output`, generator block, `seed:` in `prisma.config.ts` (line 120), `prisma migrate dev`, `prisma generate`, and the CI steps `npx prisma generate` + `npx prisma db push` (lines 424-427, 442) — the Prisma ORM 8 CI shape is `prisma contract emit`, then `prisma db init --db $URL` for a fresh database (or `migration status --to --db $URL --json` as the check and `db migrate --to --db $URL` to apply); there is no `db push` — evidence: `skills/prisma-8/references/migration-review.md:172-197`, `skills/prisma-8/references/quickstart.md:390`, CLI command list above. +- `guides/integrations/ai-sdk.mdx:56` (10 constructs, through line 392) — `prisma@7.10.0`, `@prisma/client@7.10.0`, `prisma init --output`, generator block, `prisma migrate dev`, `prisma generate`, `new PrismaClient({ adapter })`, `prisma.session.upsert({ where, update, create })` (line 320), `prisma.message.create({ data })` (line 357), `prisma.message.findMany({ orderBy: { createdAt: "asc" } })` (line 392), `npx prisma studio` (line 627) — Prisma ORM 8 spells these `db.orm.public.Session.upsert({ create, update, conflictOn })`, `db.orm.public.Message.create({ … })`, `db.orm.public.Message.orderBy((m) => m.createdAt.asc()).all()` — evidence: `skills/prisma-8/references/queries-postgres.md:196-219,345-377`, `create-prisma-src/templates/create/_shared/src/prisma/seed.ts.hbs` (`conflictOn`). +- All ten pages above: `prisma.config.ts` written as `defineConfig({ schema, migrations: { path }, datasource: { url: env("DATABASE_URL") } })` from `prisma/config` (first at `react-router-7.mdx:125`) — the Prisma ORM 8 config is `definePrismaConfig({ orm: ormConfig({ contract: "./src/prisma/contract.prisma", db: { connection: process.env.DATABASE_URL! } }) })` with `definePrismaConfig` from `prisma/config` and `defineConfig as ormConfig` from `@prisma/orm-postgres/config` — evidence: `create-prisma-src/templates/create/_shared/prisma.config.ts.hbs`, `packages/3-extensions/postgres/src/config/define-config.ts`. +- `guides/runtimes/bun.mdx:161` (also in the agent prompt at line 33) — replace the `build` script with `esbuild src/server.ts --bundle --platform=node --format=esm --outfile=dist/server.mjs` — the minimal scaffold's `build` is `tsdown`, driven by a generated `tsdown.config.ts` whose entry is `{ server: "src/index.ts" }`; `esbuild` is not a dependency the scaffold installs, so `bun run build` fails with a missing `esbuild` binary unless it happens to be hoisted from a transitive dependency; the change that matches the scaffold is editing `entry` in `tsdown.config.ts` to `src/server.ts` — evidence: `create-prisma-src/templates/create/minimal/package.json.hbs` (`"build": "tsdown"`), `create-prisma-src/templates/create/_shared/tsdown.config.ts.hbs`, `create-prisma-src/src/templates/render-create-template.ts:25-30`, `create-prisma-src/src/constants/dependencies.ts` (no `esbuild` entry). + +## Misleading + +- `guides/runtimes/bun.mdx:96` (also lines 99 and 152; `guides/frameworks/hono.mdx:154`; `guides/frameworks/elysia.mdx:125`; `guides/frameworks/nestjs.mdx:138`) — teardown is written as `await db.runtime().close()` — the client's teardown method is `await db.close()` (idempotent, terminal, also wired to `Symbol.asyncDispose`); `runtime().close()` closes the inner `Runtime` without marking the client closed, and every other page on the site (`orm/reference/transactions-and-runtime.mdx:135,145`, `(index)/prisma-orm/add-to-existing-project/mongodb.mdx:115`) uses `db.close()` — evidence: `packages/3-extensions/postgres/src/runtime/postgres.ts:72,374-383`, `skills/prisma-8/references/runtime.md:66-78,126-133`. +- `guides/runtimes/bun.mdx:11` — "Bun runs TypeScript directly, so there is no build step anywhere in the flow" — the same page requires `bun run build` before `deploy` (lines 156-176), and the scaffold's `dev` script is `tsx watch src/index.ts` for every non-Deno package manager, so `bun run dev` (line 104) starts a watcher that never exits rather than running the script once and printing the two lines shown; `bun src/index.ts` is the one-shot run — evidence: `create-prisma-src/templates/create/minimal/package.json.hbs` (`"dev": "tsx watch src/index.ts"` in the non-Deno branch), `create-prisma-src/src/constants/dependencies.ts:57-63` (`tsx` installed for the minimal template). +- `guides/frameworks/nestjs.mdx:88-92` (repeated in the agent prompt at line 31) — tells the reader to add `@Inject(UsersService)` and `@Inject(PrismaService)` when `GET /users` returns 500 — the scaffold already emits both explicit injection tokens, so the note describes a state the template no longer produces — evidence: `create-prisma-src/templates/create/nest/src/users.controller.ts.hbs:7`, `create-prisma-src/templates/create/nest/src/users.service.ts.hbs:8`. +- `guides/frameworks/astro.mdx:27` — "Node.js 24 or later" (same on `nestjs.mdx:17`, `nextjs.mdx:17`, `nuxt.mdx:27`, `sveltekit.mdx:27`, `tanstack-start.mdx:27`, `hono.mdx:17`; 7 pages) — the scaffolds declare `"engines": { "node": "^22.18.0 || >=24.11.0" }` and create-prisma itself refuses anything below 22.18, so Node 22.18+ works and Node 24.0 to 24.10 does not — evidence: `create-prisma-src/templates/create/minimal/package.json.hbs`, `create-prisma-src/templates/create/hono/package.json.hbs`, `create-prisma-src/src/utils/node-version.ts:17-20`. (The known-issue list mentions differing Node claims on the getting-started pages; these are the framework pages.) +- `guides/runtimes/deno.mdx:142` — "The generated ORM tasks currently use the Deno-compatible Prisma 8 ORM CLI entry point. The consolidated `npm:prisma@latest` CLI still loads Node-only credential storage under Deno" — create-prisma now generates every Deno task against the consolidated CLI (`deno run -A --env-file=.env npm:prisma@latest db init` and so on); the separate entry point is gone — evidence: `create-prisma-src/src/constants/dependencies.ts:26-31` (`PRISMA_DENO_CLI_PACKAGE = PRISMA_PLATFORM_CLI_PACKAGE`, "The former `prisma-next` fallback is frozen"), `create-prisma-src/src/tasks/install.ts:25-45`. +- `guides/runtimes/deno.mdx:93-99` — the sample response shows only `email` and `name` per user — the generated `listUsers` (which the page itself prints at lines 108-127) returns `id`, `email`, `username`, `name`, and `createdAt` — evidence: `create-prisma-src/templates/create/_shared/src/prisma/users.ts.hbs`. +- `guides/frameworks/astro.mdx:78` — "`db:init` applies your schema (`src/prisma/contract.prisma`)" (same on `nestjs.mdx:66`, `nextjs.mdx:65`, `nuxt.mdx:76`, `sveltekit.mdx:75`, `tanstack-start.mdx:74`, `bun.mdx:62`; 7 pages) while the same pages say to "answer the prompts for contract authoring style" — with `--authoring typescript` the source is `src/prisma/contract.ts` and the emitted `contract.json` / `contract.d.ts` land under `src/prisma/generated/`, which no page mentions — evidence: `create-prisma-src/templates/create/_shared/prisma.config.ts.hbs`, `create-prisma-src/templates/create/_shared/src/prisma/db.ts.hbs` (`./generated/contract.json` when `authoring === "typescript"`). +- `guides/frameworks/astro.mdx:39` — "Then run `npx prisma@latest init` in `my-astro-app` so the Prisma agent skills are installed" (same step in every framework and Bun agent prompt; 9 pages) — create-prisma already runs `prisma init --yes` after scaffolding for every package manager except Deno, so the step repeats work the scaffold did; harmless but it suggests the scaffold leaves skills uninstalled — evidence: `create-prisma-src/src/tasks/prisma-setup/commands.ts:56-62`. +- `guides/integrations/vercel-deployment.mdx:18` — the only pointer for deploying your own Prisma ORM app to Vercel goes to `/orm/v7/prisma-client/deployment/serverless/deploy-to-vercel`, and line 629 says the demo templates use "Prisma ORM" without saying which major — under the Prisma 8 guides this sends readers to Prisma 7 instructions; there is no Prisma 8 Vercel deployment path on the site (see Missing). + +## Missing + +- A Prisma ORM 8 deployment guide for any host: Docker, Cloudflare Workers, Vercel, and the three monorepo pages are all Prisma 7 (see above), and nothing under `orm/`, `cli/`, or `(index)/prisma-orm/` mentions Docker, Workers, Lambda, or edge (`grep -i "cloudflare|workers|edge|lambda|docker"` finds nothing there). What a Prisma 8 deploy needs is documented in the source: `prisma contract emit` as a `prebuild` step, `db migrate` (or `db init`) from the pipeline before the app starts, and the per-request façade for edge runtimes — evidence: `skills/prisma-8/references/build.md:421-435`, `skills/prisma-8/references/migrations.md:487`, `packages/3-extensions/postgres/README.md:8-10,41-60` — belongs under `guides/deployment/`. +- `@prisma/orm-postgres/serverless` (`postgresServerless({ contractJson })`, `await using runtime = await db.connect({ url })`, a fresh `pg.Client` per request, no `orm` / `runtime()` / `transaction()` on that façade, `cursor: { disabled: true }`) — the site never mentions it (`serverless` has zero hits under `orm/`, `cli/`, `(index)/prisma-orm/`) — evidence: `packages/9-public/@prisma/orm-postgres/package.json` export `./serverless`, `packages/3-extensions/postgres/src/exports/serverless.ts`, `packages/3-extensions/postgres/README.md:41-60,126-136` — belongs in `orm/reference/transactions-and-runtime.mdx` and a Prisma 8 Cloudflare Workers guide. +- The Vite plugin `@prisma/orm-postgres/vite-plugin-contract-emit` (`prismaVitePlugin('prisma.config.ts', { debounceMs, logLevel })`, Vite 7 and 8, dev-server only, plus the `"prebuild": "prisma contract emit"` pattern for `vite build`, Next.js, Webpack, esbuild, Rollup, and Turbopack) — zero hits on the site — evidence: `packages/9-public/@prisma/orm-postgres/package.json` export `./vite-plugin-contract-emit`, `packages/1-framework/3-tooling/vite-plugin-contract-emit/README.md`, `skills/prisma-8/references/build.md` — belongs in `orm/contract-authoring/the-contract-artifact.mdx` and the Vite-based framework guides (SvelteKit, Astro, Nuxt, TanStack Start), which currently tell readers to run `npm run contract:emit` by hand. +- The Supabase extension end to end: `@@rls` on a model, `policy_select` / `policy_insert` / `policy_update` / `policy_delete` / `policy_all` blocks with `target` / `roles` / `using` / `withCheck`, the cross-space foreign key `supabase:auth.AuthUser`, the async `supabase({ contractJson, url, jwksUrl | jwtSecret })` factory, role binding with `await db.asUser(jwt)` / `db.asAnon()` / `db.asServiceRole()` returning a `RoleBoundDb`, the `asServiceRole().supabase` admin root, the session-pooler requirement (never port 6543), the one-time `GRANT` for `auth.*` reads, and the `SUPABASE.JWT_INVALID` / `SUPABASE.CONFIG_INVALID` errors — the site has one table row (`orm/extensions/using-extensions.mdx:113`) and the two error codes; `@@rls`, `policy_`, `asUser`, `jwksUrl`, and `RoleBoundDb` have zero hits — evidence: `packages/9-public/@prisma/orm-extension-supabase/package.json` (exports `./contract`, `./pack`, `./runtime`), `skills/prisma-8/references/supabase.md`, `skills/prisma-8/references/contract.md:309-331,348-367` — belongs as a page under `orm/extensions/` and a guide under `guides/` (the known-issue list covers RLS outside Supabase; this is RLS with Supabase). +- Control policy: `@@control(managed | tolerated | external | observed)` on a model and `defaultControlPolicy` in the config, and what each policy does to planned DDL and `db verify` — zero hits on the site (`control policy` appears only in `guides/integrations/permit-io.mdx`) — evidence: `skills/prisma-8/references/contract.md:333-346`, `skills/prisma-8/references/migrations.md:501-503`, `packages/9-public/@prisma/orm-postgres/package.json` export `./contract/apply-specifier-default-control-policy` — belongs in `orm/contract-authoring/psl-syntax.mdx` and `orm/migrations/how-migrations-work.mdx`. +- Index and check-constraint options: `@@index(expression: …, name: …)`, `@@index([…], where: "…", name: …)`, `unique:`, `type:` / `options:`, `@@check(expression: …, name: | map: …)`, `@noCheck` / `@noCheck(membership)` / `@noCheck(elementNotNull)`, and the naming rule behind `name:` (wire name, `_<8 hex>`) versus `map:` (exact name, body compared byte for byte, warns `PN_EXACT_NAME_BODY_COMPARISON`) — `orm/contract-authoring/psl-syntax.mdx:97,137` shows only `@@index([authorId])`; `@@check`, `@noCheck`, and `wire name` appear only inside `orm/reference/error-reference.mdx` — evidence: `skills/prisma-8/references/contract.md:101-108,265-290`, `packages/2-sql/1-core/contract/src/index-naming.ts:72`, `docs/glossary.md` "Naming Mode" and "Wire Name" — belongs in `orm/contract-authoring/psl-syntax.mdx` (functional and partial indexes are also on the known-missing list; this bullet adds the syntax and the naming rule). +- CLI telemetry opt-out via environment: `DO_NOT_TRACK=1`, `PRISMA_NEXT_DISABLE_TELEMETRY=1`, and `"enableTelemetry": false` in the per-user config — `cli/telemetry.mdx` lists only `telemetry status|enable|disable`; `DO_NOT_TRACK` appears on the site only for create-prisma's own telemetry (`(index)/prisma-orm/create-prisma.mdx:98`) — evidence: `packages/1-framework/3-tooling/cli/src/orm/telemetry/reporting.ts:30-31`, `packages/1-framework/3-tooling/cli-telemetry/src/gating.ts:17-26` — belongs on `cli/telemetry.mdx`. +- The full set of package scripts create-prisma generates: besides `contract:emit`, `db:init`, and `db:update` the scaffold adds `db:verify`, `migration:plan`, `migrate`, `migration:status`, `migration:show`, `skills:sync`, `composer:dev`, `composer:deploy`, `deploy` (build + composer deploy), and `dev:composer` — only `db:init`, `db:update`, `contract:emit`, and one `migration:plan` mention exist on the site — evidence: `create-prisma-src/src/tasks/install.ts:25-70` — belongs on `(index)/prisma-orm/create-prisma.mdx` and in each framework guide's "Where things live". +- The `composer` section of `prisma.config.ts` (`composer: { configPath: "./prisma-composer.config.ts" }`) and the generated Composer files every scaffold ships: `prisma-composer.config.ts` (`defineConfig({ extensions: [prismaCloud(), nodeBuild(), nextjsBuild()], state: prismaState() })`), `src/prisma/composer.ts` (`dataContract(contractJson)`), and the `db.ts` pattern `service.load().database.client ?? postgres({ contractJson, url: process.env.DATABASE_URL })` with `import "temporal-polyfill/global"` — `cli/configuration.mdx` documents only the `orm` and `skills` sections, and no framework page shows the generated `db.ts` — evidence: `create-prisma-src/templates/create/_shared/prisma.config.ts.hbs`, `prisma-composer.config.ts.hbs`, `src/prisma/composer.ts.hbs`, `src/prisma/db.ts.hbs` — belongs on `cli/configuration.mdx` and the framework guides. +- Where the automatic seed lives: `src/prisma/seed.ts` (three users via `upsert({ create, update: {}, conflictOn: { email } })`, memoised) called from `src/prisma/users.ts` before every `listUsers` — the framework pages say "seeded automatically on the app's first query" without naming either file (only `nestjs.mdx:99` and `sveltekit.mdx:89` name `users.ts`; the Deno page names both) — evidence: `create-prisma-src/templates/create/_shared/src/prisma/seed.ts.hbs`, `users.ts.hbs` — belongs in each framework guide's "Where things live". +- PSL scalar names the starter contract and the aggregate types use: `TimestamptzString` (the starter `createdAt` column), `BigIntNumber`, `UnboundedInt` — `TimestamptzString` and `BigIntNumber` appear only in `orm/reference/error-reference.mdx`, `UnboundedInt` nowhere; `psl-syntax.mdx` uses `DateTime` throughout — evidence: `packages/1-framework/3-tooling/cli/src/commands/init/templates/code-templates.ts:180`, `skills/prisma-8/references/queries-postgres.md:405-417` — belongs in `orm/contract-authoring/psl-syntax.mdx` (see Unverified for the `DateTime` question). +- The public packages `@prisma/orm-toolchain` (CLI, emitter, config loader, language server, Vite plugin) and `@prisma/orm-framework` (target-agnostic contract, components, PSL parser and printer) are never named — evidence: `packages/9-public/@prisma/orm-toolchain/package.json`, `packages/9-public/@prisma/orm-framework/package.json` — belongs on `orm/reference/index.mdx` or `cli/index.mdx` wherever the package set is listed. +- A Prisma 8 CI workflow: `prisma migration status --to --db "$URL" --json`, failing the job on any `diagnostics[]` entry with `severity: "warn"`, then `prisma db migrate --to --db "$URL"`; `migration status` exits 0 on warnings, so exit-code checks alone miss `MIGRATION.DIVERGED`, `MIGRATION.MARKER_NOT_IN_HISTORY`, `CONTRACT.AHEAD`, and `MIGRATION.NO_MARKER` — the only CI guide (`guides/integrations/github-actions.mdx`) is Prisma 7 — evidence: `skills/prisma-8/references/migration-review.md:172-197` — belongs in a rewritten `guides/integrations/github-actions.mdx`. + +## Unverified + +- `HostedStateBootstrapError` and the `--name` recovery (every framework page's deploy section, `guides/runtimes/bun.mdx:33,186`, `cli/deploy.mdx:28`) — the Composer / deploy implementation is not in `wip/prisma-src` (`grep -rl HostedStateBootstrapError packages` is empty) and the Composer packages are not in the worktree, so the error name and behaviour could not be checked. +- `guides/runtimes/deno.mdx:29,65` — `npx create-db@latest --env .env --ttl 24h` — the `create-db` source is not in the worktree; flags not checked. +- `guides/runtimes/deno.mdx:17` "Deno 2.0 or later" and `guides/frameworks/elysia.mdx:17` "Bun 1.1 or later" — no engine constraint for Deno or Bun in `create-prisma-src` or `prisma-src`. +- `orm/contract-authoring/psl-syntax.mdx:39` and the framework guides' starter contract — the site writes `createdAt DateTime` while the rc.9 starter contract writes `createdAt TimestamptzString @default(now())` (`code-templates.ts:180`); I could not find the PSL scalar-name table in `packages/2-sql/2-authoring/contract-psl/src` by grep (`'DateTime'` and `TimestamptzString` have no hits there), so whether `DateTime` still parses is not confirmed. Another area owns `psl-syntax.mdx`. +- The scaffold pins `@prisma/orm-postgres` and `@prisma/orm-mongo` to `8.0.0-rc.8` (`create-prisma-src/src/constants/dependencies.ts:8-10`, "Must match @prisma/composer-prisma-cloud's exact peerDependency") while this audit is against rc.9; whether the published `create-prisma@latest` has moved could not be checked offline. Any rc.9-only behaviour the framework pages describe would not hold on a fresh scaffold until it does. + +## Skill vs site + +- Skill `references/debug.md:306-338`, `contract.md:62-66`, `migrations.md:67-75` route on numeric codes `PN-CLI-4001…4020`, `PN-MIG-2001…2005`, `PN-RUN-3001…3030`, `PN-SCHEMA-0001`; no such string exists in rc.9 `packages/` (`grep -rn "PN-MIG-[0-9]\|PN-CLI-[0-9]\|PN-RUN-[0-9]" packages --include='*.ts'` is empty); the site's `orm/reference/error-reference.mdx` uses the dotted `DOMAIN.CODE` form only. Site matches the source; the skill is stale here. +- Skill `references/quickstart.md:125` says the marker is a `pn_meta_marker` row; the source writes `prisma_contract.marker` and so does the site (`orm/migrations/the-migration-graph.mdx`, `orm/reference/error-reference.mdx`) — evidence: `packages/3-targets/3-targets/postgres/src/core/migrations/runner.ts:321-326`. Site matches the source. +- Skill `references/runtime.md:135-158` documents `createTelemetryMiddleware` from `@internal/middleware-telemetry`; no such package exists in `packages/` or `packages/9-public/@prisma/` (grep empty). The site does not mention it, which is correct. +- Skill `references/runtime.md:66-78` and `queries.md:107-127` end scripts with `await db.close()`; the site's Bun, Hono, Elysia, and NestJS guides use `db.runtime().close()` while `orm/reference/transactions-and-runtime.mdx:135,145` uses `db.close()`. The source defines `close()` on the client (`packages/3-extensions/postgres/src/runtime/postgres.ts:72,374`); the skill and the reference page match it, the four guides do not. +- Skill `references/build.md:466` says Next.js has no first-party build integration and needs a `prebuild` emit; the site's `guides/frameworks/nextjs.mdx:117` tells readers to run `npm run contract:emit` by hand after schema changes and never mentions `prebuild`. Both agree there is no plugin; the site omits the workaround the skill gives. diff --git a/docs/orm-docs-audit/audit/migrations-cli.md b/docs/orm-docs-audit/audit/migrations-cli.md new file mode 100644 index 0000000000..f5340752d6 --- /dev/null +++ b/docs/orm-docs-audit/audit/migrations-cli.md @@ -0,0 +1,66 @@ +# Audit: migrations-cli + +Pages read: orm/migrations/how-migrations-work.mdx, orm/migrations/generating-a-migration.mdx, orm/migrations/editing-a-migration.mdx, orm/migrations/applying-a-migration.mdx, orm/migrations/rollbacks-and-recovery.mdx, orm/migrations/the-migration-graph.mdx, cli/index.mdx, cli/global-flags.mdx, cli/configuration.mdx, cli/error-reference.mdx, cli/init.mdx, cli/orm-init.mdx, cli/skills.mdx, cli/dev.mdx, cli/git.mdx, cli/db-init.mdx, cli/db-update.mdx, cli/db-migrate.mdx, cli/db-sign.mdx, cli/db-verify.mdx, cli/db-schema.mdx, cli/migration-new.mdx, cli/migration-plan.mdx, cli/migration-ref.mdx, cli/migration-show.mdx, cli/migration-status.mdx + +Monorepo paths below are relative to `wip/prisma-src/`. `cli/src` means `packages/1-framework/3-tooling/cli/src`. + +## Breaks copy-paste + +- `orm/migrations/how-migrations-work.mdx:56-62` (also `generating-a-migration.mdx:74-80,85-86`, `editing-a-migration.mdx:63-66`) — the migration directory contains `start-contract.json`, `end-contract.json` and `*-contract.d.ts`, and `migration.ts` imports `./end-contract` — since 0.17 every contract snapshot lives once in `migrations/snapshots//contract.json` + `contract.d.ts`, and the rendered `migration.ts` imports `../../snapshots//contract.json` and `../../snapshots//contract` (types). The directory holds only `migration.ts`, `ops.json`, `migration.json` (plus `contract.prisma` in the examples). Copying the page's imports into a real migration fails to resolve — evidence: `docs/releases/v0.17.0.md:47`, `examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.ts:2-10`, `packages/1-framework/3-tooling/migration/src/space-layout.ts:55-66` +- `orm/migrations/the-migration-graph.mdx:130` — `migration ref set prod sha256:f9a41d7...` — hashes are bare hex since 0.17 and loaders reject the `sha256:` form; the ref parser only accepts a 64-hex hash, a 6+ hex prefix, a ref name, a migration dir, or `^`. Same stale prefix on every hash the pages print: `how-migrations-work.mdx:119-123`, `generating-a-migration.mdx:47,144-145`, `applying-a-migration.mdx:32`, `rollbacks-and-recovery.mdx:37-38`, `the-migration-graph.mdx:40` — evidence: `docs/releases/v0.17.0.md:45`, `packages/1-framework/3-tooling/migration/src/refs/contract-ref.ts:14-24,56-62`, `examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.json` +- `orm/migrations/the-migration-graph.mdx:187-188` — `ref set prod @db` and `ref list` — the commands are `migration ref set` / `migration ref list` (the bare `ref` group was retired in rc.5), and `migration ref set` is offline: `@db` parses to an empty sentinel hash that is not a graph node, so it fails with `MIGRATION.HASH_NOT_IN_GRAPH` rather than reading the database marker — evidence: `docs/releases/v8.0.0-rc.5.md:9`, `cli/src/orm/cli.ts:104-106`, `packages/1-framework/3-tooling/migration/src/refs/contract-ref.ts:46-52`, `cli/src/control-api/operations/ref.ts:104-118` +- `cli/orm-init.mdx:38` — `--skip-skills` — the flag was removed in rc.6 together with the skill install it opted out of; `orm init` no longer installs skills at all. The flag set is `--target`, `--authoring`, `--schema-path`, `--write-env`, `--probe-db`, `--strict-probe`, `--skip-install`, `--keep-previous-facade` — evidence: `docs/releases/v8.0.0-rc.6.md:29`, `cli/src/orm/init.ts:7-26` +- `cli/orm-init.mdx:53` — `CLI.INIT_SKILL_INSTALL_FAILED` — retired with the skill install (rc.6); no such code exists in the tree — evidence: `docs/releases/v8.0.0-rc.6.md:29`, grep of `packages/` for `INIT_SKILL_INSTALL_FAILED` returns nothing +- `orm/migrations/editing-a-migration.mdx:75-80` — `sql({ context: createExecutionContext(...) })` — `SqlOptions.rawCodecInferer` is a required field of `sql()`; the runtime passes `stack.adapter.rawCodecInferer`. As written the block does not typecheck, so `node migration.ts` fails before emitting `ops.json` — evidence: `packages/2-sql/4-lanes/sql-builder/src/runtime/sql.ts:11-19`, `packages/3-extensions/postgres/src/runtime/postgres-serverless.ts:136` +- `orm/migrations/the-migration-graph.mdx:117-118` — `git clone https://github.com/prisma/orm` then `cd prisma` — the clone directory is `orm` — evidence: `packages/9-public/@prisma/orm-postgres/package.json` repository url `https://github.com/prisma/orm.git` + +## Misleading + +- `orm/migrations/generating-a-migration.mdx:164-166`, `cli/migration-plan.mdx:11`, `orm/migrations/the-migration-graph.mdx:137` — "without `--from` or a `db` ref, `migration plan` plans from an empty database: a full CREATE-everything migration" — since rc.8 the command refuses with `MIGRATION.PLAN_ORIGIN_UNKNOWN` whenever migrations already exist on disk and names three exits (`migration ref set db `, `--from `, or the explicit opt-in `--from @empty`). Only an empty `migrations/app/` still plans from empty silently — evidence: `docs/releases/v8.0.0-rc.8.md:17`, `cli/src/utils/cli-errors.ts:428-475`, `cli/src/orm/migration/plan.ts:206` +- `orm/migrations/how-migrations-work.mdx:20-26`, `orm/migrations/generating-a-migration.mdx:157-161`, `orm/migrations/the-migration-graph.mdx:137`, `cli/index.mdx:70-73,86-92` — the pages present `db migrate --advance-ref db` as the way the `db` ref gets created, and every `db init` / `db update` example passes `--db "$DATABASE_URL"`. In the source `db init` and `db update` advance the `db` ref automatically when run without `--db`; passing `--db` suppresses that unless `--advance-ref` is also given. A user who follows the site's `db init --db ...` then `migration plan` then `db migrate --db ...` loop has no `db` ref, so the second `migration plan` hits `MIGRATION.PLAN_ORIGIN_UNKNOWN` — evidence: `cli/src/control-api/operations/ref-advancement.ts:19-28,116-130`, `skills/prisma-8/references/migrations.md:99-101`, `skills/prisma-8/references/migration-model.md:53-61` +- `orm/migrations/rollbacks-and-recovery.mdx:51,75` — "after a rollback the next `migration plan` fails with `MIGRATION.NO_TARGET` because the planner can no longer infer the latest state" — `migration plan` never infers a latest state: its origin is `--from`, else the `db` ref, else the rc.8 refusal above. `MIGRATION.NO_TARGET` is thrown by `findLeaf`, which `migration new` uses for its default `--from` and `migration ref set` uses for its suggestion text; `migration-plan.ts` does not call it — evidence: `packages/1-framework/3-tooling/migration/src/migration-graph.ts:505-518,544-550`, `cli/src/control-api/operations/migration-new.ts:143`, `cli/src/control-api/operations/ref.ts:115`, grep of `cli/src/control-api/operations/migration-plan.ts` for `findLeaf|findLatestMigration` returns nothing +- `orm/migrations/generating-a-migration.mdx:50,63`, `orm/migrations/applying-a-migration.mdx:34,37,65,68,113,116`, `orm/migrations/rollbacks-and-recovery.mdx:85,88` — the pages reproduce `prisma-cli migrate` / `prisma-cli migration status` hints and explain them as "the binary's own name for itself" — since rc.5 every hint uses the `{bin}` placeholder and the mounted path, so the output reads `prisma db migrate --to ...` / `prisma migration status`. The explanatory sentences are now wrong and the sample output is stale — evidence: `docs/releases/v8.0.0-rc.5.md:9`, `cli/src/orm/migration/status.ts:149`, `cli/src/utils/migrate-failure.ts:13` +- `orm/migrations/applying-a-migration.mdx:111`, `orm/migrations/rollbacks-and-recovery.mdx:83` — `(PN-RUN-3000)` in the failure output — the `PN-*` numeric codes were replaced by dotted codes in 0.17; runner failures surface as `MIGRATION.RUNNER_FAILED`, `MIGRATION.EXECUTION_FAILED`, `MIGRATION.PRECHECK_FAILED` or `MIGRATION.POSTCHECK_FAILED`. `cli/configuration.mdx:146` tells users to branch on the code, so the stale code matters — evidence: `docs/releases/v0.17.0.md:27`, `packages/1-framework/1-core/errors/src/execution.ts:255-275`, grep of `packages/` for `PN-RUN` returns nothing +- `orm/migrations/generating-a-migration.mdx:46` — `from: null` in the plan output — the CLI prints `from: (baseline)` for a null origin — evidence: `cli/src/orm/migration/plan.ts:24-31` +- `orm/migrations/how-migrations-work.mdx:44`, `orm/migrations/applying-a-migration.mdx:26,153` — `✔ Applied 1 migration(s) (3 operation(s)) across 1 contract space(s)` — the summary line is `Applied operation(s) across contract space[s]` (no migration count, no `(s)` on space), or `Database already matches contract ...` when nothing ran — evidence: `cli/src/orm/db/migration-blocks.ts:145-153` +- `orm/migrations/the-migration-graph.mdx:174` — "No baseline. You cannot yet adopt an existing database's history as a starting node without replaying it" — the planner emits an automatic baseline (`null → ref-hash`) plus the delta when the graph is empty and the `db` ref names a stored snapshot, and an explicit `--from @empty` plan at the deployed contract state is the supported retrofit; a baseline is never replayed against a marked database because the runner starts at the marker — evidence: `cli/src/control-api/operations/migration-plan.ts:369,505-545`, `skills/prisma-8/references/migration-model.md:75,139-150` +- `orm/migrations/the-migration-graph.mdx:139-142` — "two reserved tokens (`@contract`, `@db`) work anywhere a command accepts a contract reference" — there is a third, `@empty`, and `@db` is not accepted by the offline commands (`migration ref set`, see above); `migration plan --from` documents `@empty` but not `@db` — evidence: `packages/1-framework/3-tooling/migration/src/refs/contract-ref.ts:14-24`, `cli/src/orm/migration/plan.ts:206` +- `cli/orm-init.mdx:78` — "the scaffolded `db.ts` reads `DATABASE_URL` from the process environment; your own scripts do not load `.env`, run them with `node --env-file=.env`" — the scaffolded `db.ts` starts with `import 'dotenv/config'`, so any script that imports it loads `.env` — evidence: `cli/src/commands/init/templates/code-templates.ts:324-345` +- `cli/orm-init.mdx:53` — "the `contract:emit` script it adds calls `prisma-cli`" — the script is `prisma contract emit` — evidence: `cli/src/commands/init/hygiene-package-scripts.ts:19` +- `cli/orm-init.mdx:44-49` — the "what it creates" list omits `prisma-next.md`, the quick-reference file the scaffold writes (and replaces on re-run, which is why re-running asks for consent) — evidence: `cli/src/orm/init-scaffold.ts:44-59` +- `cli/migration-plan.mdx:18`, `cli/index.mdx:88`, `cli/migration-new.mdx:16,32`, `orm/migrations/generating-a-migration.mdx:211` — hyphenated `--name add-users-table` / `split-name` / `add-orders-table` — the slug is lower-cased and every non-alphanumeric character becomes `_`, so the directory is `..._add_users_table`. The command runs; the directory name shown to the user differs from the flag — evidence: `packages/1-framework/3-tooling/migration/src/io.ts:456-476` +- `cli/error-reference.mdx:13,19` — "this page lists every code the CLI can emit" — the page carries only the platform and engine namespaces (`AUTH`, `CLI`, `INIT`, `SKILLS`, ...). None of the ORM command codes (`MIGRATION.*`, `CONTRACT.*`, `CONFIG.*`) appear, and the page does not link to `orm/reference/error-reference.mdx` where they belong — evidence: `cli/src/utils/cli-errors.ts` (codes such as `MIGRATION.PLAN_ORIGIN_UNKNOWN`, `MIGRATION.HASH_MISMATCH`, `CONFIG.DB_CONNECTION_REQUIRED`), `packages/1-framework/0-foundation/utils/src/structured-error.ts:90-91` (`docsUrl` base is `.../orm/v8/reference/error-reference`) + +## Missing + +- `--from @empty` and the `MIGRATION.PLAN_ORIGIN_UNKNOWN` refusal — evidence: `cli/src/utils/cli-errors.ts:428-475`, `cli/src/orm/migration/plan.ts:206` — belongs on `cli/migration-plan.mdx` (options and the origin paragraph) and `orm/migrations/generating-a-migration.mdx` +- Automatic `db` ref advancement by `db init` / `db update` when `--db` is omitted, and its suppression when `--db` is passed — evidence: `cli/src/control-api/operations/ref-advancement.ts:19-28` — belongs on `cli/db-init.mdx`, `cli/db-update.mdx`, `cli/migration-ref.mdx`, `orm/migrations/generating-a-migration.mdx#the-db-ref` +- The auto-baseline: on an empty graph with a `db` ref, `migration plan` writes two packages (`_baseline` and the delta) in one run — evidence: `cli/src/control-api/operations/migration-plan.ts:369,505-545` — belongs on `orm/migrations/generating-a-migration.mdx` and `cli/migration-plan.mdx` +- `db update` never plans `data`-class operations, so a change that needs a backfill fails and the user must switch to `migration plan` — evidence: `cli/src/control-api/operations/db-update.ts:17` (`allowedOperationClasses: ['additive', 'widening', 'destructive']`) versus `migrate.ts:283` and `migration-plan.ts:95` which include `'data'` — belongs on `cli/db-update.mdx` and `orm/migrations/rollbacks-and-recovery.mdx:103` +- `migration ref set` refusals: the target must be the `to` hash of an on-disk package (`MIGRATION.HASH_NOT_IN_GRAPH`, `MIGRATION.BUNDLE_NOT_FOUND_FOR_GRAPH_NODE`) and may not be the empty sentinel (`MIGRATION.REF_SET_EMPTY_SENTINEL`) — evidence: `cli/src/control-api/operations/ref.ts:112-124` — belongs on `cli/migration-ref.mdx` +- `migration new` default origin: with no `--from` it takes the single leaf reachable from the empty hash and fails with `MIGRATION.NO_TARGET` (cycles, i.e. after a rollback) or `MIGRATION.AMBIGUOUS_TARGET` (two branch tips); `--from` matches a package's `to` hash by prefix only (not refs or dir names); an identical from/to hash is refused unless `--from` is explicit — evidence: `cli/src/control-api/operations/migration-new.ts:131-155`, `packages/1-framework/3-tooling/migration/src/migration-graph.ts:505-530` — belongs on `cli/migration-new.mdx` and `orm/migrations/rollbacks-and-recovery.mdx` +- The `orm` config keys `migrations.dir` and `formatter` (`indent`, `newline`, used by `contract format`) — evidence: `packages/1-framework/1-core/config/src/config-types.ts:32-37,109-113`, `examples/prisma-8-demo/fixtures/showcase/prisma.config.ts:19-21` — belongs on `cli/configuration.mdx` +- Telemetry opt-out environment variables `PRISMA_NEXT_DISABLE_TELEMETRY=1` and `DO_NOT_TRACK=1` — evidence: `packages/1-framework/3-tooling/cli-telemetry/src` (grep), `skills/prisma-8/references/migrations.md:505-507` — belongs in the `cli/configuration.mdx` environment-variable table (check `cli/telemetry.mdx`, outside this area, for consistency) +- The `node migration.ts` self-emit CLI accepts `--dry-run` and `--config ` — evidence: `cli/src/migration-cli.ts:109-125` — belongs on `orm/migrations/editing-a-migration.mdx` +- The full set of Postgres migration factories on the `Migration` base class (`createIndex`, `dropIndex`, `addForeignKey`, `addUnique`, `addCheckConstraint`, `alterColumnType`, `setDefault`, `dropDefault`, `dropNotNull`, native enum create/drop/add-value, `installExtension`, RLS enable/disable and policy create/drop/rename) plus the free factories `createExtension`, `rawSql`, `foreignKey`, `unique`, `checkExpression`, `fn`, `lit` — evidence: `packages/3-targets/3-targets/postgres/src/core/migrations/postgres-migration.ts:159-506`, `packages/3-targets/3-targets/postgres/src/exports/migration.ts` — the site shows only `createSchema`, `createTable`, `addColumn`, `setNotNull`, `dataTransform`, `rawSql`; a factory reference belongs beside `orm/migrations/editing-a-migration.mdx` +- `migration status --json` carries `diagnostics[]` (`MIGRATION.MARKER_NOT_IN_HISTORY`, `MIGRATION.MISSING_INVARIANTS`, `CONTRACT.UNREADABLE` verified in `cli/src/orm/migration/status-findings.ts`) with `severity` for CI to branch on; the process exits 0 on warn-level findings — belongs on `cli/migration-status.mdx` +- `orm init` exit codes `4` (scaffold written, install failed) and `5` (installed, contract emit failed) — evidence: `cli/src/orm/init.ts:29-31,114` — belongs on `cli/orm-init.mdx` +- `db migrate` refuses before any DDL with `MIGRATION.MARKER_MISMATCH` when the marker is not a graph node, and with `MIGRATION.PATH_UNREACHABLE` when no path exists — evidence: `cli/src/utils/cli-errors.ts` (both codes), `skills/prisma-8/references/migration-review.md:257-262` — `applying-a-migration.mdx:124` describes the behaviour without the codes; belongs on `cli/db-migrate.mdx` + +## Unverified + +- `cli/global-flags.mdx` (whole page), `cli/init.mdx` (whole page), `cli/skills.mdx` (allowlist including `@prisma/composer`, target directories, the staleness check and its suppression list), `cli/configuration.mdx:13,30,94` (`definePrismaConfig` from `prisma/config`; `defineConfig` from `@prisma/cli-engine` as an alias; the `agents` list) — all implemented in `@prisma/cli-engine` / the `prisma-cli` repo, which is not in the tree (`node_modules/@prisma/cli-engine` has no `dist`). What I could see: the monorepo scaffold imports `definePrismaConfig` from `@prisma/cli-engine` (`cli/src/commands/init/templates/code-templates.ts:304`) and the fixture imports `defineConfig` from `@prisma/cli-engine` (`examples/prisma-8-demo/fixtures/showcase/prisma.config.ts:1`), so both names exist there; `skills/` ship inside `@prisma/orm-postgres`, `@prisma/orm-sqlite`, `@prisma/orm-mongo` (`files` in each `package.json`) +- `cli/dev.mdx`, `cli/git.mdx` — platform commands; not in the monorepo +- `cli/db-migrate.mdx:9`, `orm/migrations/applying-a-migration.mdx:150` — "extensions alphabetically, then the app" — matches the command's own help (`cli/src/orm/migrate.ts:203-206`), but `packages/1-framework/3-tooling/migration/src/plan-all-spaces.ts:66-70` sorts every space id alphabetically, where `app` sorts before `pgvector`; I did not trace the apply-order code far enough to say which is right +- `orm/migrations/the-migration-graph.mdx:120` — `migration graph --config fixtures/showcase/prisma.config.ts --legend` from `examples/prisma-8-demo` — `migrations.dir` in a config is resolved against the working directory, not the config file (`cli/src/orm/migration/paths.ts:11-13`, comment in `cli/src/orm/define-command.ts`), which would point at the demo's own `migrations/` rather than the fixture's; but the fixture's own header comment gives the same invocation, so I could not tell whether it works +- `orm/migrations/editing-a-migration.mdx:95-102` — `db.public.user.update({ displayName: 'Anonymous' }).where(...)` — I verified the `sql()` builder root and the `select(...).where(...).limit(1)` check shape against `data-transform.ts:84-95`, but did not verify the mutation builder's `update(values).where(...)` signature (query-builder area) + +## Skill vs site + +- Transaction scope — `skills/prisma-8/references/migrations.md:48` says each Postgres migration runs in its own `BEGIN ... COMMIT`; `applying-a-migration.mdx:120,146` says the whole `db migrate` run is one transaction. The site matches the source: `execute()` issues one `BEGIN`, loops every space's plan (the concatenated path of edges), then one `COMMIT`/`ROLLBACK` — `packages/3-targets/3-targets/postgres/src/core/migrations/runner.ts:176-215,684-696` +- Destructive consent on `db update` — the skill (`migrations.md:33,146,470`) says pass `-y` to auto-accept; the site (`cli/db-update.mdx:30-36`) says type the database name or pass `--confirm `. The site matches the source: `cli/src/orm/db/update.ts:118-124,218-224`, `cli/src/orm/db/consent.ts`; the engine's `CLI.CONSENT_REQUIRED` entry on `cli/error-reference.mdx:147` also says `--yes` does not grant consent +- Error codes — the skill still uses `PN-MIG-2001`, `PN-MIG-2002/2003/2005`, `PN-RUN-3001`, `PN-RUN-3002` (`migrations.md:21,69-75`); the site uses `PN-RUN-3000`. Both are stale; the source has `MIGRATION.UNFILLED_PLACEHOLDER`, `MIGRATION.DATA_TRANSFORM_CONTRACT_MISMATCH`, `CONTRACT.MARKER_MISSING` ("Database not signed"), `CONTRACT.MARKER_MISMATCH` ("Hash mismatch"), `MIGRATION.RUNNER_FAILED` — `packages/1-framework/1-core/errors/src/migration.ts:19-20`, `.../execution.ts:17,34,267`, `docs/releases/v0.17.0.md:27` +- Factory call shape — the skill renders `addColumn('public', 'user', { name, typeSql, defaultSql, nullable })` and `setNotNull('public', 'user', 'name')` as free functions (`migrations.md:231-235,254-270`); the site renders `this.addColumn({ schema, table, column: col(...) })` and `this.setNotNull({ schema, table, column })`. The site matches the source and the rc.9 examples: `postgres-migration.ts:231,364`, `examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.ts:17-21` +- Contract snapshot imports — the skill imports `../../snapshots//contract.json` (`migrations.md:225,253`); the site imports `./end-contract.json`. The skill matches the source (see the first Breaks item) +- `migration show` with no argument — the skill runs `pnpm prisma migration show` bare (`migrations.md:189`); the site says `` is required (`cli/migration-show.mdx:16,23`). The site matches the source: the positional is `positional.string`, not optional — `cli/src/orm/migration/show.ts:3-6` +- `--from` grammar on `migration plan` — the skill lists `@contract | @db | @empty` (`migration-model.md:67`); the site lists neither on `cli/migration-plan.mdx:26` and only `^` on `generating-a-migration.mdx:153`. The parser accepts all three tokens (`contract-ref.ts:14-24`); the command's own brief names only `@empty` (`plan.ts:206`) diff --git a/docs/orm-docs-audit/audit/orm-client.md b/docs/orm-docs-audit/audit/orm-client.md new file mode 100644 index 0000000000..177db445a0 --- /dev/null +++ b/docs/orm-docs-audit/audit/orm-client.md @@ -0,0 +1,63 @@ +# Audit: orm-client + +Pages read: orm/index.mdx, orm/core-concepts.mdx, orm/fundamentals/reading-data.mdx, orm/fundamentals/writing-data.mdx, orm/fundamentals/relations-and-joins.mdx, orm/fundamentals/advanced-queries.mdx, orm/fundamentals/transactions.mdx, orm/reference/index.mdx, orm/reference/orm-client.mdx, orm/reference/transactions-and-runtime.mdx, orm/reference/error-reference.mdx + +## Breaks copy-paste + +- `orm/fundamentals/advanced-queries.mdx:39` (10 occurrences on this page: 39, 61, 75, 101, 123, 138, 177, 200, 218, 230), `orm/core-concepts.mdx:87`, `orm/fundamentals/reading-data.mdx:192`, `orm/fundamentals/relations-and-joins.mdx:73`, `orm/reference/transactions-and-runtime.mdx:96` (and 429-432, 741, 770) — `const rows = await db.runtime().execute(plan)` / `tx.execute(plan)` / `runtime.execute(plan)` used to read rows — since rc.2, `execute(plan)` returns `Promise<{ affectedRows }>` and rows come from `query(plan)`, which returns the `AsyncIterableResult`. Every row-reading example must call `.query(plan)`; only non-returning inserts, updates, and deletes stay on `.execute(plan)` (so `orm/fundamentals/transactions.mdx:60` is correct as written). `transactions-and-runtime.mdx:429-432` also calls `.toArray()` on an `execute` result, which is a stats promise — evidence: `packages/2-sql/4-lanes/relational-core/src/runtime-scope.ts:23-26`, `packages/2-sql/5-runtime/src/sql-runtime.ts:767-790`, `packages/1-framework/1-core/framework-components/src/execution/runtime-middleware.ts:352-358`, `docs/releases/v8.0.0-rc.2.md:72` +- `orm/reference/transactions-and-runtime.mdx:199`, `:279-301` — the MongoDB client exposes `execute()` and `db.execute(plan)` runs a pipeline plan — the row-executing `db.execute` facade method was removed in rc.2; the client exposes `orm`, `query`, `raw`, `contract`, `enums`, `context`, `connect()`, `runtime()`, `close()`. Rows come from `(await db.runtime()).query(plan)` — evidence: `packages/3-extensions/mongo/src/runtime/mongo.ts:28-40, 189-196`, `packages/2-mongo-family/7-runtime/src/mongo-runtime.ts:98-100`, `docs/releases/v8.0.0-rc.2.md:72` +- `orm/fundamentals/advanced-queries.mdx:230` — "`(await db.runtime()).execute(plan)` on MongoDB" — same rename: `(await db.runtime()).query(plan)` for rows — evidence: `packages/2-mongo-family/7-runtime/src/mongo-runtime.ts:98-100` +- `orm/reference/transactions-and-runtime.mdx:581`, `:609-611`, `:662`, `:665-710` — `ps.execute(runtime, { label })` returns rows (`Promise`) — a prepared statement built from a row-returning plan is a `PreparedStatement` whose method is `query(target, params, options?)` returning `AsyncIterableResult`; `execute(target, params)` exists only on `PreparedExecution` (a plan terminated with `.affectedCount()`) and resolves `SqlStatementStats` — evidence: `packages/2-sql/5-runtime/src/prepared/types.ts:55-105`, `docs/releases/v8.0.0-rc.2.md:72` ("Prepared row consumption moves ... to `prepared.query(target, params)`") +- `orm/reference/transactions-and-runtime.mdx:459`, `:467` — `withTransaction`'s `tx` surface is "`execute` (and `executePrepared`) only" — the transaction context is a `RuntimeScope`: `query(plan)` for rows and `execute(plan)` for stats; there is no `executePrepared` method (prepared hooks are symbol-keyed and reached through `prepared.query(tx, params)`) — evidence: `packages/2-sql/4-lanes/relational-core/src/runtime-scope.ts:23-26`, `packages/2-sql/5-runtime/src/sql-runtime.ts:137-141, 786-800` +- `orm/reference/transactions-and-runtime.mdx:653-662` — `await db.prepare(...)` followed by `const runtime = await db.connect()` — `db.prepare` calls `getRuntime()`, which starts the driver connection in the background when a `url` was given; a later `connect()` then throws `DRIVER.ALREADY_CONNECTED` ("Postgres client already connected"). Use `db.runtime()` after any query, `prepare`, or ORM call, and reserve `connect()` for the very first action — evidence: `packages/3-extensions/postgres/src/runtime/postgres.ts:194-199, 245-250, 297-303, 330-339` +- `orm/fundamentals/writing-data.mdx:182-195` — PostgreSQL `upsert({ create, update })` with no `conflictOn`, and the prose "the record is matched on the model's unique fields (here `email`)" — when `conflictOn` is omitted the conflict target falls back to the table's primary key columns, not the unique fields. Because `create` does not supply `id` (it is generated), the statement never conflicts and either inserts a second row or fails on the `email` unique constraint. Pass `conflictOn: { email: ... }` — evidence: `packages/3-extensions/sql-orm-client/src/collection-contract.ts:511-530`, `packages/3-extensions/sql-orm-client/src/collection.ts:1732-1775` + +## Misleading + +- `orm/fundamentals/reading-data.mdx:280-309`, `:362` (and `orm/index.mdx:29` "streaming results") — `for await` over `.all()` "streams" rows from the database with a "steady, flat memory footprint" for millions of rows, and leaving the loop early means "unprocessed records are never buffered" — the `postgres()` facade constructs its driver with `cursor: { disabled: true }`, which the driver documents as buffered execution: the whole result is fetched before iteration begins. `for await` only changes how the already-buffered rows are handed to your code. Cursor streaming exists in the driver (`batchSize`) and is exposed by the serverless entry point only — evidence: `packages/3-extensions/postgres/src/runtime/postgres.ts:245-247`, `packages/3-targets/7-drivers/postgres/src/postgres-driver.ts:39-43, 50-53, 95-101`, `packages/3-extensions/postgres/src/runtime/postgres-serverless.ts:28-42` +- `orm/fundamentals/advanced-queries.mdx:129` — "Prisma 8 does not run standalone raw SQL statements: every query goes through the typed builder" — a whole-query raw lane exists: `db.raw.sql\`SELECT ...\`.returnsRow({ col: 'pg/text@1' }).build()` for typed rows and `.affectedCount()` for mutations, executed through `runtime.query` / `runtime.execute`. `orm/core-concepts.mdx:106` describes the PostgreSQL raw escape hatch only as fragments spliced into builder queries — evidence: `packages/2-sql/4-lanes/sql-builder/src/types/raw-query.ts:117-170`, `packages/2-sql/4-lanes/sql-builder/src/runtime/raw-lane.ts:24-33`, `packages/3-extensions/postgres/src/runtime/postgres.ts:61, 284`, `docs/releases/v8.0.0-rc.2.md:120` +- `orm/fundamentals/relations-and-joins.mdx:64-74`, `:217` — "The mirror field on the other model (`profile Profile?` on `User`) is not supported in the contract yet", so users are sent to the SQL builder — a singular back-relation is supported; emit only rejects it when the foreign key is not unique (`PSL_NON_UNIQUE_BACKRELATION`), and `userCollection.include('profile')` type-checks as a to-one include — evidence: `packages/2-sql/2-authoring/contract-psl/src/psl-relation-resolution.ts:448`, `docs/releases/v0.16.0.md:23-40`, `packages/3-extensions/sql-orm-client/test/include-cardinality.test-d.ts:36`, `packages/3-extensions/sql-orm-client/src/collection-contract.ts:640` +- `orm/fundamentals/relations-and-joins.mdx:216` — implicit many-to-many (`through` junction) "not supported by `.include(...)` yet" — the ORM resolves `relation.through` into an include descriptor and the demo app includes the N:M `Post.tags` relation with a refinement callback; nested `create`/`connect`/`disconnect` through the junction also work (and `orm/reference/orm-client.mdx:1088-1098` already documents `disconnect` through the junction, contradicting this page) — evidence: `packages/3-extensions/sql-orm-client/src/collection-contract.ts:356-379`, `packages/3-extensions/sql-orm-client/src/mutation-executor.ts:74, 836-926`, `examples/prisma-8-demo/src/orm-client/get-post-tags.ts:17`, `examples/prisma-8-demo/src/orm-client/create-post-with-tags.ts:30`, `docs/releases/v8.0.0-rc.1.md:64` +- `orm/reference/orm-client.mdx:478`, `:513` — text-backed enum columns sort in declaration order under `orderBy` — changed in rc.9: `ORDER BY` and `DISTINCT ON` on text-backed enums follow the stored values; only native PostgreSQL enums keep database ordering — evidence: `docs/releases/v8.0.0-rc.9.md:7` +- `orm/fundamentals/advanced-queries.mdx:104-111` — "PostgreSQL returns counts as strings; convert with `Number(row.posts)`" — `fns.count()` resolves through the target's `count` aggregate descriptor to a `number` (the int8-as-number codec); `countBigInt()` is the `bigint` form. Site sample output `posts: '2'` is stale — evidence: `packages/3-targets/3-targets/postgres/src/core/aggregates.ts:159-172`, `packages/2-sql/4-lanes/sql-builder/src/runtime/functions.ts:242-253`, `docs/releases/v8.0.0-rc.2.md:32-53` +- `orm/reference/orm-client.mdx:384`, `:393`, `:465-467` — `min()`/`max()` "are typed for numeric fields (`NumericFieldNames`)" — the reducer field set is now derived from the aggregate descriptors: `min`/`max` accept numeric and textual columns and the date/time string and temporal codecs. The plain `pg/timestamptz@1` (`DateTime` decoded to `Date`) column is still absent from that list, so the `min('createdAt')` compile error the page describes is right for that codec, but the "numeric-only" explanation is not — evidence: `packages/3-targets/3-targets/postgres/src/core/aggregates.ts:126-152`, `packages/3-extensions/sql-orm-client/src/types.ts:663-677` +- `orm/reference/orm-client.mdx:726` — "`all()` takes no arguments" — `all(configure?: (meta: MetaBuilder<'read'>) => void)` accepts a callback for query annotations (for example `meta.annotate(cacheAnnotation({ ttl }))`); `first`, `aggregate`, and the write terminals take the same optional trailing `configure` argument — evidence: `packages/3-extensions/sql-orm-client/src/collection.ts:1038, 1060-1083, 1235-1245, 1867-1872`, `examples/prisma-8-demo/src/orm-client/get-users-cached.ts:7-21` +- `orm/reference/transactions-and-runtime.mdx:82`, `:128`, `:155`, `:234`, `:310`, `:325` — closed and double-connect failures are plain `Error: Postgres client is closed` / `Error: Mongo client already connected` — they are structured envelopes with codes `DRIVER.NOT_CONNECTED` and `DRIVER.ALREADY_CONNECTED` (plus `why`/`fix`), matchable with `isStructuredError` per the site's own error-reference intro; and `DRIVER.ALREADY_CONNECTED` is raised by PostgreSQL too, not only MongoDB as `:233` implies — evidence: `packages/3-extensions/postgres/src/runtime/postgres.ts:224-228, 290-303`, `packages/3-extensions/mongo/src/runtime/mongo.ts:149, 199-202` +- `orm/reference/transactions-and-runtime.mdx:48` — the PostgreSQL client "exposes `.orm`, `.sql`, `.enums`, `connect()`, `runtime()`, `transaction()`, `prepare()`, and `close()`" — it also exposes `.raw` (the raw lane), `.nativeEnums`, `.context`, `.contract`, and `.stack`; the options table (`:35-42`) omits `verifyMarker: 'onFirstUse' | false` and `binding` — evidence: `packages/3-extensions/postgres/src/runtime/postgres.ts:56-89`, `packages/2-sql/5-runtime/src/runtime-spi.ts:42` +- `orm/reference/transactions-and-runtime.mdx:185-199` — MongoDB options table omits `mode: 'strict' | 'permissive'` and `binding`; the client also exposes `.context` and `.contract` — evidence: `packages/3-extensions/mongo/src/runtime/mongo.ts:28-56, 142` +- `orm/reference/transactions-and-runtime.mdx:359`, `:369` — `tx` exposes `orm`, `sql`, `execute`, `enums` — it also exposes `query` (rows) and `nativeEnums` — evidence: `packages/3-extensions/postgres/src/runtime/postgres.ts:47-54` +- `orm/fundamentals/reading-data.mdx:114`, `:354` — `.first()` "on PostgreSQL ... fetches at most one row", implying MongoDB does not — the MongoDB collection's `first()` also applies `limit: 1` — evidence: `packages/2-mongo-family/5-query-builders/orm/src/collection.ts:341-343` +- `orm/reference/orm-client.mdx:644-651` — `distinctOn()` Remarks omit that it requires the contract capability `postgres.distinctOn` and throws `... requires capability postgres.distinctOn` without it — evidence: `packages/3-extensions/sql-orm-client/src/collection.ts:957`, `packages/3-extensions/sql-orm-client/src/collection-contract.ts:598-604` +- `orm/reference/error-reference.mdx:9-13`, `:23` (and every ADR link in entries) — links and the generator note point at `github.com/prisma/prisma`, while the rc.9 source and its release notes live in `github.com/prisma/orm` (the site's own `orm/index.mdx:110` says so). The 292 code entries otherwise match the rc.9 source word for word — evidence: `docs/releases/v8.0.0-rc.9.md:7-9` (PR links to `github.com/prisma/orm`), `docs/reference/error-reference.md` (identical body after the repo/name substitutions) + +## Missing + +- Lossless aggregates `countBigInt()`, `sumBigInt(field)`, `avgDecimal(field)` (as `aggregate()` selectors, `include` reducers, and SQL-builder `fns`), and the rule that `count()` / integer `sum()` throw `RUNTIME.DECODE_FAILED` outside the safe-integer range instead of rounding — evidence: `packages/3-targets/3-targets/postgres/src/core/aggregates.ts:159-213`, `docs/releases/v8.0.0-rc.2.md:32-53`, `skills/prisma-8/references/queries-postgres.md:233-251` — belongs in `orm/reference/orm-client.mdx` (`aggregate()` and "Refinements, reducers, and combine") and `orm/fundamentals/reading-data.mdx` "Count records" +- `runtime.query(plan)` vs `runtime.execute(plan)` split (rows vs `{ affectedRows }`), and `RuntimeStatementStats` — evidence: `packages/2-sql/4-lanes/relational-core/src/runtime-scope.ts:23-26`, `docs/releases/v8.0.0-rc.2.md:72` — belongs in `orm/reference/transactions-and-runtime.mdx` "Execution options and results" and `orm/fundamentals/advanced-queries.mdx` "Build and run a plan" +- Whole-query raw SQL: `db.raw.sql\`...\`.returnsRow(spec)` / `.affectedCount()`, `RUNTIME.RAW_COLUMN_MISSING`-style spec mismatches — evidence: `packages/2-sql/4-lanes/sql-builder/src/types/raw-query.ts:117-170`, `docs/releases/v8.0.0-rc.2.md:120` — belongs in `orm/fundamentals/advanced-queries.mdx` "Raw SQL fragments" and the `orm/core-concepts.mdx:103-108` table +- `AsyncIterableResult.first()` and `.firstOrThrow()` (throws `RUNTIME.NO_ROWS`) on any read result — evidence: `packages/1-framework/1-core/framework-components/src/execution/async-iterable-result.ts:66-80` — belongs in `orm/reference/orm-client.mdx` "Result types" +- Query annotations: the optional `configure(meta)` callback on every terminal and `meta.annotate(...)`, which is how the built-in cache middleware is opted into per query — evidence: `packages/3-extensions/sql-orm-client/src/collection.ts:1030-1060`, `examples/prisma-8-demo/src/orm-client/get-users-cached.ts` — belongs in `orm/reference/orm-client.mdx` read terminals +- `verifyMarker: 'onFirstUse' | false` on `postgres()`: the startup contract-vs-signature check that `orm/core-concepts.mdx:67` describes without saying how to turn it on or off (it reports `CONTRACT.MARKER_MISSING` / `CONTRACT.MARKER_MISMATCH` as warnings) — evidence: `packages/3-extensions/postgres/src/runtime/postgres.ts:79`, `packages/2-sql/5-runtime/src/runtime-spi.ts:42`, `packages/2-sql/5-runtime/src/sql-runtime.ts:905-925` — belongs in `orm/reference/transactions-and-runtime.mdx` `postgres(options)` +- The flat accessor form `db.orm.User` / `db.sql.user` for single-namespace contracts (the site only ever shows `db.orm.public.User`); the namespace coordinate is required only when a bare name repeats across namespaces — evidence: `packages/3-extensions/sql-orm-client/src/orm.ts:71-121`, `skills/prisma-8/references/queries.md:39-48` — belongs in `orm/reference/orm-client.mdx` "Setting up the client" +- `db.nativeEnums` (PostgreSQL native enum members at runtime) next to `db.enums` — evidence: `packages/3-extensions/postgres/src/runtime/postgres.ts:53, 60`, `docs/releases/v0.15.0.md:75` — belongs in `orm/reference/transactions-and-runtime.mdx` return-type row +- MongoDB `mode: 'strict' | 'permissive'` client option — evidence: `packages/3-extensions/mongo/src/runtime/mongo.ts:44, 142` — belongs in `orm/reference/transactions-and-runtime.mdx` `mongo(options)` +- MongoDB filter classes beyond `MongoFieldFilter`/`MongoOrExpr`: `MongoAndExpr.of([...])`, `MongoNotExpr`, `MongoExistsExpr.exists(field)` / `.notExists(field)` — evidence: `packages/2-mongo-family/4-query/query-ast/src/filter-expressions.ts:68-160`, `packages/2-mongo-family/4-query/query-ast/src/exports/execution.ts:33-37` — belongs in `orm/reference/orm-client.mdx` "Combinators" / "MongoFieldFilter" +- The SQLite facade's lifecycle (`sqlite({ contractJson, path })`, `db.connect({ path })`, `db.transaction`) has no entry on the transactions-and-runtime reference or the transactions fundamentals page (the "SQLite is planned" wording is a known item; this is the missing reference content) — evidence: `packages/9-public/@prisma/orm-sqlite/package.json`, `skills/prisma-8/references/runtime.md:285-296` — belongs in `orm/reference/transactions-and-runtime.mdx` + +## Unverified + +- `orm/reference/orm-client.mdx:334`, `:370`, `:1295` — a looked-up sub-document's `_id` and the upsert update-path `_id` "come back as a raw driver `ObjectId`" — 0.15 changed reshaping reads to decode projected `_id`s and 0.17 made Mongo write results decode through codecs; I did not trace the `$lookup` and `findOneAndUpdate` decode paths far enough to confirm which shape rc.9 returns — checked `docs/releases/v0.15.0.md:103`, `docs/releases/v0.17.0.md:85`, `packages/2-mongo-family/5-query-builders/orm/src/collection.ts:254-320, 535-600` +- `orm/reference/orm-client.mdx:871` — on PostgreSQL, `update()` / `delete()` bypassed without `where()` "narrow to a single row by identity" — the update path does call `#findFirstMatchingRowIdentityWhere()`; I did not read the delete path — checked `packages/3-extensions/sql-orm-client/src/collection.ts:1895-1955` +- `orm/reference/transactions-and-runtime.mdx:724`, `:731` — `scope` documented as a user-settable `RuntimeExecuteOptions` field — the type declares it, but every runtime call site overwrites it (`scope: 'connection'` / `'transaction'`), so a caller-supplied value may have no effect — checked `packages/1-framework/1-core/framework-components/src/execution/runtime-middleware.ts:335-338`, `packages/2-sql/5-runtime/src/sql-runtime.ts:767-880` + +## Skill vs site + +- `and` / `or` / `not` import path — skill (`queries.md:141`, `queries-postgres.md:75, 86, 251`) says the combinators are internal-only and must come from `@internal/sql-orm-client`; site (`orm/fundamentals/reading-data.mdx:167-170`, `orm/reference/orm-client.mdx:1635, 1644`) imports them from `@prisma/orm-postgres/orm-client`. Site matches the source: `packages/9-public/@prisma/orm-postgres/package.json` exports `./orm-client`, and `packages/9-public/@prisma/orm-postgres/test/facade-tarball.test.ts:58, 171` verifies it re-exports `orm`, `Collection`, and the filters +- `.count()` as a collection terminal — skill (`queries-postgres.md:30`) lists `.count()` beside `.all()` / `.first()`; site (`orm/fundamentals/reading-data.mdx:278`) says there is no `.count()` on the chain. Site matches the source: `packages/3-extensions/sql-orm-client/src/collection.ts` has no `count` method; `count()` exists only as an `include` reducer (`:259-300`) and inside `aggregate()` +- SQL-builder `insert` input — skill (`queries-postgres.md:298-299`) writes `db.sql.user.insert({ email })`; site (`orm/fundamentals/advanced-queries.mdx:118-119`) passes an array. Site matches the source: `packages/2-sql/4-lanes/sql-builder/src/runtime/table-proxy-impl.ts:207` (`insert(rows: ReadonlyArray<...>)`) +- Rows from `runtime.execute(plan)` — both the skill (`queries-postgres.md:289, 302, 333, 355`) and the site use `execute` for row reads; the source says `query` (see Breaks copy-paste, first bullet). Neither matches rc.9 +- N:M `.include()` and nested N:M mutations — skill (`queries.md:139-140`) says both are unsupported and that `mutation-executor.ts` throws; site is split (relations page says implicit N:M include is unsupported, orm-client reference shows `disconnect` through the junction). Source supports both (`mutation-executor.ts:836-926`, `examples/prisma-8-demo/src/orm-client/get-post-tags.ts`); the skill's `'N:M nested mutations are not supported yet'` string no longer exists in `packages/3-extensions/sql-orm-client/src/` +- Streaming — skill (`queries.md:146`) says there is no streaming surface; site (`reading-data.mdx:280-309`) says `for await` streams. Source: `AsyncIterableResult` is an async iterable, but the `postgres()` facade disables driver cursors, so the skill is closer to the truth about memory behaviour (`packages/3-extensions/postgres/src/runtime/postgres.ts:245-247`) +- Error codes — skill (`debug.md:12, 78-110`) routes on `PN-CLI-*` / `PN-MIG-*` / `PN-RUN-*` codes; site (`orm/reference/error-reference.mdx`) uses the dotted `NAMESPACE.SUBCODE` scheme. Site matches the source: codes were renamed at 0.16 and `docs/reference/error-reference.md` lists only dotted codes +- Prepared statements — skill (`debug.md:127`) says you "cannot pre-prepare and re-execute a statement by name" and points to TypedSQL (which `queries.md:144` says does not exist); site documents `runtime.prepare` / `db.prepare` (`transactions-and-runtime.mdx:568-663`). Site matches the source (`packages/2-sql/5-runtime/src/sql-runtime.ts:110, 549`), apart from the `query` vs `execute` method name above +- `.first()` on MongoDB — skill (`queries-mongo.md:63`) and site (`orm-client.mdx:775`) agree there is no filter argument; source agrees (`packages/2-mongo-family/5-query-builders/orm/src/collection.ts:110, 341`) diff --git a/docs/orm-docs-audit/audit/start-guides.md b/docs/orm-docs-audit/audit/start-guides.md new file mode 100644 index 0000000000..9732217a85 --- /dev/null +++ b/docs/orm-docs-audit/audit/start-guides.md @@ -0,0 +1,60 @@ +# Audit: start-guides + +Pages read: `(index)/index.mdx`, `(index)/getting-started.mdx`, `(index)/prisma-orm/index.mdx`, `(index)/prisma-orm/create-prisma.mdx`, `(index)/prisma-orm/quickstart/postgresql.mdx`, `(index)/prisma-orm/quickstart/mongodb.mdx`, `(index)/prisma-orm/add-to-existing-project/postgresql.mdx`, `(index)/prisma-orm/add-to-existing-project/mongodb.mdx`, `(index)/prisma-postgres/quickstart/prisma-orm.mdx`, `guides/index.mdx`, `guides/upgrade-prisma-orm/postgresql.mdx`, `guides/upgrade-prisma-orm/mongodb.mdx`, `guides/switch-to-prisma-orm/from-drizzle.mdx`, `guides/switch-to-prisma-orm/from-mongoose.mdx`, `guides/switch-to-prisma-orm/from-sql-orms.mdx`, `guides/database/schema-changes.mdx`, `guides/database/data-migration.mdx`, `guides/database/multiple-databases.mdx`. + +Monorepo paths below are relative to `wip/prisma-src/` unless they start with `wip/create-prisma-src/`. + +## Breaks copy-paste + +- `(index)/prisma-orm/add-to-existing-project/postgresql.mdx:46` (same on `add-to-existing-project/mongodb.mdx:48`) — tells the user to "keep the default schema path, `prisma/contract.prisma`", and every later step assumes that directory: `contract infer --output ./prisma/contract.prisma` (line 63), `prisma/contract.json` and `prisma/contract.d.ts` (line 80), `import { db } from "./prisma/db"` (lines 103, 137; mongodb page lines 109, 138) — `orm init` defaults to `src/prisma/contract.prisma` and writes `db.ts` next to it, so a user who accepts the default gets `src/prisma/`, `contract infer --output ./prisma/contract.prisma` writes a file `prisma.config.ts` never reads, `contract emit` emits the untouched starter contract, and `./prisma/db` does not exist. The site's own agent prompt at `(index)/index.mdx:96` says `src/prisma/` — evidence: `packages/1-framework/1-core/config/src/config-types.ts:45` (`DEFAULT_CONTRACT_SOURCE_DIR = 'src/prisma'`), `packages/1-framework/3-tooling/cli/src/commands/init/templates/code-templates.ts:63-67`, `packages/1-framework/3-tooling/cli/src/orm/init-scaffold.ts:188` +- `(index)/prisma-orm/create-prisma.mdx:94` — "plan and apply the migration with `npx prisma@latest migration plan` and `npx prisma@latest migrate`" — there is no top-level `migrate` command; the apply command is `db migrate`, and the generated `migrate` script runs `prisma db migrate` — evidence: `packages/1-framework/3-tooling/cli/src/orm/cli.ts:97-121` (command tree), `wip/create-prisma-src/src/tasks/install.ts:53` +- `(index)/prisma-orm/add-to-existing-project/postgresql.mdx:148` — `const rows = await db.runtime().execute(plan); console.log(rows);` for a `select` plan — `execute` returns `SqlStatementStats` (`{ affectedRows }`), not rows; rows come from `db.runtime().query(plan)` — evidence: `packages/2-sql/4-lanes/relational-core/src/runtime-scope.ts:23-26`, `packages/2-sql/4-lanes/relational-core/src/ast/driver-types.ts:24-26`, `examples/prisma-8-demo/src/queries/get-users.ts:5` +- `(index)/prisma-orm/add-to-existing-project/mongodb.mdx:148` — `const rows = await runtime.execute(plan)` for a `from("users").match(...).project(...)` pipeline — `MongoRuntime.execute` returns statement statistics and throws `RUNTIME.MONGO_STATISTICS_UNSUPPORTED` for a command with no count field (an aggregate/find); reads go through `runtime.query(plan)` — evidence: `packages/2-mongo-family/7-runtime/src/mongo-runtime.ts:98-100, 282-289`, `examples/mongo-demo/src/server.ts:75` +- `guides/upgrade-prisma-orm/mongodb.mdx:266` (repeated at 293, 345, 392, 393) — `await db.execute(plan).toArray()` — the Mongo client has no `execute` method; the surface is `orm`, `query`, `raw`, `connect`, `runtime`, `close`. A plan runs as `(await db.runtime()).query(plan).toArray()` — evidence: `packages/3-extensions/mongo/src/runtime/mongo.ts:28-41`, `examples/mongo-demo/scripts/cache-demo.ts:77` + +## Misleading + +- `(index)/prisma-orm/add-to-existing-project/postgresql.mdx:41` (same on `add-to-existing-project/mongodb.mdx:43`) — "`orm init` also adds `prisma-next.md` and project-level Prisma 8 skills for Cursor, Claude Code, Codex, and Windsurf" — `orm init` writes `prisma-next.md` but installs no skills; its only skill work is deleting retired skill directories, and its own next-steps output says to run `prisma init` for skills — evidence: `packages/1-framework/3-tooling/cli/src/commands/init/skill-sources.ts:1-5`, `packages/1-framework/3-tooling/cli/src/commands/init/output.ts:106-108` +- `(index)/prisma-orm/add-to-existing-project/postgresql.mdx:43-46, 50` (same on `mongodb.mdx:45-48, 52`) — lists the prompts as "choose PSL" and "keep the default schema path", then says "Update `.env`" — `orm init` also asks "Also write a .env file from .env.example?" (default No); by default it writes `.env.example` only, so there may be no `.env` to update. `--write-env` skips the prompt — evidence: `packages/1-framework/3-tooling/cli/src/orm/init-inputs.ts:229-233`, `packages/1-framework/3-tooling/cli/src/orm/init-scaffold.ts:199, 211-218` +- `(index)/prisma-orm/create-prisma.mdx:86` — "Review `DATABASE_URL` in `.env`, then initialize the database" — for PostgreSQL the scaffold writes no `.env` (it only does for MongoDB), calls `orm init` without `--write-env`, and the generated `prisma.config.ts` has no `dotenv` import, so `DATABASE_URL` must be in the shell environment. `quickstart/postgresql.mdx:43` says this correctly — evidence: `wip/create-prisma-src/src/tasks/prisma-setup/project-files.ts:17-29`, `wip/create-prisma-src/src/tasks/prisma-setup/commands.ts:47-59`, `wip/create-prisma-src/templates/create/_shared/prisma.config.ts.hbs:18-37`, `wip/create-prisma-src/templates/create/_shared/README.md.hbs:71` +- `(index)/prisma-orm/quickstart/mongodb.mdx:27` and `add-to-existing-project/mongodb.mdx:19` — "Prisma 8 needs a MongoDB replica set" — nothing in the Mongo runtime or migration runner opens a session or transaction (the client has no `transaction` method and the runner deliberately avoids transactions); `orm init`'s `.env.example` says a standalone `mongod` works for first-run queries and a replica set is only needed for transactions and change streams. Only the scaffold's connection string (`replicaSet=rs0&directConnection=true`) demands one — evidence: `packages/1-framework/3-tooling/cli/src/commands/init/templates/env.ts:43-48`, `packages/3-mongo-target/1-mongo-target/src/core/control-target.ts:84-86`, `packages/3-extensions/mongo/src/runtime/mongo.ts:28-41`, `wip/create-prisma-src/src/tasks/prisma-setup/project-files.ts:25` +- `(index)/prisma-orm/create-prisma.mdx:9` (and every quickstart that says "installs Prisma 8") — create-prisma 0.11.7 pins `@prisma/orm-postgres` and `@prisma/orm-mongo` to `8.0.0-rc.8` while the current release is rc.9, so a fresh scaffold does not get rc.9 behaviour the site documents (for example the rc.9 Mongo attribute rejection and index argument format) — evidence: `wip/create-prisma-src/src/constants/dependencies.ts:8-10`, `packages/9-public/@prisma/orm-postgres/package.json` (version `8.0.0-rc.9`), `docs/releases/v8.0.0-rc.9.md:9-25` +- `(index)/prisma-orm/create-prisma.mdx:57` — `--force` "Allows scaffolding into a non-empty directory" — it also overwrites the generated Prisma config, contract, and `db.ts`, and refuses when the target has a non-empty `migrations/` directory — evidence: `wip/create-prisma-src/src/index.ts:67-70`, `wip/create-prisma-src/src/commands/create-context.ts:181-192` +- `(index)/prisma-orm/create-prisma.mdx:35, 54, 56` — describes the deploy prompt and `--yes` without saying the defaults differ: interactively the prompt defaults to Yes, `--yes` answers No, and `--json` (not on the page) deploys unless `--no-deploy` is passed — evidence: `wip/create-prisma-src/src/tasks/prisma-setup/context.ts:112-121, 163-166`, `wip/create-prisma-src/src/index.ts:73-76` +- `(index)/prisma-orm/create-prisma.mdx:79` — the Deno command omits `--minimum-dependency-age=0`; create-prisma's README says Deno 2.9 blocks packages published in the last 24 hours, so a fresh release resolves to an older cached version without it — evidence: `wip/create-prisma-src/README.md:46-52` +- `(index)/getting-started.mdx:36`, `(index)/index.mdx:111`, `(index)/prisma-orm/index.mdx:43` — "Valid --template values: next, hono, nuxt, astro, nest, svelte, tanstack-start, elysia" omits `minimal`, which is also the default — evidence: `wip/create-prisma-src/src/types.ts:7-17`, `wip/create-prisma-src/src/commands/create-context.ts:13` +- `guides/upgrade-prisma-orm/postgresql.mdx:470-471, 510` — "Without a ref, the next `migration plan` starts from scratch and plans `CREATE TABLE` operations all over again" / "If you see `CREATE TABLE` operations instead, the `db` ref from step 4.3 is missing" — since rc.8 `migration plan` refuses to plan from an empty database when migrations already exist on disk and raises a structured error instead, so the symptom the guide tells the user to look for no longer appears — evidence: `docs/releases/v8.0.0-rc.8.md:17` +- `guides/upgrade-prisma-orm/postgresql.mdx:455` — expected output "`Database signed (marker created)`" — the command prints `Database signed` — evidence: `packages/1-framework/3-tooling/cli/src/orm/db/sign.ts:68` +- `guides/upgrade-prisma-orm/mongodb.mdx:27` — "validated against `@prisma/orm-mongo@0.16.0`" — the current package is `8.0.0-rc.9`, and breaking changes since 0.16.0 affect this guide's material (rc.7 pagination rename, rc.9 Mongo attribute rejection and index argument format) — evidence: `packages/9-public/@prisma/orm-mongo/package.json`, `docs/releases/v8.0.0-rc.7.md:9`, `docs/releases/v8.0.0-rc.9.md:9-25` +- `guides/index.mdx:35-39, 43` — lists "Upgrading: moving from Prisma 7" and "Databases" under "Coming as they land" and never links the upgrade, database, or switch guides, while `guides/upgrade-prisma-orm/postgresql.mdx` exists and is in the nav — evidence (site only): `guides/meta.json`, `guides/upgrade-prisma-orm/meta.json` +- `guides/switch-to-prisma-orm/from-drizzle.mdx`, `from-sql-orms.mdx`, `from-mongoose.mdx`, `guides/database/schema-changes.mdx`, `data-migration.mdx`, `multiple-databases.mdx` — all six sit in the Prisma 8 guides tree but are Prisma 7 / v6 guides: they install `prisma@7.10.0` or `prisma@6.19` and `@prisma/client` (`from-drizzle.mdx:52-53`, `from-sql-orms.mdx:40-41`, `from-mongoose.mdx:57-61`, `data-migration.mdx:73-77`, `multiple-databases.mdx:59-63`) and run `prisma init`, `db pull`, `migrate diff`, `migrate resolve`, `migrate dev`, `migrate deploy`, `generate`, `studio` (`from-drizzle.mdx:69, 169, 201, 209, 246`; `from-sql-orms.mdx:49, 61, 67-69, 75`; `from-mongoose.mdx:77, 170, 307`; `schema-changes.mdx:88, 155`; `data-migration.mdx:121, 243`; `multiple-databases.mdx:79, 233-237`). None of those commands exist in the Prisma ORM 8 CLI, whose tree is `orm init`, `contract *`, `db *`, `migration *`. A user coming from the Prisma 8 guides index is sent to a different product — evidence: `packages/1-framework/3-tooling/cli/src/orm/cli.ts:97-121`, `guides/meta.json` +- `guides/switch-to-prisma-orm/from-mongoose.mdx:14-20, 64-70` — "MongoDB support for Prisma ORM v7 is coming in the near future... use Prisma ORM v6.19" — Prisma ORM 8 ships MongoDB support as `@prisma/orm-mongo`, and the site's own MongoDB quickstart uses it — evidence: `packages/9-public/@prisma/orm-mongo/package.json`, `packages/3-extensions/mongo/src/runtime/mongo.ts:110-118` + +## Missing + +- Temporal requirement for inferred contracts — `contract infer` maps `timestamp`/`timestamptz` columns to `Timestamp`/`Timestamptz`, which are Temporal-backed; on a runtime without a global `Temporal` (Node.js 25 and earlier ship none; 26 does) every read of such a column fails with `RUNTIME.TEMPORAL_UNAVAILABLE` unless `temporal-polyfill/full/global` is imported first or the type is changed to `TimestamptzString`. create-prisma installs the polyfill for this reason; `orm init` does not. Neither existing-project page nor the Prisma 7 upgrade guide (whose real-world schemas have `DateTime` columns) mentions it — evidence: `packages/3-targets/3-targets/postgres/src/core/psl-infer/postgres-type-map.ts:30-33`, `packages/3-targets/3-targets/postgres/src/core/errors.ts:74-86`, `skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.5-to-8.0.0-rc.6/instructions.md:38-47`, `wip/create-prisma-src/src/constants/dependencies.ts:21-23`, `wip/create-prisma-src/templates/create/_shared/src/prisma/db.ts.hbs:15` — belongs on `add-to-existing-project/postgresql.mdx` step 4 and `guides/upgrade-prisma-orm/postgresql.mdx` section 2.4 +- `create-prisma --json` — non-interactive machine-readable mode for agents; not in the flag table — evidence: `wip/create-prisma-src/src/index.ts:73-76`, `wip/create-prisma-src/README.md:66-81` — belongs in the `create-prisma.mdx` flag table and the agent prompts on `getting-started.mdx` / `index.mdx` +- The full set of generated package scripts — the scaffold adds `db:update`, `db:verify`, `migration:status`, `migration:show`, `skills:sync`, `composer:dev`, `composer:deploy`, and `deploy` alongside the four the site mentions — evidence: `wip/create-prisma-src/src/tasks/install.ts:47-70` — belongs in `create-prisma.mdx` "Start the app" +- `orm init` flags — `--schema-path`, `--write-env`, `--probe-db`, `--strict-probe`, `--skip-install`, `--keep-previous-facade`, plus `--confirm ` for re-running init in an already scaffolded directory — evidence: `packages/1-framework/3-tooling/cli/src/orm/init.ts:87-112`, `packages/1-framework/3-tooling/cli/src/orm/init-inputs.ts:218-227` — `--write-env` and `--schema-path` belong on both add-to-existing-project pages, which currently rely on the defaults they describe wrongly +- Minimum database server versions — PostgreSQL 15 and MongoDB 8.0, written into `.env.example` by `orm init` — evidence: `packages/1-framework/3-tooling/cli/src/commands/init/templates/env.ts:16-19` — belongs on the PostgreSQL quickstart and existing-project pages (only the MongoDB upgrade guide states the Mongo minimum) +- MongoDB projects and Composer — Composer does not provision MongoDB; `dev:composer` and `deploy` need `MONGODB_URL` set as the secret bound in `module.ts` — evidence: `wip/create-prisma-src/templates/create/_shared/README.md.hbs:55-57`, `wip/create-prisma-src/templates/create/_shared/module.ts.hbs:24-27`, `wip/create-prisma-src/src/tasks/prisma-setup/presentation.ts:44-49` — belongs on `quickstart/mongodb.mdx`, which never mentions the Composer scripts, and on `prisma-orm/index.mdx` step 6 of the agent prompt, which assumes PostgreSQL provisioning +- A Prisma ORM 8 path for switching from another ORM — the shipped skill describes the brownfield flow (`orm init`, `contract infer --output`, review, `contract emit`, `db sign`, `db verify`) that replaces the Prisma 7 `db pull` / baseline guides — evidence: `skills/prisma-8/references/quickstart.md:229-263` — belongs in `guides/switch-to-prisma-orm/*`, which are Prisma 7 only +- Prisma ORM 8 team and data-migration workflow (`migration plan`, refs, `db update` for local iteration, `db verify`) — evidence: `skills/prisma-8/references/migrations.md`, `packages/1-framework/3-tooling/cli/src/orm/db/update.ts` — belongs in `guides/database/*`, which are Prisma 7 only +- `db init --dry-run` / `db update --dry-run` to preview operations before touching a database — evidence: `packages/1-framework/3-tooling/cli/src/orm/db/init.ts` and `db/update.ts` flag specs — useful on the quickstart step that runs `db:init` + +## Unverified + +- `(index)/getting-started.mdx:41`, `(index)/index.mdx:115`, `(index)/prisma-orm/index.mdx:48` — `npx prisma@latest db migrate --yes` — `db migrate` declares no `--yes` flag (`packages/1-framework/3-tooling/cli/src/orm/migrate.ts` flags: `db`, `to`, `advance-ref`, `show`, `from`); a global `--yes` would live in `@prisma/cli-engine`, which is not in the monorepo. create-prisma does pass `--yes` to `orm init` (`wip/create-prisma-src/src/tasks/prisma-setup/commands.ts:50`), so it exists at least there +- `(index)/index.mdx:96`, `(index)/getting-started.mdx:71` — "`orm init` may end with `CLI.INIT_SKILL_INSTALL_FAILED`, which `skills sync` repairs" — no such code exists in the monorepo and `orm init` installs no skills (`skill-sources.ts:1-5`); `skills sync` and `prisma init` are unified-CLI commands outside the monorepo +- `guides/upgrade-prisma-orm/postgresql.mdx:17, 161, 237-238` — `@prisma/prisma7@7.10.0-dev.58` and `definePrismaConfig` from `prisma/config` — both come from the prisma-cli repo; the monorepo's own `orm init` scaffold imports `definePrismaConfig` from `@prisma/cli-engine` (`code-templates.ts:304`) while create-prisma's template imports it from `prisma/config` (`prisma.config.ts.hbs:18`) +- `guides/upgrade-prisma-orm/mongodb.mdx:13` — the `prisma-mongodb-upgrade` skill and `npx skills add prisma/skills` — external repository, not checked +- `guides/upgrade-prisma-orm/mongodb.mdx:280` — "the typed builder sends the id through as a plain string, so the stage silently matches nothing" for `.match((f) => f.authorId.eq(authorId))` — did not trace ObjectId encoding in the pipeline builder's `match` path +- `(index)/prisma-orm/quickstart/postgresql.mdx:59` and `quickstart/mongodb.mdx:64` — the quoted output lines for `db init` and `db migrate` — did not locate the exact strings + +## Skill vs site + +- `skills/prisma-8/references/quickstart.md:73, 189, 201` says `orm init` scaffolds `prisma/contract.prisma` by default (TML-2532) and the site's existing-project pages (`add-to-existing-project/postgresql.mdx:46`) say the same; the source defaults to `src/prisma/contract.prisma` (`config-types.ts:45`, `code-templates.ts:63-67`). Source disagrees with both; the skill's note is stale +- `skills/prisma-8/references/queries-postgres.md:289, 302, 333` runs SQL-builder reads with `const rows = await db.runtime().execute(plan)`, and `queries-mongo.md:12, 140, 164, 178` runs pipeline reads with `runtime.execute(plan)`; the site's existing-project pages do the same. Source: `execute` returns statistics on both families and the Mongo runtime throws for a find/aggregate (`runtime-scope.ts:25`, `mongo-runtime.ts:99, 282-289`); the examples use `runtime.query(plan)`. Both skill and site are wrong +- `skills/prisma-8/references/contract.md:221` says `@@discriminator` / `@@base` are SQL-only in PSL; the site's MongoDB upgrade guide (`guides/upgrade-prisma-orm/mongodb.mdx:129, 164, 171`) uses them on MongoDB. The Mongo PSL interpreter implements both (`packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts:276-418`), so the site matches the source +- `skills/prisma-8/references/quickstart.md:196` documents an `orm init --skip-skills` flag; the site does not mention it and `orm init` declares no such flag (`packages/1-framework/3-tooling/cli/src/orm/init.ts:87-112`). Site matches the source +- `skills/prisma-8/references/quickstart.md:37` / mongo note at line 99 and the site's MongoDB pages agree on storage-name addressing (`db.orm.users`), and the scaffold's seed confirms it (`wip/create-prisma-src/templates/create/_shared/src/prisma/seed.ts.hbs:16`); no disagreement, listed for completeness diff --git a/docs/orm-docs-audit/brief-db-ref-on-adoption.md b/docs/orm-docs-audit/brief-db-ref-on-adoption.md new file mode 100644 index 0000000000..a0710d1aed --- /dev/null +++ b/docs/orm-docs-audit/brief-db-ref-on-adoption.md @@ -0,0 +1,53 @@ +# Brief: set the `db` ref when a database is adopted + +Written 2026-09-10 for an agent with no prior context. Repo: `prisma/orm` `main` (the Prisma ORM 8 monorepo; formerly `prisma/prisma`, and the old name still redirects). Verified against rc.9. + +## What we want + +After adopting a database, the migration system knows the baseline. Concretely: `db sign` on a database with no prior signature should leave the project in a state where the next `migration plan` chains from the signed contract, with no extra commands. + +The shape we have in mind: `db sign` records the signed contract hash as the baseline state and sets the `db` ref to it, writing the contract snapshot so the ref resolves. Whether that also needs a baseline migration package on disk, or whether a snapshot alone is enough for `migration plan` to chain from and for `migration status` to report cleanly, is for the implementer to determine from how the planner and status resolve origins. + +Open questions to settle in the design, not to guess at: + +1. Should `db sign` on an already-signed database (re-signing under a newer hash) also move the ref? Probably yes, for the same reason, but it changes an existing behaviour. +2. Does `db sign --db ` follow the same rule as `db init --db ` and skip the ref? The existing rule is that an explicit `--db` means "not my dev database, don't move my ref". Adoption is usually done against the real database, so the rule may be wrong for sign. Decide and document. +3. If the ref already exists and points elsewhere, refuse, overwrite, or require `--advance-ref`. +4. `migration plan` with no `--from`, no ref, and no migrations on disk currently plans silently from empty. Consider warning or refusing the same way it does when migrations exist, since after this change the silent case means "you never ran init, update, or sign". + +## Acceptance + +- A test in `test/integration/` that: creates tables directly, runs `contract infer`, `contract emit`, `db sign`, changes the contract, emits, runs `migration plan`, and asserts the plan contains only the delta. +- The same flow with `--db ` on `db sign`, asserting whichever behaviour the design settles on. +- `migration status` after `db sign` reports current and target hashes equal and nothing pending. +- Update the shipped skill: `skills/prisma-8/references/migration-model.md` ("The trap" section and the origin rules) and `references/quickstart.md` (the brownfield adoption path). +- Update the monorepo docs that describe the ref rules: `docs/glossary.md` under "Ref", and any release note for the version this ships in (`docs/releases/`). +- Record upgrade instructions if the behaviour change is user-visible (the `record-upgrade-instructions` skill). + +The docs site (`prisma/web`) changes are handled separately; note in the PR what the new user-facing sequence is so the docs page can be written from it. + +## The problem + +A user with an existing database brings it under Prisma ORM 8 by running `contract infer`, `contract emit`, and `db sign`. That is what the getting-started page for existing projects tells them to do. Then they change the contract and run `migration plan`. The plan proposes creating every table again. + +Cause: `migration plan` takes its origin from `--from`, else the `db` ref, else the empty contract. `db sign` writes the database marker but never sets the `db` ref, and there are no migrations on disk, so the plan resolves to the empty origin and the CLI does not refuse (the `MIGRATION.PLAN_ORIGIN_UNKNOWN` refusal only fires when migrations already exist on disk). Once the graph has a snapshot the `db` ref can point at, `migration plan` writes an automatic baseline package; the gap is only that nothing after `db sign` creates that snapshot or the ref. + +Two Discord users hit this in one week and asked what `--from` is for. The incremental upgrade guide avoids it by teaching three extra commands (`migration plan --name baseline`, `db sign`, `migration ref set db `), and the "add to existing project" page does not teach them at all. + +## What exists today + +- `db init` and `db update` advance the `db` ref by default. `computeRefAdvancementName` in `packages/1-framework/3-tooling/cli/src/control-api/operations/ref-advancement.ts` returns `db` when no `--db` flag is given and `null` when `--db ` is passed, so passing `--db` also skips the ref. That second rule is a separate trap the shipped skill documents under "The trap" in `skills/prisma-8/references/migration-model.md`. +- `db migrate --advance-ref db` advances the ref after applying. +- `db sign` (`packages/1-framework/3-tooling/cli/src/orm/db/sign.ts`) verifies the live schema against the emitted contract and writes the marker. It does not touch refs. +- `migration ref set ` sets a ref by hand. +- `executeRefAdvancement` writes a contract snapshot under `migrations/snapshots/` and then the ref file. A ref must point at a hash that has a snapshot. + +## Pointers + +- Ref advancement: `packages/1-framework/3-tooling/cli/src/control-api/operations/ref-advancement.ts` +- Sign command: `packages/1-framework/3-tooling/cli/src/orm/db/sign.ts` +- Plan command and origin flag: `packages/1-framework/3-tooling/cli/src/orm/migration/plan.ts`, `control-api/operations/migration-plan.ts` +- Refusal text: `PLAN_ORIGIN_UNKNOWN` in `packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts` +- Existing ref tests: `test/integration/test/cli.db-ref-advancement.e2e.test.ts` +- The documented workaround: `prisma/web` `apps/docs/content/docs/guides/upgrade-prisma-orm/postgresql.mdx`, sections 4.1 to 4.3 +- Migration graph ADRs: search `docs/architecture docs/adrs/` for "ref" diff --git a/docs/orm-docs-audit/brief-intermediate-contracts.md b/docs/orm-docs-audit/brief-intermediate-contracts.md new file mode 100644 index 0000000000..50920cebaa --- /dev/null +++ b/docs/orm-docs-audit/brief-intermediate-contracts.md @@ -0,0 +1,47 @@ +# Docs update for intermediate contracts in data transform migrations + +Recorded 2026-09-13 from a brief written on another branch. Target page: `apps/docs/content/docs/orm/migrations/editing-a-migration.mdx`, next to `dataTransform`. Change item C25 in `changes.md`. + +# For the docs site: data transforms that need an intermediate contract + +Audience: an agent writing or extending the Prisma 8 migrations documentation. This describes a technique that exists in the ORM today and is specified in ADR 197 ("Migration packages snapshot their own contract", section "Intermediate contracts") in `prisma/orm`. It should be documented next to `dataTransform`, because it answers the first question anyone hits when a migration both changes a column and has to move data through it. + +## The problem the reader has + +A migration in Prisma 8 is an edge between two contracts: the start contract and the end contract. Its `dataTransform` step runs typed ORM queries, and those queries are typed and planned against a contract the author hands in. The natural choice is the end contract, and for a pure backfill it is right: add a nullable column, fill it, the end contract has the column. + +It stops working the moment the transform has to read something the migration removes. Example: replacing a boolean `needsReply` with an enum column `state`. The transform must read `needsReply` to decide each row's `state`, but the end contract no longer has `needsReply`, so a query against it cannot name that column; the planner rejects it (`PN-MIG-2005`, a reference to something not in the contract). The start contract has `needsReply` but not `state`, so a query against it cannot write the new column either. Neither contract describes the database at the moment the transform runs. + +Authors who hit this reach for raw SQL inside the migration, which throws away the typing the contract-first design exists to give them. + +## The technique + +Describe the in-between state as a contract of its own, inside the migration directory, and type the transform against it. + +1. Copy the schema authoring file (the `.prisma` file, or the TypeScript builder) into the migration directory as `intermediate.prisma`. +2. Edit it to the shape the database has at the moment the transform runs: in the example, `ConversationReplyState` carries both `needsReply Boolean` and `state reply_state?` (nullable, because it is not yet filled). +3. Emit it: `prisma contract emit` pointed at that file, producing `intermediate.json` and `intermediate.d.ts` next to it. These are snapshots; they never change after the migration is written, which is the same rule ADR 197 applies to the migration's own `contract.json`. +4. In `migration.ts`, import both the end contract snapshot and the intermediate one, and order the operations as: schema steps that reach the intermediate state (add the nullable `state` column), then `this.dataTransform(intermediateContract, 'fill reply state', { run: () => })`, then the schema steps that reach the end state (set `state` not null, drop `needsReply`, drop anything else the migration removes). + +The transform's queries are now typed: `needsReply` and `state` both exist in the contract they are built from, the plan's storage hash matches the contract the transform was handed (the runner checks this and refuses a mismatch with `MIGRATION.DATA_TRANSFORM_CONTRACT_MISMATCH`), and nothing is raw text. + +Multiple intermediate contracts are allowed; the author names and emits each one. Every migration directory is self-contained: it never reaches outside itself for type information, so it keeps type-checking however the project's schema evolves afterwards. + +## A trap the page must warn about + +A column declared with `temporal.updatedAtString()` makes the query builder set it on every update, and it does so by putting the current time into the plan as a parameter. A migration's transform is lowered into `ops.json` when the migration is planned, not when it is applied, so that timestamp is baked into `ops.json`, and every re-emit of the migration produces different bytes while `migration.json` keeps the same hash. `migration status` then reports `MIGRATION.CONTRACT_SPACE_VIOLATION` and the author has no idea why. In the intermediate contract, declare such columns as a plain timestamp type (`TimestamptzString`) rather than the auto-updating form; the intermediate contract only types the transform, so the end contract's `updatedAt` behaviour is untouched. Found while writing the first such migration in `prisma/asks` (2026-09-12). + +## What the docs page should show + +- A worked example with three files: `intermediate.prisma` (the relevant model only), `migration.ts` with the imports and the operation order, and the directory listing after `contract emit`. +- The rule of thumb: type a transform against the end contract when the transform only writes columns the end contract has and reads columns it keeps; use an intermediate contract when the transform reads anything the migration removes or renames, or writes a column before its final constraints are applied. +- The failure the reader sees without it (`PN-MIG-2005` against the end contract) and why raw SQL is the wrong fix. +- The alternative of two migrations (add and fill in the first, drop in the second), when a team prefers not to keep an intermediate snapshot, with its cost: an extra contract version that exists only to make the transform expressible. + +## References for the writer + +- `prisma/orm`, `docs/architecture docs/adrs/ADR 197 - Migration packages snapshot their own contract.md`, section "Intermediate contracts" (the directory layout and the import pattern are there, written for Mongo; the Postgres form is identical apart from the builder). +- `prisma/orm`, `packages/3-targets/3-targets/postgres/src/core/migrations/operations/data-transform.ts`, the Postgres `dataTransform(contract, name, { run, check })` signature and its contract-mismatch check. +- `prisma/orm`, `examples/retail-store/migrations/app/20260513T0508_backfill_product_status/migration.ts`, a backfill transform (Mongo) showing the operation order and the snapshot imports. +- ADR 028 ("Migration Structure & Operations") for migrations as edges between contract hashes, and ADR 192 for why no TypeScript runs at apply time: the intermediate contract matters at plan time only; the runner executes the lowered `ops.json`. +- Status note from ADR 197: the scaffolder's automatic snapshot copy is target state; the intermediate-contract workflow itself is done by hand with `contract emit` and works with the current CLI. Verify against the release the page documents. diff --git a/docs/orm-docs-audit/brief-skill-staleness.md b/docs/orm-docs-audit/brief-skill-staleness.md new file mode 100644 index 0000000000..52641b656e --- /dev/null +++ b/docs/orm-docs-audit/brief-skill-staleness.md @@ -0,0 +1,42 @@ +# Brief: bring the `prisma-8` agent skill up to rc.9 + +Written 2026-09-11 for an ORM-focused agent with no prior context. Repo: `prisma/orm` (the Prisma ORM 8 monorepo). The skill is `skills/prisma-8/`: `SKILL.md` plus `references/*.md` and `upgrading/`. It ships inside the `@prisma/orm-postgres`, `@prisma/orm-sqlite`, and `@prisma/orm-mongo` tarballs (each package's `files` includes `skills`, and `prepack` runs `sync-package-skills.ts`), and `prisma init` / `prisma skills sync` copy it into user repos for Claude Code, Cursor, Codex, and Windsurf. It is what coding agents read instead of the docs site, so every stale line below is an agent writing broken code into a user's project. + +## What we want + +Every claim, sample, import path, command, flag, and error code in `skills/prisma-8/references/*.md` true on the current release, verified against `packages/`, `docs/releases/`, and `examples/`, with the specific defects below fixed first. Do not rewrite the skill's structure or voice; fix facts. Where the skill deliberately uses `@internal/*` import paths, leave them: that is a known convention users read as `@prisma/orm-postgres/*`. + +## Verified defects, most damaging first + +Line numbers are from the rc.9 tag. "Source" is the file that proves the correct form. + +1. **Row reads through `execute()`.** `queries-postgres.md:255-300` (and 289, 302, 333, 355), `queries.md:144, 149`, `queries-mongo.md:12, 140, 164, 178` read rows with `db.runtime().execute(plan)` or `(await db.runtime()).execute(plan)`. Since rc.2, `execute()` resolves `{ affectedRows }` and rows come from `query()`, which returns an `AsyncIterableResult`. On Mongo, `execute()` on a find or aggregate throws `RUNTIME.MONGO_STATISTICS_UNSUPPORTED`. Only non-returning writes stay on `execute`. Source: `packages/2-sql/5-runtime/src/sql-runtime.ts:309-322`, `packages/2-mongo-family/7-runtime/src/mongo-runtime.ts:98-100,282-289`, `docs/releases/v8.0.0-rc.2.md`, `examples/prisma-8-demo/src/queries/*.ts`, `examples/mongo-demo/test/query-builder-writes.test.ts`. +2. **`PN-*` error codes.** `debug.md:12, 78-110, 306-338`, `contract.md:28, 62-66`, `migrations.md:21, 67-75` route on `PN-CLI-4001…4020`, `PN-MIG-2001…2005`, `PN-RUN-3001…3030`, `PN-SCHEMA-0001`. No such string exists in `packages/`. Every published error is a dotted structured code since 0.17 (`MIGRATION.RUNNER_FAILED`, `CONTRACT.MARKER_MISSING`, `CONTRACT.MARKER_MISMATCH`, `MIGRATION.UNFILLED_PLACEHOLDER`, `MIGRATION.DATA_TRANSFORM_CONTRACT_MISMATCH`, and so on). Source: `docs/releases/v0.17.0.md:27`, `docs/reference/error-reference.md`, `packages/1-framework/1-core/errors/src/`. +3. **Flat config file.** `contract.md:47, 164-170` shows `defineConfig({ contract, extensions })` from `@internal/postgres/config` as the whole `prisma.config.ts`. The envelope form `definePrismaConfig({ orm: ormConfig({ contract, db: { connection }, extensions }) })` replaced it in rc.4 and the flat fallback is gone. Source: `docs/releases/v8.0.0-rc.4.md`, `examples/prisma-8-demo/prisma.config.ts`. +4. **`orm init` facts.** `quickstart.md:73, 189, 201` says the default schema path is `prisma/contract.prisma` (it is `src/prisma/contract.prisma`, with `db.ts` beside it); `quickstart.md:196` documents `orm init --skip-skills` (removed in rc.6; `orm init` no longer touches skills at all). Source: `packages/1-framework/1-core/config/src/config-types.ts:45`, `packages/1-framework/3-tooling/cli/src/orm/init.ts:87-112`, `cli/src/commands/init/skill-sources.ts:1-5`, `docs/releases/v8.0.0-rc.6.md:29`. +5. **Many-to-many.** `queries.md:139-140` says N:M `.include()` and nested N:M mutations are unsupported and that `mutation-executor.ts` throws. Both work through the junction: `include('tags', …)`, nested `create` / `connect` / `disconnect`. The quoted throw string no longer exists. Source: `packages/3-extensions/sql-orm-client/src/mutation-executor.ts:836-926`, `collection-contract.ts:356-379`, `examples/prisma-8-demo/src/orm-client/get-post-tags.ts`, `create-post-with-tags.ts`. +6. **Destructive consent.** `migrations.md:33, 146, 470` says pass `-y` to auto-accept a destructive `db update`. `--yes` does not grant consent; the user types the database name or passes `--confirm `. Source: `cli/src/orm/db/update.ts:118-124, 218-224`, `cli/src/orm/db/consent.ts`. +7. **Migration factories as free functions.** `migrations.md:231-235, 254-270` renders `addColumn('public', 'user', {...})` and `setNotNull('public', 'user', 'name')`. They are methods on the `Migration` base class with object arguments: `this.addColumn({ schema, table, column: col(...) })`, `this.setNotNull({ schema, table, column })`. Source: `packages/3-targets/3-targets/postgres/src/core/migrations/postgres-migration.ts:231, 364`, `examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.ts:17-21`. +8. **Transaction scope of `db migrate`.** `migrations.md:48` says each migration runs in its own `BEGIN … COMMIT`. The runner issues one `BEGIN`, applies every space's plan, then one `COMMIT` or `ROLLBACK`. Source: `packages/3-targets/3-targets/postgres/src/core/migrations/runner.ts:176-215, 684-696`. +9. **Marker name.** `quickstart.md:125` calls the marker a `pn_meta_marker` row. It is `prisma_contract.marker`. Source: `runner.ts:321-326`. +10. **Middleware that does not exist, and one that does.** `runtime.md:11, 135-158, 315, 322` imports `createTelemetryMiddleware` from `@internal/middleware-telemetry`; no such package exists. `runtime.md` also says lints and budgets must come from `@internal/sql-runtime` because the facade has no middleware subpath; the public path is `@prisma/orm-postgres/family-runtime`. And it never mentions the cache middleware (`@prisma/orm-extension-middleware-cache`), which ships. Source: `packages/9-public/@prisma/orm-postgres/package.json` (`./family-runtime`), `packages/3-extensions/middleware-cache/`, `examples/prisma-8-demo/src/prisma/db.ts`. +11. **Dropped extension as the canonical example.** `contract.md:25, 29, 49` uses `cipherstash.EncryptedString(...)`. The in-tree cipherstash extension was dropped in favour of CipherStash's own package (`CHANGELOG.md:1040`). Use `pgvector.Vector(1536)` as the demo does. +12. **Mongo PSL attributes.** `contract.md:221` says `@@discriminator` / `@@base` are SQL-only and Mongo polymorphism needs the TypeScript builder. The Mongo PSL interpreter implements both and the Mongo demo uses them. Source: `packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts:276-418`, `examples/mongo-demo/src/contract.prisma`. +13. **Scalar lists.** `contract.md:123` says `String[]` is rejected by the SQL interpreter. Lists are gated on the `sql.scalarList` capability, which PostgreSQL declares; only SQLite rejects them. Source: `packages/2-sql/2-authoring/contract-psl/src/psl-field-resolution.ts:497-500`, `examples/prisma-8-demo/src/prisma/contract.json` (`"scalarList": true`). +14. **`.count()` as a collection terminal.** `queries-postgres.md:30` lists `.count()` beside `.all()` / `.first()`. There is no `count` method on the collection; `count()` exists as an `include` reducer and inside `aggregate()`. Source: `packages/3-extensions/sql-orm-client/src/collection.ts`. +15. **SQL-builder `insert` input.** `queries-postgres.md:258, 298-299` passes a single object; `insert()` takes an array of rows. Source: `packages/2-sql/4-lanes/sql-builder/src/types/table-proxy.ts:122-123`. +16. **Combinator import path.** `queries.md:141`, `queries-postgres.md:75, 86, 251` say `and` / `or` / `not` are internal-only. `@prisma/orm-postgres/orm-client` exports them. Source: `packages/9-public/@prisma/orm-postgres/package.json`, `test/facade-tarball.test.ts:58, 171`. +17. **Prepared statements.** `debug.md:127` says you cannot pre-prepare and re-execute a statement and points to TypedSQL (which `queries.md:144` says does not exist). `db.prepare(...)` and `runtime.prepare(...)` exist; a row-returning prepared statement runs with `ps.query(runtime, params)`. Source: `packages/2-sql/5-runtime/src/sql-runtime.ts:110, 549`, `packages/2-sql/5-runtime/src/prepared/types.ts:55-105`. +18. **Streaming.** `queries.md:146` says there is no streaming surface and recommends holding a `pg.Client`. The read terminals return an `AsyncIterableResult` you can `for await`; but on the long-lived `postgres()` facade the driver runs with cursors disabled, so the full result is fetched before iteration and only decoding is per row. The serverless facade (`@prisma/orm-postgres/serverless`) reads through a cursor in batches of 100. Say exactly that. Source: `packages/3-extensions/sql-orm-client/src/collection.ts:998-1022`, `packages/3-extensions/postgres/src/runtime/postgres.ts:245-247`, `packages/3-targets/7-drivers/postgres/src/postgres-driver.ts:39-53`, `postgres-serverless.ts:28-42`. +19. **`migration show` with no argument.** `migrations.md:189` runs it bare; the `` positional is required. Source: `cli/src/orm/migration/show.ts:3-6`. +20. **`--from` grammar.** `migration-model.md:67` lists `@contract | @db | @empty` for `migration plan --from`. The command is offline: only `@empty` resolves there (`@db` needs the live marker, `@contract` needs a contract hash the plan resolver does not pass). Source: `cli/src/control-api/operations/plan-resolution.ts:224-229`, `packages/1-framework/3-tooling/migration/src/refs/contract-ref.ts:39-56`. +21. **Node.js floor for Temporal.** Wherever the skill says Temporal-backed types need a polyfill "on Node", say: Node.js 26 and later ship `globalThis.Temporal` (Homebrew's Node.js 26 build omits it); 25 and earlier, including every 22 and 24, do not, so `temporal-polyfill/full/global` must be imported before the first read or the column authored as a `*String` type. Checked empirically on 22.22.3, 24.16.0, 25.8.0, 26.0.0, 26.8.1 (official and Homebrew), 26.8.2. + +Things the skill gets right that the site had wrong (keep them): `db.raw.sql` as the raw tag and whole-query raw SQL with `.returnsRow()` / `.affectedCount()` (`queries.md:143`, `queries-postgres.md:392, 415`); `extensions` not `extensionPacks` in `defineContract` (`contract.md:51, 386`); the five lint severity keys (`runtime.md`); `../../snapshots//contract.json` imports (`migrations.md:225, 253`); `db.close()` for teardown (`runtime.md:66-78`); the `prebuild` emit for Next.js (`build.md:466`). + +## Acceptance + +- Every item above is fixed, with the commit citing the source line used. +- A pass over the rest of `references/*.md` for the same classes of defect: any `execute(` on a row-returning plan, any `PN-` code, any flat `defineConfig`, any `@internal/middleware-telemetry`, any `--skip-skills`, any `prisma/contract.prisma` default. +- The skill's own journey tests (`skills/journey-tests/`) pass, and any sample that can be typechecked against `examples/prisma-8-demo` or `examples/mongo-demo` is. +- The `skills/prisma-8/upgrading/` instructions are not in scope unless they repeat one of the defects above. diff --git a/docs/orm-docs-audit/brief-skills-opt-out.md b/docs/orm-docs-audit/brief-skills-opt-out.md new file mode 100644 index 0000000000..568020ae81 --- /dev/null +++ b/docs/orm-docs-audit/brief-skills-opt-out.md @@ -0,0 +1,47 @@ +# Brief: let users opt out of agent skill files at scaffold time + +Written 2026-09-10 for an agent with no prior context. Repos: `prisma/create-prisma` (the scaffolder, checked at 0.11.7) and `prisma/prisma-cli` (the unified `prisma` binary, whose `init` and `skills` commands are involved). + +## What we want + +1. `create-prisma` asks. Add a prompt in the interactive flow, "Install agent skills for coding assistants?" or similar, alongside the existing prompts, defaulting to yes. Add a flag for the non-interactive path, `--skills ` matching `prisma init --skills`, so `--yes` plus `--skills none` produces a project with no agent files, no `postinstall` hook, and `skills: { agents: [] }` in the config. +2. When the answer is no, the generated project has no `.claude/`, `.cursor/`, `.agents/`, or Devin directories, no `postinstall`, and no `skills:sync` script. `prisma-next.md` is a separate question: decide whether it is an agent file or a human readme and treat it accordingly. +3. `prisma init` on the existing-project path (the command that installs skills; `orm init` stopped touching skills in rc.6) asks the same question when interactive, and honours `--skills`. +4. Removing skills after the fact: `prisma skills sync` with `agents: []` should remove the directories it previously wrote, or there should be a `prisma skills remove`. Decide which; the current behaviour (write nothing, leave the old copies) means the config opt-out looks like it did nothing. + +## Acceptance + +- `create-prisma my-app --yes --skills none` produces a project with none of the four agent directories, no `postinstall`, and `agents: []` in the config. Test in create-prisma's `tests/`. +- The interactive prompt appears and its answer is reflected the same way. +- `prisma init --skills=none` on an existing project behaves the same and, with an existing config, writes `agents: []` into it rather than leaving it alone. +- `prisma skills sync` after setting `agents: []` removes the previously written directories, or the new remove command does, with a test in prisma-cli. +- The `create-prisma` README and the CLI help text list the new flag. +- Note in the PR the exact user-facing sequence for the docs page, which is written separately: the `create-prisma` reference page, both quickstarts, `cli/init`, `cli/orm-init`, `cli/skills`, and `cli/configuration` on the docs site all need the answer. + +## The problem + +`npx create-prisma@latest` writes agent skill files for Claude, Cursor, Codex-style `.agents`, and Devin into every new project, adds a `postinstall` hook that re-syncs them on every install, and offers no prompt or flag to decline. One user counted "more than 200 files" and left. Five people raised it in one week. The workaround being passed around in the community (`skills sync --disable`, a `PRISMA_DISABLE_AGENT` env var) is wrong: the first only silences the staleness notice and the second does not exist. + +## What exists today + +In `create-prisma`: + +- `templates/create/_shared/prisma.config.ts.hbs` writes `skills: { agents: ["claude", "cursor", "agents", "devin"] }` for every non-Deno project. +- `src/tasks/install.ts` adds a `skills:sync` package script. +- `src/tasks/prisma-setup/commands.ts` first runs `prisma orm init --yes --target ... --authoring ... --skip-install`, which writes `prisma-next.md` (create-prisma deletes it again only for Deno). Then `initializeAgentSkills` runs `prisma init --yes`. That command adds a `postinstall` of `prisma skills sync || exit 0`, leaves the existing `prisma.config.ts` alone, and runs `skills sync` once. +- The CLI flags are `--template`, `--provider`, `--authoring`, `--package-manager`, `--deploy`/`--no-deploy`, `--workspace`, `--yes`, `--force`, `--verbose`, `--json`. Nothing about skills. + +In `prisma-cli`: + +- `prisma init --skills=none` writes `skills: { agents: [] }` when no config exists. With an existing config it does nothing to it. +- `prisma orm init` does not install skills (the `--skip-skills` flag was removed in rc.6 along with the install); it only deletes retired skill directories. +- `prisma skills sync` writes the directories listed in `skills.agents`; with `agents: []` it writes nothing, but it does not delete copies already on disk. +- `skills: { check: false }` only silences the out-of-date notice. + +So the real opt-out is `skills: { agents: [] }` in `prisma.config.ts`, and nothing on the scaffold path lets the user say that. + +## Pointers + +- create-prisma: `src/tasks/setup-prisma.ts`, `src/tasks/prisma-setup/commands.ts`, `src/tasks/install.ts`, `templates/create/_shared/prisma.config.ts.hbs`, `src/index.ts` for flags +- prisma-cli: the `init`, `orm init`, and `skills` commands +- Docs pages describing current behaviour, in `prisma/web` under `apps/docs/content/docs/`: `cli/init.mdx`, `cli/orm-init.mdx`, `cli/skills.mdx`, `cli/configuration.mdx` diff --git a/docs/orm-docs-audit/changes.md b/docs/orm-docs-audit/changes.md new file mode 100644 index 0000000000..f677c29f5e --- /dev/null +++ b/docs/orm-docs-audit/changes.md @@ -0,0 +1,154 @@ +# Prisma ORM 8 docs: what to change, and in what order + +This is the working list of changes to the Prisma ORM 8 documentation, built from a page-by-page audit against `@prisma/orm-postgres@8.0.0-rc.9` (the audit files are under `audit/`, with a source citation for every finding). Four kinds of change: restructure the site, correct existing pages, write new pages, change the ORM or its tooling where the docs are hard because the tool is. + +Item numbers (A1, B11, C9, D4) are stable identifiers used in the audit files and the PRs; some are out of sequence because items were added as they were found. "J1" to "J10" are the reader jobs defined in `journeys.md`. Source paths are in `prisma/orm` at rc.9 unless they start with `create-prisma`. + +Status as of 2026-09-11. + +## Where things stand + +**Shipped, or nearly.** Every correction to an existing page (section B, 35 items) is in three PRs: prisma/web #8236 (API names, flags, and paths that no longer exist) and #8237 (pages whose explanations were wrong) are merged; #8238 (facts on the getting-started pages and guides) is open as of 2026-09-11. Each commit cites the source line it was verified against. Section B below is a summary of what changed; the details are in the PRs. + +**Landed since the audit.** A7 shipped as prisma/web #8242 (merged 2026-09-11), in a different shape from the move proposed below: fifteen of the sixteen Prisma 7 guides were rewritten for Prisma ORM 8 at the same slugs, each with its Prisma 7 page preserved under `guides/v7/`; Cloudflare D1 moved to `guides/v7/` only, with a redirect; thirteen other pages (authentication, some integrations, some Prisma Postgres guides) stay in the Latest tree with a "This guide uses Prisma 7" note. C9, C11, and C12 need re-scoping against the pages #8242 added. + +**In progress.** D1 (adoption sets the `db` ref) and D3 (a skills opt-out in create-prisma) have briefs and are with ORM-side agents. D14 (the agent skill) has a brief ready to hand over. + +**Not started.** The naming pass, the restructure (A1 to A3, A5, A6, A8), every new page (C), and the rest of D. + +## Recommended order for the open work + +1. **Naming pass**: "Prisma 8" to "Prisma ORM" everywhere (679 occurrences in 109 files). Mechanical, one PR. A7 has landed (#8242), so nothing blocks it. +2. **C4, release status page.** Half a page; unblocks J4 (the reader who got upgraded by accident). Needs the GA window and the Prisma ORM 7 support policy from Will. +3. **C2, add Prisma ORM to an app you already started.** The J1 page and the complaint that started this work. One page, written from `mental-model.md`. +4. **C1, coming from Prisma 7.** The mapping table and the "not yet" list. Unblocks J3. +5. **C9, deployment.** Was the largest hole; #8242 shipped Prisma ORM 8 guides for Docker, Cloudflare Workers, Turborepo, pnpm workspaces, and Bun workspaces. What remains is what those pages do not cover (see C9). +6. **The restructure** (A1, A2, A3, A5, A6) as one piece of work, after the pages above exist to be organised. +7. **Everything else in C** as its dependency in D clears. + +## Decisions needed, and from whom + +| Item | Decision | Who | +|---|---|---| +| D6 | Keep the release candidate on npm `latest`, or move it to `next` until GA | Will, with whoever owns publishing | +| D4 | For each missing client API: build it, or document the workaround and say it is not planned | ORM product | +| D1 | Which of the two designs for one-step adoption | ORM (brief in `brief-db-ref-on-adoption.md`) | +| D3 | Flag name and prompt wording for the skills opt-out | create-prisma (brief in `brief-skills-opt-out.md`) | +| C18 | The version floors to publish: the monorepo's `docs/Supported Versions.md` says Node 24; the scaffold enforces 22.18 and rc.9 runs on 22.12 | ORM product | +| C5 | Current state of the VS Code extension and language server | Serhii | +| C7 | Whether RLS, expression indexes, and `@@control` are documented as supported or as preview | ORM product | + +## A. Restructure + +The proposed sidebar trees and the `/orm` page structure are in `ia.md`. These are the moves that tree implies. + +- **A1. Give the ORM its own front door, organised by starting state.** Replace the quickstart-versus-existing-project split with four doors on `/prisma-orm` and `/orm`: new app from scratch; app already started with an empty database; existing database with data; existing Prisma 7 app. Each door is one page that carries the whole path. Today three of the four starting states share one page written for the third. Unblocks J1, J2, J5. +- **A2. "Coming from Prisma 7" at the top level of the ORM sidebar**, linked from the root page, `/getting-started`, `/orm`, and reading-data. The fourteen inline diff blocks stay but point at it. Unblocks J3, J4. The page itself is C1. +- **A3. Human content first, agent prompts last.** `/prisma-orm`, `/getting-started`, and every framework guide open with a copyable agent prompt before any human instruction. Move the prompt to the end or into a collapsed block. This is the direct answer to the "written for AI" complaint. +- **A5. Root page: give the ORM a section**, not one line ("Here for the ORM? Jump to Prisma 7 or Prisma 8"). The four doors from A1 in one row. File: `(index)/index.mdx`. +- **A6. Rewrite `orm/core-concepts.mdx` as a narrative** from `mental-model.md`. The current page is a glossary; the mental model explains why, in the order a reader meets each piece. Keep the glossary as a closing section. +- **A7. Move the sixteen Prisma 7 guides out of the Latest tree.** Landed as #8242 (2026-09-11), in a different shape. `guides/frameworks/react-router-7`, `solid-start`, `guides/deployment/*` except Cloudflare D1, `guides/integrations/github-actions`, `ai-sdk`, all of `guides/switch-to-prisma-orm/*` and `guides/database/*` were rewritten for Prisma ORM 8 at the same slugs, each with its Prisma 7 page preserved under `guides/v7/`; Cloudflare D1 moved to `guides/v7/` only (there is no Prisma ORM 8 D1 driver). The Latest sidebar keeps those labels. Thirteen further pages stay in Latest with a "This guide uses Prisma 7" note; #8242 says which are blocked on third-party adapters and which are portable follow-ups. +- **A8. One error reference, or two that link.** `cli/error-reference.mdx` claims to list every CLI code but carries only the platform namespaces; every `MIGRATION.*`, `CONTRACT.*`, `CONFIG.*` code is on `orm/reference/error-reference.mdx`, and the structured errors' `docsUrl` points there. Merge them, or state the split on both and cross-link. +- **A4** (sidebar labels and the guides index) is in #8238, open as of 2026-09-11. + +## B. Corrections to existing pages (#8236 and #8237 merged; #8238 open) + +Thirty-five items, all verified against rc.9 source, in #8236 and #8237 (merged) and #8238 (open as of 2026-09-11). What they fixed, in order of how many readers they hit: + +| Theme | What was wrong | Items | +|---|---|---| +| Row reads through `execute()` | Since rc.2 `execute()` returns `{ affectedRows }`; rows come from `query()`. About 200 lines on 14 pages, plus Mongo's removed `db.execute`. | B11 | +| Renamed API | `take`/`skip` (rc.7), `createCount` and siblings (0.17), `extensionPacks` (0.17), `@db.*` attributes (0.17), `sha256:` hash prefixes (0.17), the migration snapshot layout (0.17) | B1, B2, B4, B12, B13, B14 | +| Existing-project pages | Wrong default path (`prisma/` vs `src/prisma/`), skills claim, `.env` prompt, Temporal polyfill, namespace-qualified accessors | B3, B15, B10 | +| The `db` ref | `migration plan` refuses (`MIGRATION.PLAN_ORIGIN_UNKNOWN`) instead of planning CREATE-everything; `db init`/`db update` advance the ref only without `--db`; the automatic baseline | B16 | +| Middleware | Five-hook model replaced by the two rc.2 lifecycles; the logger example never fired on reads; lint keys, budgets latency, "permissive mode" | B17 | +| Things called unsupported that work | Whole-query raw SQL, 1:1 back-relations, many-to-many include and nested writes, referential actions, `.variant()` | B18, B19 | +| Aggregates | Return numbers, not strings; the lossless variants | B20 | +| Stale CLI surface | `--skip-skills`, bare `ref` group, `prisma-cli` hints, `PN-RUN-*` codes, printed output, top-level `migrate` | B21 | +| Samples that failed | Data-transform wiring, upsert without `conflictOn`, `prepare` then `connect()`, structured error codes shown as plain `Error` | B22, B24, B27 | +| Getting-started facts | Node floor (22.18; on the 24 line 24.11; 24 recommended), MongoDB replica set not required, create-prisma flags and scripts, `npm create prisma@latest`, SQLite ships, upgrade-guide version targets, `// use prisma-next`, `db.close()`, enum ordering, the scalar table, the skills page, the tutorial's starter model, the extensions recipe, streaming does not stream on `postgres()` | B5 to B9, B23, B25, B26, B28 to B35 | + +Two audit claims were overruled by the source during implementation and the pages left as they were: `migration plan` can raise `MIGRATION.NO_TARGET` after a rollback cycle, and `min`/`max` over a `DateTime` column type-checks at rc.9. + +## C. New pages + +Grouped by where the page lives. Each says what the page is for, what it must cover, where the facts come from, and what it waits on. + +### Entry pages + +- **C2. Add Prisma ORM to an app you already started.** Starts from "you have a project directory and an empty or absent database": `orm init`, edit the contract, `contract emit`, `db init`, one query, with `db.ts` shown. Bun as the worked example, since that is the case reported, with a note for Node. Unblocks J1. Source: `mental-model.md`; the scaffold in `create-prisma`. +- **C1. Coming from Prisma 7.** The mapping table from `mental-model.md` in four sections: schema and types, CLI commands, client API, and "not in Prisma ORM 8 yet" with a status per item. The last section is the one readers ask for most and no page has: `skipDuplicates`, `increment`/`decrement`, `findUniqueOrThrow`, case-insensitive filters, `$transaction` arrays, model and payload types, `@updatedAt` (rejected; use `temporal.updatedAt()`), `cuid()` (rejected; use `cuid(2)`), JSON path filtering, soft delete, validations, callbacks, read replicas. Unblocks J3. Placement is A2; content of the "not yet" list depends on D4. +- **C3. Adopt an existing database.** Split from the existing-project page so it has room: what `contract infer` produces (SQL-only; captures indexes, checks, RLS, 1:1 relations; emits Temporal-backed timestamp types that need the polyfill, D10), what `db sign` checks and what exit code 4 means, the baseline migration, the `db` ref, the second migration with `--advance-ref db`. Unblocks J2. Waits on D1, which decides how short it can be. +- **C4. Release status.** One short page: release candidate; GA window; Prisma ORM 7 gets bug fixes and security updates for 18 months from GA; `npx prisma` and `npm install prisma` resolve to Prisma ORM 8; to stay on 7, pin `prisma@7` and `@prisma/client@7`. Linked from the root page and every "Using Prisma 7?" note. Unblocks J4. GA is expected in October 2026 and Prisma ORM 7 is supported for 18 months from GA; waits on D6 (whether the tag stays). +- **C18. Supported versions.** No page states the floors. Candidates: Node (see the decision above), Bun 1.2 (site says 1.1), Deno 2.0, PostgreSQL 15 (stated nowhere), MongoDB 8.0, TypeScript 5.9, `tsconfig` with `moduleResolution: "bundler"` and `strict: true`, ESM-only. The monorepo's `docs/Supported Versions.md` is the draft but is wrong about Node and documents a `--merge-tsconfig` flag that does not exist, so confirm each number. One page under getting started, linked from every quickstart. +- **C22. A database on your machine.** One page in the Getting Started > Prisma ORM subtree for the reader with no hosted database: `prisma dev` first (a local Prisma Postgres, no install; the page under Local development already exists, link it), then the Composer local stack (`dev`), then a PostgreSQL you run yourself, with a Docker Compose file and the connection string for it. Ends with how to look at the rows: Studio against the local instance (check whether `prisma dev` serves Studio, and which command opens it in Prisma ORM 8), or a Postgres client such as pgAdmin. Linked from every quickstart at the point where `DATABASE_URL` first appears. Unblocks J11. Source: `local-development/postgres.mdx`, `composer/local-development.mdx`, the Docker deployment guide for the Compose file shape. +- **C23. Example repositories.** One small runnable repository per starting state, each with a README that lists which files the reader writes, which files Prisma generates, and the command that produces each, and with comments in `db.ts`, `prisma.config.ts`, and the query file. Linked from the matching quickstart or starting-state page. Seed: `examples/prisma-8-demo` in the monorepo. Needs a home (a `prisma/examples`-style repository or a folder in `prisma/orm`) and an owner on the ORM side. Unblocks J12. +- **C5. Editor setup.** Where the VS Code extension lives (open-vsx), that Prisma ORM 8 does not bundle a language server and uses the locally installed CLI, that `// use prisma-next` on line one is what the language server keys on, and `prisma contract format` as the fallback. Unblocks J7. Waits on Serhii. + +### Guides + +- **C9. Deploying Prisma ORM 8.** Partly overtaken by #8242, which added Prisma ORM 8 guides for Docker, Cloudflare Workers, Turborepo, pnpm workspaces, and Bun workspaces; re-scope against those pages before writing. What a deploy needs: `prisma contract emit` as a `prebuild` step, `db migrate` (or `db init`) from the pipeline before the app starts, `db.close()` on shutdown, and for edge runtimes the per-request facade `@prisma/orm-postgres/serverless` (`postgresServerless({ contractJson })` at module scope, `await using runtime = await db.connect({ url })` per request). Sections or pages for Docker, Vercel, Cloudflare Workers, monorepos. The monorepo's `docs/Serverless Deployment Guide.md` is the source for the Cloudflare path (Hyperdrive, `nodejs_compat`, migrations against the origin URL never through Hyperdrive, one runtime per `connect()`, the 128 MiB isolate limit) and carries a production caveat with no site presence: against real Hyperdrive the serverless facade's default cursor path hangs (SQLSTATE 58000, request killed at 30 s); the workaround is `cursor: { disabled: true }`. Source: `skills/prisma-8/references/build.md:421-435`, `packages/3-extensions/postgres/README.md:41-60`, `docs/Serverless Deployment Guide.md:97,274`. +- **C11. CI.** #8242 rewrote `guides/integrations/github-actions.mdx` for Prisma ORM 8; check it against this before writing. `prisma migration status --to --db "$URL" --json`, fail the job on any `diagnostics[]` entry (the process exits 0 on warnings, so exit codes alone miss `MIGRATION.DIVERGED` and friends), then `prisma db migrate --to --db "$URL"`. Source: `skills/prisma-8/references/migration-review.md:172-197`. D16 would make this one line. +- **C12. Switching from another ORM; team schema workflow.** #8242 rewrote the three `guides/switch-to-prisma-orm/*` and three `guides/database/*` pages for Prisma ORM 8; re-scope against them. The brownfield flow (`orm init`, `contract infer`, review, `contract emit`, `db sign`, `db verify`) and the team loop (`migration plan`, refs, `db update` for local iteration, `db verify`, `migration status` in CI). Reuses C3 and C11. +- **C10. Supabase and row level security.** The Supabase extension is one table row on the site. What ships: `@@rls` on a model, the five `policy_*` block kinds with `target` / `roles` / `using` / `withCheck`, the cross-space foreign key `supabase:auth.AuthUser`, `supabase({ contractJson, url, jwksUrl | jwtSecret })`, `db.asUser(jwt)` / `db.asAnon()` / `db.asServiceRole()`, the session-pooler requirement, the one-time `GRANT` for `auth.*`, and the `SUPABASE.*` errors. Source: `packages/9-public/@prisma/orm-extension-supabase/`, `skills/prisma-8/references/supabase.md`. + +### Reference completeness + +- **C7. Advanced Postgres.** All of it ships and none of it is documented: RLS and policies in PSL and the TypeScript builder; expression and partial indexes (`@@index(expression:, where:, unique:, type:)`); `@@check`; `@noCheck`; `installExtension` / `createExtension` migration factories; `@@control` for how much of a table Prisma manages. Unblocks J8. Source: `skills/prisma-8/references/contract.md:101-108,265-367`, `packages/2-sql/2-authoring/contract-psl/README.md`. Waits on the product call about how to label these. +- **C8. Money and Decimal.** Cents as integers (already there), `Numeric(p, s)` in type position, what `@default` accepts, `avgDecimal`, and that a decimal string avoids float loss while the declared scale still rounds. Answers an open Discord question. +- **C6. Types.** Model, result, where, and input types with the Prisma 7 names beside each. Written from `docs/reference/model-and-result-types.md` on prisma/orm `main` (#30231 and #30236 merged 2026-09-10); publish when the first tagged release after rc.9 ships them. #30158 (reusable where filter types) is already in rc.9. Also what `contract.d.ts` already exports today. Unblocks J9. +- **C13. Migration factory reference.** The site shows six factories; the `Migration` base class has about thirty (indexes, foreign keys, uniques, checks, column changes, native enums, `installExtension`, RLS and policies), plus free factories and the `node migration.ts --dry-run` / `--config` self-emit CLI. Source: `packages/3-targets/3-targets/postgres/src/core/migrations/postgres-migration.ts:159-506`. +- **C14. PSL and TypeScript builder completeness.** A rewrite of `psl-syntax.mdx` and `typescript-schema-builder.mdx` large enough to plan as one. The PSL page lacks native types in type position, the `@default` generator list, the `temporal.*` presets, scalar lists, `@@unique`, `@@index` options, `@@check`, `@noCheck`, `@@control`, `namespace` blocks, `native_enum`, `@relation("Name")`, `onDelete`/`onUpdate`, inline extension types, MongoDB index options, `.variant()`. The TypeScript page lacks most field helpers, composite keys (`.attributes()`), indexes and checks, `foreignKeyDefaults`, the `output` option, and the MongoDB builder's value objects, indexes, enums, vectors, and polymorphism. The full list is in `audit/contract.md` under Missing. Source: the two `contract-psl` and `contract-ts` READMEs. +- **C15. Runtime reference completeness.** Additions to `transactions-and-runtime.mdx` and `orm-client.mdx`: `query` vs `execute`; `verifyMarker`; `binding`; `db.raw`, `db.nativeEnums`, `db.context`, `db.contract`, `db.stack`; the flat `db.orm.User` accessor for single-namespace contracts; `AsyncIterableResult.first()` / `.firstOrThrow()`; the `configure(meta)` callback and `meta.annotate(...)` (how cache opt-in works); `distinctOn`'s capability; MongoDB `mode` and the extra filter classes; the SQLite facade; the static client; `db.prepare`; `ctx.signal` and `MongoMiddleware`; `createInMemoryCacheStore`. Full list in `audit/orm-client.md` and `audit/builders.md` under Missing. +- **C16. Build integration.** The Vite plugin `@prisma/orm-postgres/vite-plugin-contract-emit` (re-emits on save; dev server only) and the `"prebuild": "prisma contract emit"` pattern for every other bundler. Zero hits on the site; the Vite-based framework guides tell readers to run emit by hand. Source: `packages/1-framework/3-tooling/vite-plugin-contract-emit/README.md`. +- **C19. Extension pages beyond pgvector.** PostGIS (`Geometry`, seven operations, the `pg/geometry@1` JSON round-trip exemption), ParadeDB (`key_field`-only), arktype-json (`arktypeJson(schema)`) each have one catalog row. +- **C20. Telemetry page.** `cli/telemetry.mdx` omits `DO_NOT_TRACK=1`, `PRISMA_NEXT_DISABLE_TELEMETRY=1`, the per-user config file, that CI never sends, the first-run notice, and the event fields. The rc.9 first-run notice links to `prisma-next.dev/docs/cli/telemetry`, which is not this page. Source: `packages/1-framework/3-tooling/cli-telemetry/src/`. +- **C21. Plain-language pass over the reference and fundamentals pages.** 66 Prisma ORM 8 pages use vocabulary from the source code that readers do not have ("terminal", "codec", "reducer", "refinement callback", "namespace-qualified", "facade", "lane", "surface"). This is the largest complaint about the docs. Every page gets the reader review in `plain-language.md`: an agent with only the Prisma ORM 7 user's knowledge marks every sentence it cannot restate, and each mark is rewritten. Start with `orm/fundamentals/*` and `orm/reference/orm-client.mdx`, which every reader hits. The check script `check-plain.sh` catches the banned words before push; the review catches the rest. +- **C25. Data transforms that need an intermediate contract.** A migration that both removes a column and has to read it in `dataTransform` cannot type the transform against either the start or the end contract; the planner rejects the query with `PN-MIG-2005`, and authors fall back to raw SQL. The technique that exists today: emit an `intermediate.prisma` inside the migration directory describing the in-between shape, and type the transform against it. Goes on `orm/migrations/editing-a-migration.mdx` next to `dataTransform`, with the `updatedAtString()` trap that makes `ops.json` non-deterministic. Brief: `brief-intermediate-contracts.md`. +- **C17. CLI page additions.** `configuration.mdx`: `migrations.dir`, `formatter`, the `composer` section, the telemetry variables. `orm-init.mdx`: full flag list, exit codes 4 and 5, `prisma-next.md`. `db-update.mdx`: never plans `data`-class operations. `migration-ref.mdx`: the refusals. `migration-new.mdx`: default origin rules. `migration-status.mdx`: `--json` diagnostics. `db-migrate.mdx`: `MIGRATION.MARKER_MISMATCH`, `PATH_UNREACHABLE`. `contract-emit.mdx`: `--json` output, the `output` option. `cli/index.mdx`: `@prisma/orm-toolchain`, `@prisma/orm-framework`. + +## D. Change the ORM and its tooling + +Places where the docs are hard because the tool is. Grouped by who owns the fix. + +### ORM behaviour + +- **D1. Make adoption one step.** Today: `contract infer`, edit, `contract emit`, `db sign`, `migration plan`, `migration ref set db `. Either `db sign` sets the `db` ref when none exists, or a `db adopt` command does sign plus ref. Removes the recreate-everything trap in J2 and halves C3. In progress; brief in `brief-db-ref-on-adoption.md`. +- **D2. `migration plan` with no origin and no migrations on disk plans from empty silently.** With migrations on disk it refuses (`MIGRATION.PLAN_ORIGIN_UNKNOWN`). For a signed database with no ref the silent case is the wrong default; it should warn or refuse the same way. +- **D9. `--db` suppresses `db` ref advancement.** `db init` and `db update` advance the ref only when the URL comes from config; with `--db` they do not unless `--advance-ref` is also passed. Surprising, and every example passes `--db`. Advance regardless, or warn when `--db` is used without `--advance-ref` and no ref exists. Source: `cli/src/control-api/operations/ref-advancement.ts:19-28`. +- **D13. `migration ref set … @db` fails** with `MIGRATION.HASH_NOT_IN_GRAPH` instead of reading the marker. Support it when a connection is available, or reject it with a message that says why. Source: `cli/src/control-api/operations/ref.ts:104-118`. +- **D15. `db update` never plans data operations.** A change that needs a backfill fails and the user must switch to `migration plan`; nothing says so. Allow it, or print the hint. Source: `cli/src/control-api/operations/db-update.ts:17`. +- **D16. `migration status` exits 0 on warnings.** CI has to parse `--json`. A `--strict` flag or a non-zero exit on warn-level findings makes C11 one line. Source: `cli/src/orm/migration/status-findings.ts`. +- **D8. Streaming does not stream on `postgres()`.** The facade creates its driver with `cursor: { disabled: true }`, so `for await` iterates an already-fetched result; only the serverless facade exposes cursor batches. Measured: breaking after the first row of a 400,000-row table still grew the heap by 64 MB. Expose `cursor` on `postgres()` or keep the docs' new wording. Source: `packages/3-extensions/postgres/src/runtime/postgres.ts:245-247`. +- **D12. Permissive mode is unreachable on Postgres.** `postgres()` never forwards `mode`, so every "warn in permissive mode" branch in lints and budgets is dead on Postgres; only `mongo()` accepts it. Expose it or remove the branches. Source: `postgres.ts:86-94,276-282`, `sql-runtime.ts:187`. +- **D7. `db.connect()` vs `db.runtime()`, `db.close()` vs `db.runtime().close()`.** Two names for adjacent things, a Mongo-only `await`, and `db.prepare` then `db.connect()` throwing `DRIVER.ALREADY_CONNECTED`. One call on both targets would simplify every page that shows a script. +- **D10. Inferred contracts fail at read time without the Temporal polyfill.** `contract infer` maps timestamp columns to Temporal-backed types; on Node.js 25 and earlier every read throws (official Node.js 26 ships `globalThis.Temporal`; Homebrew's Node.js 26 build omits it) `RUNTIME.TEMPORAL_UNAVAILABLE` unless `temporal-polyfill/full/global` is imported. `create-prisma` installs the polyfill; `orm init` does not. Options: infer to `TimestamptzString` by default, have `orm init` install and import the polyfill, or fail at emit with a clear message. Source: `packages/3-targets/3-targets/postgres/src/core/psl-infer/postgres-type-map.ts:30-33`. +- **D5. `contract infer` on Prisma 7 databases.** Users report inferred contracts that fail to emit: `@db.*` attributes, `Decimal` defaults, non-`public` schemas. Check whether rc.9 still emits removed syntax and whether `Decimal @default(0.00)` is a bug. See D10. + +### Product decisions + +- **D4. Missing client API.** Verified absent: `firstOrThrow` on the collection (it exists on the result, so `.all().firstOrThrow()` works), atomic `increment`/`decrement`, case-insensitive string filters, insert with on-conflict-do-nothing (`skipDuplicates`), JSON path or containment filters (the `jsonb` codec has only the `equality` trait). Also not supported: soft delete, validations, lifecycle callbacks, custom `Collection` subclasses, read replicas, MongoDB referential actions. Each needs a decision: build it, or document the workaround and say it is not planned. The types work is merged: the where filter types (#30158) are in rc.9; `Models` and `Shape<>` (#30231, #30236) are on `main` awaiting a tagged release. +- **D6. The npm `latest` tag.** `prisma@latest` is 8.0.0-rc.13 while `@prisma/client@latest` is 7.10.0, so a Prisma 7 project's routine `npx prisma` picks up the 8 CLI against a 7 client. Move the release candidate to `next` until GA, or state the policy on C4. + +### create-prisma + +- **D3. Opt out of agent files at scaffold time.** No flag exists; every scaffold writes all four agent directories plus a `postinstall` hook. Add a prompt and `--skills none`, and have `orm init` ask. In progress; brief in `brief-skills-opt-out.md`. +- **D11. create-prisma pins rc.8.** `create-prisma@0.11.7` pins the ORM packages to `8.0.0-rc.8` for a Composer peer dependency, so fresh scaffolds lag the site. Bump on every release candidate, or document the lag. Source: `create-prisma/src/constants/dependencies.ts:8-10`. +- **D18. The MongoDB scaffold writes a `.env` nothing reads.** It contains `DATABASE_URL`; the generated `prisma.config.ts` reads `MONGODB_URL` and loads no dotenv file. Source: `create-prisma/templates/create/_shared/prisma.config.ts.hbs:13,31`, `src/tasks/prisma-setup/project-files.ts:22-26`. +- **Node 24.0 to 24.10.** The generated `package.json` declares `^22.18.0 || >=24.11.0` while create-prisma itself accepts `>=22.18.0`. Nobody could say why the 24 sub-range is excluded. Worth a question to the owner. + +### The agent skill + +- **D14. `skills/prisma-8/` is stale in the same places the site was, and more.** Twenty-one verified defects, the worst being row reads through `execute()`, `PN-*` error codes that no longer exist anywhere, and the flat config file removed in rc.4. It ships inside the ORM packages and is what coding agents read instead of the site. Brief ready to hand over: `brief-skill-staleness.md`. + +### Monorepo docs hygiene + +- **D17. `docs/reference/` and `docs/Supported Versions.md` disagree with the site and partly with the source.** `reference/capabilities.md` disagrees with the site's capabilities page on key namespacing, pack key nesting, the error code spelling, and whether capabilities are fixed at emit or negotiated at connect. `Supported Versions.md` says Node 24 and documents `--merge-tsconfig`, which does not exist. `Telemetry.md` says no exit code is sent; rc.9 sends one. `docs/reference/error-reference.md`, from which the site's error reference is generated verbatim, still says `ref set` and `prisma ref set` in eight entries (the command is `migration ref set` since rc.5). `mongodb-user-promise.md` promises MongoDB referential actions that are not implemented. Until reconciled, the audit treats `packages/` and `docs/releases/` as the only truth. + +## Naming + +The product is "Prisma ORM". A version number appears only when two versions are contrasted ("Prisma ORM 7 to 8"). Never "Prisma 8" as a product name. Rule, counts, and exceptions in `naming.md`. Applies to every item above and to the proposed nodes in `ia.md`. Not started; planned as its own PR (A7 has landed, so nothing blocks it). + +## Open questions + +Things the audit flagged but could not settle, mostly because the `prisma` binary and `@prisma/cli-engine` live in the `prisma-cli` repo, which was not audited: the global `--yes` flag on `db migrate`; `cli/global-flags.mdx`, `cli/init.mdx`, `cli/skills.mdx` as whole pages; `definePrismaConfig` from `prisma/config` versus `@prisma/cli-engine`; `HostedStateBootstrapError` and `--name`; Mongo `_id` decoding through `$lookup` and `findOneAndUpdate`; `_id` equality inside `match()`; the apply order of extension spaces versus `app`; whether `DateTime` still parses in PSL now that the starter writes `TimestamptzString`; the raw `EXTRACT(...)` codec claim; the `ilike` `fns` helper; "ParadeDB and Supabase are experimental"; which agent directory list is current (the skills page says Claude Code, Cursor, Codex, Windsurf; `cli/skills.mdx` and the scaffold say `claude`, `cursor`, `agents`, `devin`); whether `npx skills add` wants `prisma/prisma/skills` or `prisma/orm/skills`; the real capability key strings. Each audit file has an `## Unverified` section with what was checked. diff --git a/docs/orm-docs-audit/ia.md b/docs/orm-docs-audit/ia.md new file mode 100644 index 0000000000..8a7585f142 --- /dev/null +++ b/docs/orm-docs-audit/ia.md @@ -0,0 +1,190 @@ +# Information architecture: proposal + +Proposal first, then why, then the current state it changes. Source: the `meta.json` files and page titles under `apps/docs/content/docs/` in `prisma/web` on 2026-09-10. Labels are what the sidebar shows. + +## Proposed + +Scope: the Getting Started > Prisma ORM subtree, the ORM > Introduction group, and the Guides tree (two labels, and the Prisma 7 pages leaving it). Nothing else moves. `+` is a new node, `~` is a renamed or rewritten node, `-` is content leaving the tree, unmarked is unchanged. + +### Getting Started > Prisma ORM + +``` +Prisma ORM + Introduction to Prisma ORM human content first, agent prompt last + + Release status RC, GA window, 7 support period, what npx prisma installs, how to pin 7 + ~ Start a new app was "Quickstart"; same two pages + PostgreSQL + MongoDB + + Add to an app you already have empty or no database: orm init, contract, emit, db init, first query, db.ts shown + PostgreSQL + MongoDB + ~ Adopt an existing database was "Add to Existing Project"; adds baseline, ref, second migration + PostgreSQL + MongoDB + + Coming from Prisma 7 link to the ORM page below, so the Upgrader finds it from here + + A database on your machine prisma dev (no install) first; Composer's local stack; your own Postgres (Docker Compose); how to look at the rows + + Editor setup + create-prisma reference; moved to last +``` + +Four starting states, one group each. "Coming from Prisma 7" is a link node here because the page itself belongs with the API it maps. + +"A database on your machine" answers the question every starting state runs into on the first page: where does `DATABASE_URL` come from when I have no hosted database. The site already has the answers, `prisma dev` under Local development and the Composer local stack, but a reader inside the ORM subtree never meets them; the quickstart's "Path A" is the only mention. The node names the three options in order of effort (`prisma dev`, Composer, a Postgres you run yourself with Docker Compose), links to the pages that already exist for the first two, gives the Docker Compose file and connection string for the third, and ends with how to look at the data (Studio against the local instance, or any Postgres client). + +### ORM > Introduction + +``` +Introduction + Prisma ORM + + Coming from Prisma 7 the mapping page: schema and types, CLI, client API, not-yet list + ~ Core concepts rewritten as narrative from mental-model.md; glossary kept at the end +``` + +### Guides + +``` +Overview no "coming soon" line for upgrading; it exists +Upgrade Prisma ORM + ~ Prisma 7 to 8 (PostgreSQL) was "Migrate from Prisma 7 to Prisma 8" + ~ Prisma 6 to 8 (MongoDB) was "MongoDB" + v7, v6, v5, v4, v3, v1 + - Deployment, Database, Switch to Prisma ORM, Prisma 7 content; lives under guides/v7 + GitHub Actions, AI SDK, React Router 7, SolidStart +``` + +The Prisma ORM 8 replacements for those labels sit at the same slugs; C9 (deployment), C11 (CI), and C12 (switching, team workflow) in `changes.md` say what they must cover. + +### The ORM root page (`/orm`) + +Today the page has six sections: "Your schema becomes a contract", "What changed for developers", "Supported databases", "Get started" (three cards), "Go deeper" (five cards), and "Learn more about the design of Prisma 8" (five blog links). Three of the six argue for the design against Prisma 7. There is no query on the page. A Newcomer cannot tell from it what the product does; an Upgrader is told why it is different before being shown what it is. + +Proposed structure, in order: + +1. One sentence saying what it is: a typed data layer for TypeScript, for PostgreSQL, MongoDB, and SQLite. +2. The product in one screen: a short contract, the query it enables, and the typed result, side by side. Nothing else above the fold. +3. The four starting-state doors from the Getting Started subtree. +4. What you can do with it: one line and one link each for modeling, querying, relations, transactions, migrations, middleware, extensions. This is the section map, not an argument. +5. Coming from Prisma 7: one line linking to the mapping page. +6. Release status: one line linking to the status page. + +The page name follows the naming rule: "Prisma ORM", not "Prisma 8". + +Removed from this page: the three-step workflow explanation, "What changed for developers", and the blog list. The first moves into Core concepts; the other two go, or the blog list moves to the bottom of Core concepts. + +### Root page (`/`) + +The ORM line becomes a short row with the four starting states, linking into the subtree above. The platform hero stays. + +### What each proposed node fixes + +| Node | Jobs | +| --- | --- | +| Release status | J4 | +| Add to an app you already have | J1 | +| Adopt an existing database (rewritten) | J2 | +| Coming from Prisma 7 | J3, J5 (links to the incremental guide), J6 (attribute map) | +| A database on your machine | J1, J11 | +| Editor setup | J7 | +| Guides labels and overview line | J5 | +| Core concepts rewritten | the "written for AI" complaint, by giving the Newcomer a why-first page | + +J8, J9, and J10 need product decisions or shipped code before a node makes sense; they are in `changes.md` under C and D. + +### Not proposed + +No change to the top-level section list, to Data Modeling, Contract Authoring, Fundamentals, Migrations, Middleware, Extensions, or Reference. Under Guides, only the label changes above and the removal of the Prisma 7 pages (A7). The "Prisma 7" group at the bottom of Getting Started stays as the home for people staying on 7. + +## Why: where each job lands today, and where the structure fails it + +| Job | Lands on | Structural failure | +| --- | --- | --- | +| J1 existing app, empty database | Add to Existing Project > PostgreSQL | page assumes tables exist; no node for this state | +| J2 existing database with data | Add to Existing Project > PostgreSQL | right node, page stops before the ref step | +| J3 Prisma 7 query mapping | Fundamentals > Reading data | no node; fourteen diff blocks spread over six pages | +| J4 should I move now | root, `/prisma-orm` | no node; note box only says 7 is supported | +| J5 incremental upgrade | Guides > Upgrade Prisma ORM > Migrate from Prisma 7 to Prisma 8 | not linked from any ORM node; three levels down, and the MongoDB twin is labelled only "MongoDB" | +| J6 data types | Data Modeling > Overview | right node; one stale example | +| J7 editor setup | none | no node | +| J8 advanced Postgres | none | no node; needs product answers | +| J9 types | none | no node; API in PRs | +| J10 opt out of agent files | CLI > configuration | right node, unreachable from Getting Started | + +Two observations follow from the table. + +First, the Getting Started > Prisma ORM subtree is organised by the tool that runs (`create-prisma` for Quickstart, `orm init` for Add to Existing Project) and then by database. The reader does not know those tools yet. The failures in J1 and J2 both happen at the point where the reader's situation differs from the page's assumption about it. The reader knows their situation: do I have an app, do I have a database, does it have data, was it Prisma 7. That is why I proposed organising this subtree by starting state. The database split (PostgreSQL / MongoDB) stays inside each. + +Second, the Upgrader has no node anywhere in Getting Started or ORM. Every Prisma 7 pointer is for staying on 7. The one page written for moving to 8 is filed under Guides, three levels down, and its MongoDB twin carries only a database name as its label. + +## Current state + +Top-level sections, in nav order: Getting Started, ORM, Composer, Local development, AI, Compute, Postgres, Storage, Studio, Query insights, Console, Guides, CLI, REST API, Accelerate. + +### Getting Started + +``` +Start + Get started with Prisma / platform landing; ORM is one line + Choose a Prisma 8 setup path /getting-started + Deploy the full Prisma stack /full-stack-tutorial + Deploy your first app /prisma-compute/deploy +Prisma ORM + Introduction to Prisma 8 /prisma-orm + create-prisma /prisma-orm/create-prisma + Quickstart + PostgreSQL /prisma-orm/quickstart/postgresql + MongoDB /prisma-orm/quickstart/mongodb + Add to Existing Project + PostgreSQL /prisma-orm/add-to-existing-project/postgresql + MongoDB /prisma-orm/add-to-existing-project/mongodb +Prisma Postgres + (quickstarts per ORM, import from existing database, from the CLI) +Prisma 7 + Prisma 7 setup paths /v7/getting-started (for people staying on 7) +``` + +### ORM + +``` +Introduction + Prisma 8 /orm + Core concepts /orm/core-concepts +Data Modeling + Overview, Relational data modeling, MongoDB data modeling +Contract Authoring + The data contract, Author in PSL, Author in TypeScript, The emitted artifacts, Capabilities +Fundamentals + Reading data, Writing data, Relations and joins, Transactions, Advanced queries +Migrations + How migrations work, The migration graph, Generating, Editing, Applying, Rollbacks and recovery +Middleware + How middleware works, Built-in: budgets, lints, cache, Authoring custom middleware +Extensions + Using extensions +Reference + API reference, ORM client, SQL query builder, Pipeline builder, Raw queries, Transactions and runtime, Error reference +``` + +### Guides (ORM-relevant parts) + +Snapshot of 2026-09-10. On 2026-09-11, #8242 rewrote the Deployment, Database, Switch to Prisma ORM, GitHub Actions, AI SDK, React Router 7, and SolidStart pages for Prisma ORM 8 at the same slugs, moved their Prisma 7 versions (and Cloudflare D1) under `guides/v7`, and gave the overview an Upgrading section. + +``` +Overview /guides says "Upgrading: moving from Prisma 7" is coming +v7 +Frameworks (Next.js, Hono, TanStack Start, NestJS, Astro, Nuxt, SvelteKit, Elysia, React Router 7, SolidStart) +Runtimes (Bun, Deno) +Deployment, Authentication, Integrations, Postgres +Database (Expand-and-contract migrations, Multiple databases, Schema management in teams) +Switch to Prisma ORM, Switch to Prisma Postgres +Upgrade Prisma ORM + Migrate from Prisma 7 to Prisma 8 /guides/upgrade-prisma-orm/postgresql Prisma 7 to 8 + MongoDB /guides/upgrade-prisma-orm/mongodb v6 Mongo to 8 + v7, v6, v5, v4, v3, v1 older version-to-version guides +Making guides +``` + +### Where Prisma 7 readers are pointed today + +- Root page: one line, "Here for the ORM? Jump straight to Prisma 7 or Prisma 8". +- A note box ("Using Prisma 7?", "Prisma 7", or "Prisma 7 users") on `/orm`, `/prisma-orm`, `/prisma-orm/create-prisma`, all four getting-started pages (PostgreSQL and MongoDB), and `/cli`. It says Prisma 7 remains supported and links to `/orm/v7` and `/v7/getting-started`. Both links are for staying on 7. +- Nothing under Getting Started or ORM links to the upgrade guide. It is reachable only through Guides > Upgrade Prisma ORM, where the PostgreSQL page is labelled "Migrate from Prisma 7 to Prisma 8" and the MongoDB page just "MongoDB" (#8238 relabels both). diff --git a/docs/orm-docs-audit/journeys.md b/docs/orm-docs-audit/journeys.md new file mode 100644 index 0000000000..f8ef9eb5c4 --- /dev/null +++ b/docs/orm-docs-audit/journeys.md @@ -0,0 +1,164 @@ +# Jobs readers come to the docs to do + +Twelve jobs. Each names who does it, what "done" looks like, what the docs must provide for it, and where the site's structure fails it. The jobs are the design input for `ia.md` (which node serves each job) and `changes.md` (which page or ORM change supplies what is missing). Verdicts describe the structure, not the state of any one sentence: **green** a page exists for the job and carries it end to end, **amber** the job can be done but the reader has to assemble it from several places or work around a gap, **red** no page is written for the job. + +## J1. Add Prisma ORM to an app I already started, with an empty database + +Who: Newcomer, sometimes Upgrader. The most common Discord complaint is this job. + +Done: models defined, tables created, one query returns rows, from inside the app they already have, and they can open the database and see those rows. + +What the docs must provide: one page that starts from "you have a project directory and no tables", runs `orm init`, edit the contract, `contract emit`, `db init`, one query, and shows `db.ts` and where `DATABASE_URL` comes from. + +Where the structure fails: the getting-started subtree is organised by which tool runs (`create-prisma` for new apps, `orm init` for "existing project"). The reader's situation, an app with no database yet, falls between the two: the scaffold page creates a new app, and the existing-project page assumes tables exist and starts with `contract infer`. The three commands that fit exist only as a snippet on the CLI overview. + +Verdict: **red**. IA node: "Add to an app you already have" (C2). + +## J2. Bring an existing database with data under Prisma ORM, then make the next schema change + +Who: Upgrader mostly, some Newcomers with a legacy database. + +Done: contract matches the database, database is signed, first query works, and `migration plan` for the next change produces one small migration rather than a full recreate. + +What the docs must provide: the adoption sequence end to end, including what to expect from `contract infer` (removed attributes, non-`public` schemas, defaults it cannot express, Temporal-backed timestamp types), what `db sign` checks and what its exit codes mean, and whatever makes the next `migration plan` chain from the signed state. + +Where the structure fails: the existing-project page stops at "sign, then query". The step that makes the next migration work (today: a baseline migration and `migration ref set db`) is taught only in the incremental upgrade guide, which this reader never sees. That step exists because `db sign` does not record where migrations start from; the design in `brief-db-ref-on-adoption.md` removes it. + +Verdict: **red**. IA node: "Adopt an existing database" (C3); ORM change D1. + +## J3. Rewrite Prisma ORM 7 queries in Prisma ORM 8 + +Who: Upgrader. + +Done: for every Prisma ORM 7 call in their code, they know the Prisma ORM 8 call or that it does not exist. + +What the docs must provide: one mapping page in four sections (schema and types, CLI commands, client API, not-yet list with a status per item), reachable from every ORM entry point. The not-yet list is the part readers ask for most: `createMany` and `skipDuplicates`, `findUniqueOrThrow`, atomic `increment`/`decrement`, case-insensitive filters, JSON path filters, `$transaction` arrays, the payload types. + +Where the structure fails: there is no node. The mapping exists as fourteen inline diff blocks across six `orm/` pages (twelve of them introduced with "For Prisma 7 users"), so the reader finds it by luck, and nothing admits what does not exist. The accessor change (`prisma.user` to `db.orm.public.User`) is stated once. + +Verdict: **red**. IA node: "Coming from Prisma 7" (C1, placed by A2); the not-yet list depends on D4. + +## J4. Decide whether to move to Prisma ORM 8 now + +Who: Upgrader, also a Newcomer choosing an ORM. + +Done: they know Prisma ORM 8 is a release candidate, when GA is expected, how long 7 is supported, what `npx prisma` now installs, and how to pin 7. + +What the docs must provide: one short page with those five facts, linked from the root page and from every "Using Prisma 7?" note. + +Where the structure fails: no node. The only signal is a note box saying Prisma 7 remains supported, with links for staying on 7. Nothing says "release candidate", gives a window, or explains that `npm install prisma` now resolves to 8 while `@prisma/client` resolves to 7. + +Verdict: **red**. IA node: "Release status" (C4); the npm tag question is D6. + +## J5. Migrate a whole Prisma ORM 7 app incrementally + +Who: Upgrader with a team and a production database. + +Done: both versions run side by side, routes move one at a time, migrations hand over, Prisma ORM 7 is removed. + +What the docs must provide: the five-phase guide that exists (`guides/upgrade-prisma-orm/postgresql`), reachable from the ORM entry points and from the mapping page, with a label that says what it is. + +Where the structure fails: the page is filed under Guides and nothing under Getting Started or ORM links to it. It is the best page on the site for this reader and the hardest to find. + +Verdict: content **green**, discoverability **amber**. IA: link it from "Coming from Prisma 7" and the ORM front door (A2). + +## J6. Model my data, especially types Prisma ORM 7 handled with `@db.*` + +Who: Newcomer and Upgrader. + +Done: they can write `Text`, `VarChar(100)`, `Uuid`, `Decimal`, enums, relations, and know which Prisma ORM 7 attributes are gone. + +What the docs must provide: the scalar table, the native types in type position, the `@default` generators, index and check options, and a Prisma ORM 7 attribute map (`@db.Text`, `@db.VarChar(n)`, `@db.Decimal(p,s)` to their type-position forms), plus a money and `Decimal` section. + +Where the structure fails: the right nodes exist (Data Modeling, Contract Authoring) but cover a fraction of the authoring surface, and the attribute map has no home. + +Verdict: **amber**. Pages: C1 (schema section), C14, C8. + +## J7. Set up my editor + +Who: Newcomer and Upgrader. + +Done: syntax highlighting and formatting work for `contract.prisma`. + +What the docs must provide: where the VS Code extension lives (open-vsx, not the Microsoft marketplace), that there is no bundled language server and the locally installed CLI is used, that `// use prisma-next` on line one is what the language server keys on, and `prisma contract format` as the fallback. + +Where the structure fails: no node. The facts were given once, in a Discord thread. + +Verdict: **red**. IA node: "Editor setup" (C5). + +## J8. Use advanced Postgres features + +Who: Newcomer and Upgrader on real projects. RLS, policies, expression and functional indexes, extensions such as pg_trgm. + +Done: they know whether the contract can express it, and how, or that they must manage it outside Prisma. + +What the docs must provide: one page: `@@rls` and the `policy_*` blocks, `@@index(expression:, where:, unique:, type:)`, `@@check` and `@noCheck`, `installExtension` / `createExtension` in migrations, `@@control` for tables Prisma should not manage. + +Where the structure fails: no node. All of it ships and none of it is documented; readers' agents invent APIs because there is nothing to read. + +Verdict: **red**. Page: C7. + +## J9. Name a model or result type for reuse + +Who: Newcomer and Upgrader. + +Done: `type User = ...` and the type of a query result, without reading `contract.d.ts`. + +What the docs must provide: a types page with model, result, where, and input types, each beside its Prisma ORM 7 name. The `Models` namespace and `Shape<>` are in `prisma/orm` `main` and ship with the next tagged release; `ResultType` for SQL builder plans exists today. + +Where the structure fails: no node. + +Verdict: **red**. Page: C6, on the release that carries the types. + +## J10. Stop Prisma from writing agent files into my repo + +Who: Newcomer and Upgrader. + +Done: one config line, and they know it. + +What the docs must provide: the one line (`skills: { agents: [] }` in `prisma.config.ts`) at the point where the scaffold introduces the files, linking to the configuration reference; and, once it exists, the scaffold-time opt-out. + +Where the structure fails: the reader meets the files on the scaffold pages and the answer lives on the CLI configuration page, which those pages must link. The tool itself has no prompt or flag, and `skills sync` with an empty list does not remove what it wrote. + +Verdict: docs **amber**, tool **red**. Tool change D3. + +## J11. Run Prisma ORM against a database on my machine + +Who: Newcomer, often a junior or a learner who does not want a hosted database yet. Reported on Discord by a reader who spent several days getting a local setup running and ended up reading example repositories on GitHub instead of the site. + +Done: a database runs locally, `DATABASE_URL` points at it, the first migration or `db init` has run, and the reader can see the tables and rows. + +What the docs must provide: one page in the ORM subtree that names the three ways to get a local database, in order of effort, and links or shows each: `prisma dev` (a local Prisma Postgres, no install), the Composer local stack (`dev`, which also runs the app), and a PostgreSQL the reader runs themselves, with a Docker Compose file and the matching connection string. It ends with how to look at the data: Studio against the local instance, or any Postgres client such as pgAdmin. + +Where the structure fails: the answers exist on the site, under Local development and Composer, but those are top-level platform sections. Nothing in the ORM subtree points at them except the quickstart's "Path A", which a reader who arrived at the ORM section from a search result never sees. A reader who wants Docker Compose finds only the Docker deployment guide, which is about shipping, not developing. + +Verdict: **red**. IA node: "A database on your machine". + +## J12. Learn from a runnable example + +Who: Newcomer. The same Discord reader: "I would recommend tutorials which can be cloned, run locally, and have a lot of comments in the code to understand what files need to be created and coded, which files are generated and why." + +Done: the reader has cloned a repository, run it against a local database, and can tell from comments in the code which files they wrote, which files Prisma generated, and what each command in `package.json` does. + +What the docs must provide: one small example repository per starting state (new app, app you already have, existing database), each with a README that says which files are yours (`contract.prisma`, `db.ts`, `prisma.config.ts`, your queries), which are generated (`contract.json`, `contract.d.ts`, `migrations/`), and the command that produces each generated file. Every quickstart and starting-state page links the matching repository. The monorepo's `examples/prisma-8-demo` is the seed. + +Where the structure fails: no page links a runnable repository. The quickstarts are copy-and-paste sequences with no way to check a broken local setup against a known-good one. + +Verdict: **red**. IA node: links from every starting-state page; no new node. + +## Summary + +| Job | Verdict | What serves it | +| --- | --- | --- | +| J1 existing app, empty DB | red | new node: Add to an app you already have (C2) | +| J2 existing DB, next migration | red | Adopt an existing database (C3) after D1 | +| J3 Prisma ORM 7 query mapping | red | Coming from Prisma 7 (C1, A2); D4 for the not-yet list | +| J4 should I move now | red | Release status (C4); D6 | +| J5 incremental upgrade | green / amber | links from the ORM entry points (A2) | +| J6 data types | amber | C1 schema section, C14, C8 | +| J7 editor | red | Editor setup (C5) | +| J8 advanced Postgres | red | Advanced Postgres (C7) | +| J9 types | red | Types (C6) on the next tagged release | +| J10 agent files | amber / red | the line on scaffold pages; D3 | +| J11 local database | red | A database on your machine (C22) | +| J12 runnable example | red | example repositories linked from every starting state (C23) | diff --git a/docs/orm-docs-audit/mental-model.md b/docs/orm-docs-audit/mental-model.md new file mode 100644 index 0000000000..0522ff5b83 --- /dev/null +++ b/docs/orm-docs-audit/mental-model.md @@ -0,0 +1,112 @@ +# The Prisma ORM 8 mental model, from first principles + +This is the source we write docs pages from. It explains why Prisma ORM 8 has the pieces it has, in the order a person meets them. Everything here was checked against the `prisma/orm` glossary, the rc.9 release notes, and the source under `packages/`. + +## The one idea + +Your application and your database each have an opinion about what the data looks like. In Prisma ORM 7 the schema file was the only written-down version, and the generated client trusted that the database matched it. Prisma ORM 8 makes the agreement explicit and checkable from both sides. Everything else follows from that. + +## Two things, not one: the contract and the schema + +The **contract** is what you write. It is your description of the models, fields, relations, and how they map to tables. It lives in your repository, in `contract.prisma` or in TypeScript. + +The **schema** is what the database actually has right now: the tables, columns, and indexes. + +Prisma ORM 7 called your file the schema. Prisma ORM 8 reserves that word for the database side, because the whole point is that the two can disagree. When a command or error says "schema", it means the database. + +Every Prisma ORM 8 operation is a relationship between these two: queries are typed against the contract, migrations move the schema toward the contract, and verification checks that the schema still satisfies the contract. + +## Emit: the build step that replaces `prisma generate` + +Prisma ORM 7 generated a client package from your schema. Prisma ORM 8 emits two plain files from your contract: + +- `contract.json`, a canonical description of the models, storage layout, and the database features it needs. +- `contract.d.ts`, the TypeScript types derived from it. + +The command is `prisma contract emit`. It is deterministic, so the same source always produces byte-identical output, and you commit both files. Think of the pair as `package.json` and its lockfile: the source is what you asked for, the artifacts are the resolved result. + +Why a build step at all: every other tool reads the artifacts, not your source. The query APIs read the types. The migration planner diffs two `contract.json` files. The runtime compares `contract.json` with the database. That is why "emit first" comes before almost every other command, and why forgetting it produces stale types or an empty migration plan. + +## The signature: how the database says which contract it satisfies + +Because emit is deterministic, hashing `contract.json` gives a short identifier for that exact contract, the way a commit hash identifies an exact state of your code. + +The database stores a small marker record, the **signature**, naming the contract hash it currently satisfies. Three commands write it: + +- `db init` creates the tables for the contract in an empty database and signs it. +- `db sign` checks that an existing database already matches the emitted contract and, if so, signs it. This is how you adopt a database you did not create with Prisma ORM 8. +- `db migrate` applies a migration and updates the signature to the migration's target hash. + +`db verify` reads it and reports **drift**: any disagreement between contract and schema. + +Why this exists: a deploy against an unmigrated database, or a migration run against the wrong database, is caught before any query runs, because the runtime and the migration runner both check the signature first. In Prisma ORM 7 the same mistake produced wrong results or a failed query later. + +## Queries: every query becomes a plan + +All query APIs are typed against `contract.d.ts`. Each query compiles to a **plan**, a plain data object holding the statement, its parameters, and metadata about what it touches. Running the plan is a separate step. + +Why: one pipeline. The ORM client, the SQL builder, and raw SQL all reach the database as plans, so middleware, telemetry, budgets, and authorization see every query in the same shape. A plan is data, so it can be inspected before anything touches the database. + +The three query surfaces on Postgres, from highest to lowest: + +1. The **ORM client**: `db.orm.public.User.where({ ... }).all()`. Model-based, coordinates several statements for `.include()`, hands back one typed result. Start here. +2. The **SQL builder**: `db.sql.public.user.select(...).where(...).build()`. Composable joins, grouping, and projections. One plan is exactly one statement. +3. **Raw SQL**: a whole statement, `db.raw.sql`\`...\`.returnsRow(spec)`, when the builder cannot express it, or a `fns.raw` fragment spliced into a builder query. Whole statements declare their row shape and are decoded; fragments are not. + +Why `db.orm.public.User` and not `prisma.user`: Postgres tables live in schemas, and Prisma ORM 8 addresses models through the schema namespace so a contract can span more than one. `public` is the default schema. Aliasing by assignment (`const User = db.orm.public.User`) is fine. On MongoDB the key is the collection's storage name (`db.orm.users`) and there is no SQL builder. + +## Migrations: edges between contract hashes + +A migration is a step from one contract hash to another. Together they form a graph, not a timestamped list. Every migration records the hash it starts from and the hash it produces. + +`migration plan` diffs two contracts and writes a migration package. The target is always your emitted contract. The origin is `--from` if you pass it, otherwise the `db` ref. With neither, the command refuses (`MIGRATION.PLAN_ORIGIN_UNKNOWN`) when migrations already exist on disk, and plans from an empty database only when the migrations directory is empty. + +The `db` **ref** is a named pointer to a contract hash, kept in the repo, meaning "the state I consider the database to be at". `db init` and `db update` move it when the connection comes from `prisma.config.ts`; with an explicit `--db` they move it only if `--advance-ref db` is also passed. `db migrate --advance-ref db` moves it. `db sign` does not today; the design in `brief-db-ref-on-adoption.md` makes it do so, so that adopting a database is one step. + +Why this matters more than it seems: a project with no `db` ref and no migrations on disk plans its first migration from empty, which proposes recreating every table. This is the trap for anyone who adopts an existing database with `db sign` and then runs `migration plan`. The fix today is a baseline migration plus `migration ref set db ` once, then `--advance-ref db` on each migrate. When the graph is empty and the `db` ref names a stored snapshot, `migration plan` writes the baseline for you. + +Why a graph and not a list: two branches can each add a migration, and the graph resolves the merge by hashes instead of by timestamp order. Partial failures are safe to retry because each edge has a verifiable precondition. + +`db update` is the development shortcut: diff the contract against the live database and apply the difference directly, no migration package, ref advanced for you. Use it while iterating; use `migration plan` plus `db migrate` when you want the change checked in. + +## The command vocabulary in one table + +| Command | Reads | Writes | When | +| --- | --- | --- | --- | +| `contract emit` | contract source | `contract.json`, `contract.d.ts` | after every contract change, before anything else | +| `db init` | emitted contract | tables, signature, `db` ref | empty database, first time | +| `db sign` | emitted contract, live schema | signature | adopting a database that already matches | +| `db verify` | emitted contract, live schema | nothing | check for drift | +| `db update` | emitted contract, live schema | schema diff, signature, `db` ref | development iteration | +| `migration plan` | origin contract, emitted contract | a migration package | change you want checked in | +| `db migrate` | migration packages, signature | schema, signature, `db` ref with `--advance-ref` | apply checked-in migrations | +| `migration ref set` | a hash | the ref | after a baseline, or to repoint | +| `contract infer` | live schema | a draft contract source | first contract for an existing database | + +## What moved from Prisma ORM 7, and why + +| Prisma ORM 7 | Prisma ORM 8 | Why | +| --- | --- | --- | +| `schema.prisma` | `contract.prisma` | the file describes an agreement, not the database | +| `prisma generate` | `prisma contract emit` | data artifacts instead of a generated client | +| `prisma migrate dev` | `db update` (dev) or `migration plan` + `db migrate` | plans are explicit and reviewable | +| `prisma db push` | `db update` | same job, now also signs | +| `prisma db pull` | `contract infer` | produces a contract draft | +| `new PrismaClient()` | `postgres({ contractJson, url })` in `db.ts` | the client is built from the contract at runtime | +| `prisma.user.findMany({ where })` | `db.orm.public.User.where(...).all()` | chainable, namespace-qualified | +| `findUnique` / `findFirst` | `.first()` | one terminal for one row | +| `{ data: { ... } }` | the fields directly | no wrapper | +| `take` / `skip` | `.limit()` / `.offset()` | renamed in rc.7 | +| `createMany` | `createAll` (rows back) or `createAndCount` (count back) | the return shape is in the name | +| `$transaction([...])` | `db.transaction(async tx => ...)` | callback form only | +| `@db.Text`, `@db.VarChar(n)` | `Text`, `VarChar(n)` in type position | native types are types, not attributes | +| `Prisma.UserGetPayload<...>` | `Shape<...>`, with the `Models` namespace | in `prisma/orm` `main`; ships with the next tagged release | +| `skipDuplicates`, `increment`, `findUniqueOrThrow`, `mode: "insensitive"`, JSON path filters | no equivalent yet (`.all().firstOrThrow()` covers the throw case) | say so plainly | + +## The eight words + +**Contract**: what you write. **Schema**: what the database has. **Emit**: build the contract into artifacts. **Hash**: the identifier of one exact contract. **Signature**: the database's record of which hash it satisfies. **Drift**: the two disagree. **Plan**: a compiled query or migration, as data. **Ref**: a named pointer to a hash, used as the migration origin. + +## What the model costs, honestly + +Two extra concepts before the first query: the emit step and the signature. Prisma ORM 7 had neither. The docs must pay that cost back on the same page by showing what it buys: a deploy that refuses to run against the wrong database, a migration plan you can read before it runs, and a query you can inspect before it executes. If a page introduces the concept without the payoff, cut the concept from that page. diff --git a/docs/orm-docs-audit/naming.md b/docs/orm-docs-audit/naming.md new file mode 100644 index 0000000000..c288bf19c8 --- /dev/null +++ b/docs/orm-docs-audit/naming.md @@ -0,0 +1,37 @@ +# Naming: the product is "Prisma ORM" + +## The rule + +"Prisma ORM" names the product. A version number appears only when two versions are being told apart. So: + +- "Add Prisma ORM to an existing project", not "Add Prisma 8 to an existing project". +- "Prisma ORM emits the contract", not "Prisma 8 emits the contract". +- "Coming from Prisma ORM 7" and "Migrate from Prisma ORM 7 to 8" keep the numbers, because the contrast is the point. +- The release status page says "Prisma ORM 8 is a release candidate; Prisma ORM 7 is supported until ...". +- The Prisma 7 docs trees (`orm/v7`, `(index)/v7`, `guides/v7`, `cli/v7`) keep their version label in the nav, because that is what they are. + +Why: the Prisma brand identity is being separated from the ORM. Prisma is the company and the platform; the ORM is one product on it. Calling the ORM "Prisma 8" ties the whole brand to one product's release cycle, and it leaves no name for the platform, where "Prisma" alone already means the stack. So the ORM is never "Prisma" plus a version; it is "Prisma ORM", and that name is what gets a version number. + +## How to do it + +One pass over the whole site, as its own PR. Not a global replace: each of the 109 files needs a read, because "Prisma 8" in a contrast sentence becomes "Prisma ORM 8" and elsewhere becomes "Prisma ORM". The 96 titles are the visible part and go first; they are the sidebar and the browser tab. + +## What stays as is + +- Identifiers: `prisma-next.md`, `// use prisma-next`, `@prisma/orm-postgres`, `/studio/prisma-next`, package names, error codes. 39 occurrences of `prisma-next` as an identifier; none as prose. +- The "Prisma 7" group at the bottom of Getting Started and the v7 doc trees. +- Release notes and changelogs, which name versions by nature. + +## Scope on the site (2026-09-10, outside the v6 and v7 trees) + +Counted with `git grep -o` over the `.mdx` and `meta.json` files under `apps/docs/content/docs/`, excluding every `v6/` and `v7/` directory, on `main` as of 2026-09-10 (`684dcbf6`). + +| Term | Occurrences | Files | +| --- | --- | --- | +| "Prisma 8" | 679 | 109 | +| "Prisma 7" | 135 | 33 | +| "Prisma ORM" | 487 | 95 | + +By area: `orm/` 221, `guides/` 196, `(index)/` 131, `cli/` 77. 96 frontmatter titles contain "Prisma 8": 7 `title:` values, which are also the sidebar labels of those pages, and 89 `metaTitle:` values. No `meta.json` group title does. + +The common sentence shapes ("Add Prisma 8 to an ...", "Prisma 8 is the ...", "Prisma 8 app with ...", "Prisma 8 skills for ...") all take "Prisma ORM" as a drop-in replacement. The contrast sentences keep the number and add "ORM". diff --git a/docs/orm-docs-audit/personas.md b/docs/orm-docs-audit/personas.md new file mode 100644 index 0000000000..a7244b5b6c --- /dev/null +++ b/docs/orm-docs-audit/personas.md @@ -0,0 +1,33 @@ +# Who reads the Prisma ORM docs + +Four readers arrive. The first two produce almost all the complaints (Discord, GitHub, and support, August and September 2026). The docs outside `orm/` are built for the third. The fourth never complains. + +## The Newcomer + +Has never used Prisma. Has an app, or is about to start one, and wants typed queries against a database. Arrives from npm, a search result, or a framework tutorial. Often already ran `bun init` or `npm create next-app` before looking for an ORM, so scaffolding tools that create the app for them are the wrong shape. + +What they need on the first visit: install, define models, connect, run one query, in one sitting. They will accept one new idea (the contract) if it pays off on the same page. They will not read a philosophy page first. + +How they judge the docs: can I copy the example and does it run. + +## The Upgrader + +Has used Prisma ORM 5, 6, or 7. Has a running app, a database with data in it, and a migration history. Arrived because `npx prisma` installed Prisma ORM 8 without them choosing it, or because they saw the announcement. Most of the complaints are from this reader. + +What they need: first, a straight answer on whether to move now and how to stay on 7 if not. Second, the Prisma ORM 8 name for every Prisma ORM 7 thing they know, or a plain statement that it does not exist yet. Third, the exact sequence to bring an existing database under Prisma ORM 8 control and make the next migration work. + +How they judge the docs: against the mental model they already have. Every page that explains contracts before answering "where did `findMany` go" costs trust. + +## The Builder + +Wants to ship a product on the Prisma platform: Composer, Compute, Postgres, Storage. May be technical, may lean on a coding agent. The ORM is one component. Happy to have tools scaffold the app and happy to hand a prompt to an agent. + +The docs root, the framework guides, the agent prompts, and the full-stack tutorial serve this reader well. The problem is that their content occupies the entry points the Newcomer and the Upgrader land on. + +## The Agent + +Reads the shipped `prisma-8` skill, `llms.txt`, and the `.md` rendition of any page. Never complains. The skill is out of scope for the docs work except where the human pages feed into it; its own defects are in `brief-skill-staleness.md`. + +## What this means for the docs + +The ORM entry points must serve the Newcomer and the Upgrader before the Builder. Today the order is reversed: the root page is the Builder's, the ORM landing opens with an agent prompt, and the Upgrader gets a two-line note saying Prisma ORM 7 still exists. diff --git a/docs/orm-docs-audit/plain-language.md b/docs/orm-docs-audit/plain-language.md new file mode 100644 index 0000000000..4f8ae3498c --- /dev/null +++ b/docs/orm-docs-audit/plain-language.md @@ -0,0 +1,63 @@ +# Plain language: the rule for every page + +The reader is an ordinary developer who has used Prisma ORM before and has never seen the Prisma ORM 8 source code. Every sentence must be one they could restate in their own words on first reading. This is the largest complaint about the current docs and it overrides any other style preference. + +## The rule + +1. Say what the reader does and what happens. Not what the system is. +2. One idea per sentence. If a sentence needs a parenthetical, split it. +3. Use the reader's words for things: "the command-line tool", "the library your app imports", "your schema file", "the call that runs the query", "an error with a code". +4. A term from the source code may appear on a page only if the docs glossary already defines it for readers, or the sentence that first uses it says what it means in plain words. Otherwise use the plain words and drop the term. +5. Explain a mechanism only when the reader needs it to act. If they do not, cut it. +6. No sentence is finished if the reader has to guess what a word refers to. + +## Banned words and their replacements + +These come from `packages/` and the release notes and mean nothing to readers. Do not use them in prose; the replacement is the meaning. + +| Do not write | Write | +| --- | --- | +| terminal, terminal call, terminal method | the call that runs the query (`.all()`, `.first()`, `.create(...)`) | +| codec | how the value is stored and read; or just name the type | +| envelope, structured envelope, structured error | an error with a code (`RUNTIME.NO_ROWS`) | +| type position, "in type position" | written as the type (`VarChar(255)` instead of `@db.VarChar(255)`) | +| runtime (as a noun for the library) | the library your app imports; or `db` | +| release line, release cycle, runs ahead | released separately, on its own schedule | +| lane (SQL lane, raw lane) | the raw SQL API; the query builder | +| facade | the client; `postgres(...)` | +| junction model, join model | the model for the join table | +| plan, query plan | the query, before it runs; or the built query | +| emit, emitted artifacts (unexplained) | `prisma contract emit` writes `contract.json` and `contract.d.ts` | +| signature, marker (unexplained) | the record in the database of which contract it matches | +| ref (unexplained) | a named pointer to a migration state; explain on first use | +| capability | which database features the contract needs | +| buffers, buffered, materializes | loads every row into memory first | +| surface, API surface | the methods; the commands | +| lowered, lowering | turned into SQL | +| namespace-qualified, namespace coordinate | `db.orm.public.User` (`public` is the PostgreSQL schema) | +| reducer, refinement callback | a way to count or sum related records; a way to filter related records | +| dotted codes | a code such as `RUNTIME.NO_ROWS` | +| envelope config, CLI envelope | the config file | +| dev loop | while you are iterating | +| brownfield, greenfield | an existing database; a new project | +| deterministic | the same input always produces the same output | +| idempotent | safe to run more than once | +| topology, graph (for migrations, unexplained) | the chain of migrations | + +Also banned: "unblocks", "surfaces", "carries", "wires up", "sits", "lives at" (say "is at"), "spells", "hands back", "rides on", "routes on", "keys on", "the shape", "story" (as in "the `DATABASE_URL` story"). + +## Sentence shapes to avoid + +- A noun with three or more modifiers: "per-request cursor-enabled serverless facade". +- "X, so Y, which Z": three claims chained; split. +- A caveat in parentheses mid-sentence; make it its own sentence or cut it. +- Passive voice for what the reader does: "the version is pinned" becomes "lock the version". +- Starting with the mechanism: "Because emit is deterministic, hashing..." becomes what the reader sees. + +## Process + +1. Write the page from the facts. +2. Fact review against the source (as today). +3. Reader review: an agent with the persona above and only this file reads the page and marks every sentence it cannot restate and every word it had to guess. Fix every mark. Repeat until clean. +4. Run `check-plain.sh` on the changed files before pushing. +5. Will does the final pass. diff --git a/docs/orm-docs-audit/slice-corrections.md b/docs/orm-docs-audit/slice-corrections.md new file mode 100644 index 0000000000..b283c636cb --- /dev/null +++ b/docs/orm-docs-audit/slice-corrections.md @@ -0,0 +1,22 @@ +# The corrections slice: what shipped, and the decisions behind it + +Before any restructure, every factual error on the published Prisma ORM 8 pages was fixed in place. Three PRs to `prisma/web`, each reviewed independently against the rc.9 source, each commit citing the line it was verified against: + +| PR | What | Size | +|---|---|---| +| #8236 | Names, flags, paths, codes, and printed output that no longer exist at rc.9, replaced by the form that does | 40 files, merged 2026-09-10 | +| #8237 | Pages whose explanations were wrong (the `db` ref, middleware, raw SQL, relations, aggregates, the existing-project pages) | 31 files, merged 2026-09-11 | +| #8238 | Facts on the getting-started pages and guides (Node floor, MongoDB replica set, create-prisma flags and scripts, the skills page, the tutorial, streaming, `npm create prisma@latest`) | 32 files, open as of 2026-09-11 | + +Out of scope by design: the sixteen Prisma 7 guides under Prisma ORM 8 paths (A7, since landed as #8242), new content (C), the agent skill (D14), the monorepo docs (D17), and the "Prisma 8" to "Prisma ORM" rename. + +## Four decisions taken along the way + +1. **Agent files.** The scaffold pages carry one line saying Prisma writes skill files for coding agents and that `skills: { agents: [] }` in `prisma.config.ts` stops it. That is the only correct answer until create-prisma grows an opt-out flag (D3), at which point the line is replaced. +2. **Node floor.** The site says "Node.js 22.18 or newer (on the 24 line, 24.11 or newer); 24 recommended". Checked by running rc.9 end to end (`contract emit`, `db init`, ORM create and read, `for await`, SQL builder) under Node 22.12.0, 22.18.0, and 22.22.3: all pass. Node 22 is Maintenance LTS until 2027-04-30; 23 reached end of life 2025-06-01; 24 is Active LTS until 2028-04-30. The `prisma` CLI and create-prisma both declare `>=22.18.0`; only the `package.json` create-prisma writes into a project narrows to `^22.18.0 || >=24.11.0`, which is why the 24 sub-range is stated. The monorepo's `docs/Supported Versions.md` says 24 and is wrong about what runs. +3. **Streaming.** The reading-data page promised that `for await` streams rows with a flat memory footprint. It does not on `postgres()`: the facade creates its driver with `cursor: { disabled: true }`, so the whole result is fetched before iteration and only decoding is per row. Measured on a 400,006-row table under Node 22.18: `for await` that breaks after the first row took 186 ms and grew the heap by 64 MB (every row fetched), against 824 ms for `await ...all()` (fetch plus decode). The page now says what happens and points large-result readers at `limit()` or the serverless facade, which reads through a cursor in batches of 100. Whether `postgres()` should expose cursors is D8. +4. **The `--db` quirk.** `db init` and `db update` advance the `db` ref only when the URL comes from config; with `--db` they do not unless `--advance-ref db` is also passed. Documented as-is, with the flag shown on every dev-loop example that passes `--db`; deploy and CI examples are left without it on purpose. Whether the rule should change is D9. + +## Two audit claims the source overruled + +Left as the pages had them: `migration plan` can raise `MIGRATION.NO_TARGET` after a rollback cycle (the planner does resolve a leaf), and `min` / `max` over a `DateTime` column type-checks at rc.9 (there is no `pg/timestamptz@1` codec; `DateTime` is the temporal codec, which `min`/`max` accept).