From 079a580503cd91ef2ed86244cbdf365eab724262 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 24 Sep 2026 15:43:11 +0200 Subject: [PATCH 1/8] docs: TypeScript module settings for Prisma 8 projects The orm-init-prisma7-detection project's docs brief becomes a reference page: what a Prisma 8 project needs from tsconfig.json, which settings fit which kind of project, what prisma orm init writes, and how the CommonJS option was verified. The guide changes it asked for are TML-3291. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/README.md | 1 + docs/reference/typescript-module-settings.md | 35 ++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 docs/reference/typescript-module-settings.md diff --git a/docs/README.md b/docs/README.md index 1bb592b0a441..0de8ee15c3fe 100644 --- a/docs/README.md +++ b/docs/README.md @@ -28,6 +28,7 @@ This directory contains the primary documentation for the repository. - [Integer representation types](./reference/integer-representation-types.md) — choosing `BigInt`, `BigIntNumber`, or `UnboundedInt` by target, storage, application value, and aggregate behavior - [Aggregate descriptor guide](./reference/aggregate-descriptor-guide.md) — how a target or extension declares aggregate operations and their result codecs (`SqlAggregateDescriptor` on `types.aggregateDescriptors`) - [PSL editor tooling for tagged literal defaults](./reference/psl-editor-tooling-tagged-literals.md) — what the language server, formatter and highlighting need for `` sql`...` `` and `` json`...` `` defaults, and what is not done yet +- [TypeScript module settings for Prisma 8 projects](./reference/typescript-module-settings.md) — the `tsconfig.json` settings a Prisma 8 project needs, which fit which kind of project, and what `prisma orm init` writes - [Naming model and result types](./reference/model-and-result-types.md) — `Models`, `Scalars`, `Shape`, and `ResultType` on ORM queries - [Mongo Pipeline Builder](./reference/Mongo%20Pipeline%20Builder.md) — typed builder for MongoDB aggregation pipelines, reads, writes, and find-and-modify - [`migration graph --tree` rendering](./reference/migration-graph-rendering.md) — condensed annotated-tree rendering for offline migration topology diff --git a/docs/reference/typescript-module-settings.md b/docs/reference/typescript-module-settings.md new file mode 100644 index 000000000000..cd94d6a5a97d --- /dev/null +++ b/docs/reference/typescript-module-settings.md @@ -0,0 +1,35 @@ +# TypeScript module settings for Prisma 8 projects + +Which `tsconfig.json` settings a Prisma 8 project needs, which ones work for which kind of project, and what `prisma orm init` writes. Guides that show a tsconfig for a Prisma 8 project, including the Prisma ORM 7 to 8 upgrade guides, should follow this page. + +## What Prisma 8 requires + +- **An ESM-capable `module` setting.** The `@prisma/orm-*` packages ship ES modules only; every export is an `.mjs` file. +- **`resolveJsonModule: true`.** The scaffolded `db.ts` imports the emitted `contract.json`. +- **The import attribute, where the file is an ES module.** `db.ts` imports the contract with `with { type: 'json' }`. TypeScript accepts that attribute only when `module` is `esnext`, `node18`, `node20`, `nodenext`, or `preserve`, and only from TypeScript 5.3. +- **The emitted declarations in `include`,** so `contract.d.ts` is part of the program. + +Prisma 8 does not require `NodeNext`. Under `"module": "NodeNext"` with `"type": "module"` in `package.json`, TypeScript requires a `.js` extension on every relative import, because Node's ESM loader requires one at runtime. That is Node's rule, and it only matters to projects that compile with `tsc` and run the output with plain `node`. + +## Which settings to use + +| Project | Settings | Notes | +| --- | --- | --- | +| Already on an ESM-capable `module` | Keep it; add `resolveJsonModule` and the `include` entry | Nothing else changes. | +| CommonJS, staying CommonJS | `"module": "nodenext"` and `resolveJsonModule`; `db.ts` imports the JSON without the `with { type: "json" }` attribute | No `"type": "module"`, no `.js` extensions, no import attribute. | +| Running through `tsx`, `vite`, `next`, `esbuild`, or another bundler | `"module": "preserve"`, `"moduleResolution": "bundler"`, `resolveJsonModule` | Accepts extensionless imports and the import attribute. This is what `prisma orm init` writes. | +| Compiled with `tsc` and run with plain `node` as ES modules | `"module": "NodeNext"` | Every relative import needs its `.js` extension. | + +## What `prisma orm init` writes + +- It merges `module: 'preserve'`, `moduleResolution: 'bundler'`, and `resolveJsonModule: true` into an existing `tsconfig.json` (`packages/1-framework/3-tooling/cli/src/commands/init/templates/tsconfig.ts`). It does this whatever the project's current `module` setting is, and reports only that it updated the file. +- It scaffolds `db.ts` with the `with { type: 'json' }` import (`templates/code-templates.ts`). +- When `package.json` declares a `"type"` other than `"module"`, it keeps the user's value and warns that `db.ts` will not load under it (`commands/init/hygiene-package-scripts.ts`). For a CommonJS project, the attribute-free import from the table above would load in both module systems. + +## How the CommonJS option was verified + +Tested on Node 24.13 with TypeScript 5.9.3 against the workspace build: + +- At runtime, a plain `.cjs` file can `require("./contract.json")` and `require("@prisma/orm-postgres/runtime")`, and `postgres({ contractJson, url })` constructs the client. Node's `require(esm)` loads the ESM-only package because it has no top-level `await`. +- For types, a `.cts` file, or a `.ts` file in a package without `"type": "module"`, that imports `@prisma/orm-postgres/runtime` and `./contract.json` typechecks under `"module": "nodenext"` with no import attribute and no `.js` extensions. +- `"module": "node16"` rejects the same file with TS1479, and `"moduleResolution": "node10"` cannot resolve the package at all. From 9d98329a55fbfaf4370e0738c677ea832f856b5c Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 24 Sep 2026 15:43:11 +0200 Subject: [PATCH 2/8] docs(cli): the orm init README records the design constraints of the Prisma 7 path The decisions from the orm-init-prisma7-detection design notes that the README did not state yet: no separate upgrade command, what init reads from a Prisma 7 config, the src/prisma layout, the scope of the side-by-side consent, tsconfig handling, no cutover step, and the version limit of the in-process schema check. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- packages/1-framework/3-tooling/cli/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/1-framework/3-tooling/cli/README.md b/packages/1-framework/3-tooling/cli/README.md index 50c5a89cd639..0cfe99507375 100644 --- a/packages/1-framework/3-tooling/cli/README.md +++ b/packages/1-framework/3-tooling/cli/README.md @@ -99,6 +99,16 @@ The second question is the consent token; `--confirm ` answers it non-inter It then writes `prisma.config.ts` with `contract: prisma7Schema("")` and `output: "src/prisma"`, `src/prisma/db.ts`, and a `prisma-8.md` that describes the transition loop; no starter schema is written and `prisma/` stays byte-identical. The next steps are the transition routine: set `DATABASE_URL`, `prisma db sign`, move routes one at a time, and re-run `prisma contract emit` then `prisma db sign` after each `prisma7 migrate dev`. +**Design constraints on the Prisma 7 path:** + +- There is no separate upgrade command. Init already installs, scaffolds, and emits. A command that automated the whole upgrade guide could not find its inputs reliably in arbitrary projects (computed config values, multi-file schemas, monorepos, CI files that call `prisma migrate`), and the Prisma 7 contract source removes the need for a schema converter. +- From a Prisma 7 config init reads only `schema`. The new config connects with `process.env['DATABASE_URL']!`: copying the Prisma 7 `datasource.url` expression would need a TypeScript rewrite of user code, and matching its resolved value back to an environment variable assumes the URL came from one. +- Init's files go under `src/prisma/`, where a fresh init puts them, so an upgraded project is shaped like a new one. `prisma/` belongs to Prisma 7. +- The side-by-side edits are the only edits init makes to files it did not write, all under one consent. Renaming the config alone would leave scripts calling a `prisma` binary that is now Prisma 8, and `@prisma/client` moves with the Prisma 7 CLI because Prisma 7 requires both at the same version. +- `package.json#type` and `tsconfig.json` are handled as on a fresh init; see [TypeScript module settings for Prisma 8 projects](../../../../docs/reference/typescript-module-settings.md). +- There is no cutover step. The next steps list only what the user runs right after init. +- The schema check runs the target package's source inside the init process, so it uses the control stack of the CLI that invoked init, not the project's installed `prisma`. Published releases pin the two to the same version; an older CLI running against a newer target package can refuse a valid schema. + **Exit codes:** - `0`: set up (and, unless skipped, installed and emitted) - `2`: precondition — an invalid flag, a refusal on the Prisma 7 path (`CLI.INIT_FLAG_CONFLICT`, `CLI.INIT_PRISMA7_SCHEMA_INVALID`, `CLI.INIT_PRISMA7_CONFIG_COLLISION`, `CLI.INIT_PRISMA7_CONFIG_UNREADABLE`, `CLI.INIT_PRISMA7_TARGET_MISMATCH`, `CLI.INIT_PRISMA7_PROVIDER_UNSUPPORTED`, `CLI.INIT_PRISMA7_SCHEMA_REFUSED`, `CLI.INIT_PRISMA7_SOURCE_UNAVAILABLE`), a consent not granted, or a write that failed; nothing is written before a refusal, and the schema check's install is the only change before one From 67f2b5d5d51ce3a21d5b57b032cef9b3d49de1e3 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 24 Sep 2026 15:43:12 +0200 Subject: [PATCH 3/8] drive: land the orm-init-prisma7-detection retro lessons failure-modes.md gains F32 (a brief restates a ruling more strictly than the operator meant it) and F33 (a test fake returns what the real surface never returns). The QA pre-run section says to merge main before running QA against published builds made from main. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- drive/calibration/failure-modes.md | 34 ++++++++++++++++++++++++++++++ drive/qa/README.md | 2 ++ 2 files changed, 36 insertions(+) diff --git a/drive/calibration/failure-modes.md b/drive/calibration/failure-modes.md index 646f4b125318..31833fa1bccf 100644 --- a/drive/calibration/failure-modes.md +++ b/drive/calibration/failure-modes.md @@ -644,3 +644,37 @@ Per-repo stop conditions beyond the canonical ones: - An implementer's claim "predates this dispatch" about a journey is verified by the reviewer with `git log -1 origin/main -- ` and by asking whether `dist` was rebuilt after the last source change. **Reference incident.** remove-dbgenerated slice C dispatch 1 (2026-09-22): `infer-roundtrip-fidelity.prisma7-defaults` failed on a stale `family-sql` dist; the reviewer accepted the "red on main" claim in round 1 and corrected it in dispatch 3 after the implementer rebuilt the package. + +### F32. A brief restates an operator ruling in its own words, and the restatement is stricter than the ruling + +**Symptom.** An implementer builds work, or asks the operator a round of questions, on a premise the operator never held. The operator's answer to the first question is "this is wrong", and every question built on the premise has to be asked again. + +**Detection signal.** + +- The brief or design notes state a ruling as a rule ("init must be target-agnostic") without the operator's words, date, or context. +- Several open questions in the brief depend on that one rule. +- The rule forbids something the codebase already does in many places, and the brief treats those places as debt to remove. + +**Mitigation.** + +- Record a ruling as a quote, with speaker and date, and write the interpretation separately under it. +- In the first question round, restate the interpretation in one sentence and ask the operator to confirm it before asking any question that depends on it. + +**Reference incident.** orm-init-prisma7-detection (2026-09-23): the ruling "Our init command cannot be target specific" was recorded as "init must be target-agnostic". The brief planned to stop choosing the target from the Prisma 7 schema's provider and asked whether to remove 17 existing target branches. Will's intent was narrower: init must not be coupled to one database, and choosing among known targets is fine. + +### F33. A test double returns what the real dependency never returns, so the test covers a path production cannot reach + +**Symptom.** A test passes, and the behaviour it names never happens for a user. The fake that drives the test answers with a value or outcome the real implementation cannot produce. + +**Detection signal.** + +- A fake implements an interface whose real implementation lives in another package (for example `PromptSurface` from `@prisma/cli-engine`). +- The fake's return values were chosen for the test's convenience, not read from the real implementation. +- A reviewer asks "can the real surface return this?" and nobody has checked. + +**Mitigation.** + +- When writing a fake for another package's surface, read the real implementation and make the fake return and throw exactly what it does. When the behaviour depends on the real surface's outcomes, drive it through that package's own test harness (for the CLI engine, `createTestCli` with scripted `answers`). +- The reviewer checks each fake's possible outcomes against the real implementation, not only the code under test. + +**Reference incident.** prisma/orm#30291 (2026-09-24): a scripted prompt returned `false` from `consent`, and a test showed that a declined consent printed the command to remove packages init had installed. The engine's `consent` with a token never returns `false`: it returns `true` or throws `CLI.CONSENT_REQUIRED`, a token mismatch, or `CLI.PROMPT_CANCELLED`. Real users never saw the command. An independent review found it; the fix attached the command to whatever error follows the install. diff --git a/drive/qa/README.md b/drive/qa/README.md index af681cce8bea..6d135e5d3a46 100644 --- a/drive/qa/README.md +++ b/drive/qa/README.md @@ -27,6 +27,8 @@ Manual QA reads: A clean pre-QA tree means `pnpm typecheck && pnpm test:packages && pnpm fixtures:check` all green. QA against an unverified tree wastes the runner's time discovering broken assertions that a 1-minute `pnpm test:packages` would have surfaced. +When the QA project installs published builds made from `main` (for example a package's `dev` dist-tag), merge the current `origin/main` into the branch and rebuild before the run. A branch that is behind `main`, run against packages built from a later `main`, fails in ways neither side fails alone. (Added 2026-09-24: a Prisma 7 schema check on a branch based on a week-old `main` refused `@default(false)`, because the branch's CLI lacked a lookup the `dev` target package expected. The same run on the merged branch passed.) + ## Sample coherence, not just placement, for content-preamble dispatches Symptom seen during the drive trial (project `orchestrator-role`): a dispatch added a uniform delegated-execution preamble across 19 atomic skills. Manual-QA dry-run sampled preamble placement (right line, right format, cross-link resolves) — found nothing wrong. Reviewer (CodeRabbit) then surfaced three "critical" findings on stop-condition coherence: the preamble's "STOP. Dispatch on Read/Grep/Glob" rule contradicted skill bodies whose Step 2 explicitly required Read/Grep/Glob investigation as the default first step. From 22957049237399b28fd9b0b3c1c0fb89a0bfd3a0 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 24 Sep 2026 15:43:12 +0200 Subject: [PATCH 4/8] chore: delete projects/orm-init-prisma7-detection The project's only PR, prisma/orm#30291, merged. Its decisions are in the CLI README, its docs brief is docs/reference/typescript-module-settings.md, its deferred work is TML-3291 and TML-3292, and its retro lessons are in drive/. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../design-notes.md | 84 ------------ .../docs-brief-module-settings.md | 42 ------ .../manual-qa-reports/2026-09-15-fable.md | 124 ------------------ .../manual-qa-reports/2026-09-24-opus.md | 110 ---------------- .../orm-init-prisma7-detection/manual-qa.md | 90 ------------- projects/orm-init-prisma7-detection/plan.md | 46 ------- .../slices/01-postgres/plan.md | 29 ---- .../slices/01-postgres/pr-body.md | 109 --------------- .../slices/01-postgres/spec.md | 95 -------------- projects/orm-init-prisma7-detection/spec.md | 115 ---------------- 10 files changed, 844 deletions(-) delete mode 100644 projects/orm-init-prisma7-detection/design-notes.md delete mode 100644 projects/orm-init-prisma7-detection/docs-brief-module-settings.md delete mode 100644 projects/orm-init-prisma7-detection/manual-qa-reports/2026-09-15-fable.md delete mode 100644 projects/orm-init-prisma7-detection/manual-qa-reports/2026-09-24-opus.md delete mode 100644 projects/orm-init-prisma7-detection/manual-qa.md delete mode 100644 projects/orm-init-prisma7-detection/plan.md delete mode 100644 projects/orm-init-prisma7-detection/slices/01-postgres/plan.md delete mode 100644 projects/orm-init-prisma7-detection/slices/01-postgres/pr-body.md delete mode 100644 projects/orm-init-prisma7-detection/slices/01-postgres/spec.md delete mode 100644 projects/orm-init-prisma7-detection/spec.md diff --git a/projects/orm-init-prisma7-detection/design-notes.md b/projects/orm-init-prisma7-detection/design-notes.md deleted file mode 100644 index 42909e95a871..000000000000 --- a/projects/orm-init-prisma7-detection/design-notes.md +++ /dev/null @@ -1,84 +0,0 @@ -# Design notes - -Decisions with their reasons, and what was rejected. The spec states the outcome; this file states why, so nobody re-derives the alternatives. - -## Principles - -1. **`orm init` behaves like `git init`.** It sets up what Prisma 8 needs to operate in the project and stops. A database may not be reachable when init runs, so nothing in init requires one. Init is not "migrate me from an earlier Prisma". -2. **Nothing is silently reinterpreted.** A Prisma 7 schema is read as one only when the user said so, by flag or by answering a question. The flag names the dialect (`--from-prisma7-schema`) because init has to write `prisma7Schema(...)` rather than treat the file as Prisma 8 PSL. -3. **Prisma 7 inputs are defaults.** The config and schema answer init's own questions (which family, which file). Any flag given overrides them. -4. **Init owns only what it writes,** with one deliberate exception below. - -## Decisions - -### D1. The `prisma upgrade` orchestrator is dropped - -Validation of the original spec showed it could not find its inputs reliably in arbitrary projects and that the parallel project solves the hard part better by reading `schema.prisma` directly. Init already does install, scaffold, and emit; the detection mode is the remaining gap. - -### D2. Init never signs - -Signing needs a database. Init's promise is "set up", not "working against your database". `db sign` is the first printed next step, the same way a fresh init hands off `db init`. - -### D3. Side-by-side setup is part of init, with consent - -Will's ruling (2026-09-14): when an earlier Prisma CLI is installed, init offers to install the Prisma 7 npm package and move the `prisma` CLI to version 8. Reason: init installs `prisma@latest`, which in an untouched Prisma 7 project replaces the Prisma 7 CLI; without the swap, `prisma migrate dev` would run Prisma 8 afterwards. The alternative, stopping and telling the user to do the guide's section 1 and re-run init, was rejected because nobody should have to run init twice. - -Two consequences were decided by the orchestrator inside that ruling and are open to veto: - -- **The same consent covers the config rename and the script rewrite.** The swap alone leaves `prisma.config.ts` occupied by Prisma 7's config, which init needs, and leaves scripts calling a `prisma` binary that is now Prisma 8. Both edits are the guide's own section 1 and are deterministic: rename to `prisma7.config.`, change the `prisma/config` import to `@prisma/prisma7/config`, replace `prisma ` with `prisma7 ` in script commands. -- **`@prisma/client` moves to the Prisma 7 CLI's version.** Prisma 7 requires CLI and client at the same version and `@prisma/prisma7` publishes one version. `prisma7 generate` is printed as a next step. - -This is the one place init edits files it did not write. It is bounded to the guide's section 1 and happens only after the consent token. - -### D4. The connection line is `process.env['DATABASE_URL']` - -Evaluating the Prisma 7 config yields the resolved URL, which must not be written into a config file, and matching it back to an environment variable assumes it came from one. Copying the source expression needs a TypeScript rewrite. Both deferred; init writes what it writes today. - -### D5. Layout is `src/prisma/` - -`db.ts`, `contract.json`, and `contract.d.ts` go where a fresh init puts them, so an upgraded project ends up shaped like a new one. `prisma/` belongs to Prisma 7 and is deleted at cutover. The guide's `prisma8/` and the example app's `generated/prisma8/` were not adopted. - -### D6. Mongo is its own slice and waits (superseded by D10) - -Prisma 7 has no Mongo connector; the Mongo guide is a Prisma 6 port. The parallel project's slice 2 defines the Mongo source. Init does not refuse Mongo by name: it installs the chosen target package and uses its `prisma7Schema` when the package exports one (D10). - -### D7. `"type": "module"` and tsconfig handling are unchanged - -Will: leave init alone, it does what it does now. The docs brief in this directory covers the CommonJS guidance for the `prisma/web` guides. - -## Rejected - -- **Emitting a `contract.ts` module or any generated code beside `contract.json`.** The product emits a data artifact plus types (`docs/Architecture Overview.md`). Not to be re-proposed. -- **Signing inside init.** See D2. -- **Detecting a Prisma 7 config by its import text.** The engine already has the marker check; init reuses it. -- **Reading `datasource.url` and matching it to an environment variable.** See D4. -- **Stop-and-re-run after the user does section 1 by hand.** See D3. -- **A top-level `prisma upgrade` verb.** The unified CLI owns top-level names; `init` already exists and the grammar assigns detection to it. - -## Open - -- Whether `contract convert` should be named in init's next steps once it exists is the parallel project's call at its close-out. -- The `prisma/config` import (init writes `@prisma/cli-engine` today; the published `prisma` package re-exports it as `prisma/config`) is an orphan slice outside this project. - -### D8. Init checks the schema before it edits anything (added 2026-09-16, resolved by D10) - -Manual review after the end-to-end proof: init renamed the config, swapped `prisma`, and rewrote scripts before `contract emit` could report a construct the Prisma 7 source refuses. For a construct with no fix (a view, `Unsupported(...)`), the user's project was changed for nothing. The source's refusals are permanent for some constructs, so waiting for them to be lifted is not an option; init must find out first. - -Checking needs the target package: the refusal rules ship only in `@prisma/orm-postgres`, and the CLI is family-blind, so it carries none of that code and reaches it only through an installed target package. Options: (1) install the target package into a temporary directory, check there, delete it, so a refused schema leaves the project untouched, at the cost of one extra download on success; with `--skip-install` the check is skipped with a warning; (2) install it into the project first, leaving one unused dependency on refusal; (3) bundle the source into the CLI, which breaks the family-blind CLI. Recommended: (1). Will chose option (2), because init installs the target package anyway (D10, answer 3). - -### D9. No cutover step (added 2026-09-16, superseded by D10) - -The guide's cutover section describes `contract infer` plus hand edits, the workflow `prisma7Schema` replaces. Init prints no cutover step (D10, answer 6). - -### D10. Init selects among known targets and checks the schema before any edit (Will, 2026-09-16 to 2026-09-24) - -Will's ruling on 2026-09-16 was that init cannot be target-specific. On 2026-09-23 he clarified what that means: init must not be coupled to one database, but selecting between the targets it knows is fine. The answers to the open questions: - -1. **Target branches on `main` stay.** Init already picks starter schemas, package names, labels, and the `--probe-db` driver per known target. That is selection among known targets, so it stays. Only the target coupling this project added goes: the Mongo-by-name refusal and the PostgreSQL guide in the next steps. -2. **The target comes from the schema's `datasource` provider; `--target` may only agree with it.** A provider with no known target keeps `CLI.INIT_PRISMA7_PROVIDER_UNSUPPORTED`, which now lists every known provider. A `--target` that disagrees with the provider is refused before anything is installed or asked, with `CLI.INIT_PRISMA7_TARGET_MISMATCH` (Will: "fail early on invalid options"). Without the flag, the Prisma 7 question is not asked when the target cannot be resolved, so a yes can never end in these refusals. -3. **The check runs from the target package installed in the project.** Will asked whether bundling the target packages into the CLI would be simpler. It is not: both target packages depend on the CLI package, so bundling creates a dependency cycle, and `pnpm lint:deps` forbids framework packages from importing target packages. Init installs the target package anyway, so the check only moves that install earlier. On the Prisma 7 path, after the target is known and before any consent question, init installs the target package and `dotenv`, loads the package's `/config` entrypoint from the project, and runs its `prisma7Schema` source in memory, writing nothing. A refused schema stops init with `CLI.INIT_PRISMA7_SCHEMA_REFUSED` and the source's diagnostics; the project is unchanged apart from the two packages, and the error prints the command that removes them. -4. **A target package without `prisma7Schema` means the Prisma 7 project is ignored** (Will: init "should function fine and just ignore the presence of prisma 7 config"). When the user entered the path by answering yes to the question, init warns before its next question and continues as a fresh init for that target. The warning says what a fresh init does to the Prisma 7 setup: it asks before replacing the Prisma 7 `prisma.config.ts`, and its install of `prisma@latest` replaces the Prisma 7 CLI. Whether a yes should instead stop init is open for Will. When the user passed `--from-prisma7-schema`, init cannot honour the flag and stops with `CLI.INIT_PRISMA7_SOURCE_UNAVAILABLE`: a fresh init needs `--authoring`, which cannot be combined with the flag. This split is the implementer's call, open to veto. When `@prisma/orm-mongo/config` exports `prisma7Schema`, the Prisma 7 path works for Mongo with no change to init. The same applies to releases: the `latest` `@prisma/orm-postgres` (8.0.0-rc.11) was published before `prisma7Schema` merged, so until the next release the check finds no source and the Prisma 7 path refuses the flag or falls back to a fresh init. -5. **The terminal problems found in manual QA are out of scope.** `--confirm` being ignored in an interactive session is caused by `consent` in `@prisma/cli-engine` checking `--confirm` values only when the session is non-interactive. The process that does not exit after "Done" is unconfirmed outside a pseudo-terminal. A brief for an agent in the engine repository covers both; manual QA scenario S12 checks the second in a real terminal. -6. **The cutover step is removed** (Will: too much instruction for a command modelled on `git init`). The next steps list only what the user runs right after init. - -Supersedes D6 and D9. D8 is resolved by answer 3. diff --git a/projects/orm-init-prisma7-detection/docs-brief-module-settings.md b/projects/orm-init-prisma7-detection/docs-brief-module-settings.md deleted file mode 100644 index 8face825f31a..000000000000 --- a/projects/orm-init-prisma7-detection/docs-brief-module-settings.md +++ /dev/null @@ -1,42 +0,0 @@ -# Docs brief: stop steering upgraders to `NodeNext` - -Target: the Prisma 7 to 8 guides in `prisma/web`, starting with `apps/docs/content/docs/guides/upgrade-prisma-orm/postgresql.mdx`, section 2.6 "Include the generated types". The same fix applies to any page that shows a tsconfig for a Prisma 8 project. - -## The problem - -A user reported that after following the guide they had to rewrite every relative import in their project to end in `.js`. The cause is the tsconfig the guide's example project uses: `"module": "NodeNext"` with `"type": "module"` in `package.json`, compiled with `tsc` and run with plain `node`. Under that setting TypeScript requires the file extension on every relative import, because Node's ESM loader requires it at runtime. This is Node's rule, not Prisma's, but the guide leads people into it. - -Prisma 8 does need an ESM-capable `module` setting for two reasons: the ORM package ships ESM only, and `db.ts` imports `contract.json` with `with { type: "json" }`, which TypeScript accepts only under `esnext`, `node18`, `node20`, `nodenext`, or `preserve`. It does not need `NodeNext` specifically. - -## What the guide should say instead - -Replace the current 2.6 text with guidance in this order: - -1. **Already on an ESM-capable `module` setting?** Keep it. Add `"resolveJsonModule": true` and the generated declarations to `include`. Nothing else changes. -2. **On `commonjs`?** The smallest change is `"module": "preserve"` and `"moduleResolution": "bundler"`, plus `resolveJsonModule` and the `include` entry. This accepts extensionless imports and the JSON import attribute, so no import rewrites. It works when the project runs through `tsx`, `vite`, `next`, `esbuild`, or any bundler, which is most projects. These are the exact options `prisma orm init` writes into an existing tsconfig today. -3. **Compile with `tsc` and run with plain `node`?** Then `NodeNext` is the right setting and Node needs the `.js` extension on every relative import. Say this plainly in one sentence and link to the TypeScript docs on it. Do not present it as the default path. - -Keep the existing note that import attributes need TypeScript 5.3 or later. - -## The example project - -`prisma/prisma8-and-7-example` uses `NodeNext` with `tsc` and `node dist/...`. That is why it needs `.js` imports. Either switch it to `preserve` plus `bundler` with `tsx` for `start`, or keep it and add a README sentence explaining that the extensions are a consequence of that choice. The guide should not inherit the example's tsconfig without explaining it. - -## Verified: CommonJS projects need no module change - -Tested 2026-09-14 on Node 24.13 with TypeScript 5.9.3 against the workspace build: - -- At runtime, a plain `.cjs` file can `require("./contract.json")` and `require("@prisma/orm-postgres/runtime")`, and `postgres({ contractJson, url })` constructs the client. Node's `require(esm)` handles the ESM-only package because it has no top-level await. -- For types, a `.cts` file (or a `.ts` file in a package without `"type": "module"`) that does `import postgres from "@prisma/orm-postgres/runtime"` and `import contractJson from "./contract.json"` typechecks clean under `"module": "nodenext"`. No import attribute, no `.js` extensions, no `type: module`. -- `"module": "node16"` rejects it (TS1479), and `"moduleResolution": "node10"` cannot resolve the package at all. So the guidance for CommonJS is exactly: `nodenext` plus `resolveJsonModule`, and a `db.ts` that imports the JSON without the `with { type: "json" }` attribute. - -This becomes option 2 in the list above, ahead of `preserve` plus `bundler`: a CommonJS project keeps CommonJS and changes two tsconfig lines. The `preserve` plus `bundler` option stays for projects that want to move to ESM and run through a bundler or `tsx`. - -Note for engineering, not for the docs page: `orm init` currently scaffolds `db.ts` with the import attribute and warns when `package.json` is not `"type": "module"`. Init could instead write the attribute-free import for CommonJS projects, since that form works in both. - -## Facts behind this brief - -- `orm init` tsconfig defaults: `packages/1-framework/3-tooling/cli/src/commands/init/templates/tsconfig.ts` sets `module: 'preserve'`, `moduleResolution: 'bundler'`, `resolveJsonModule: true`, and merges them into an existing tsconfig. -- `db.ts` template: `packages/1-framework/3-tooling/cli/src/commands/init/templates/code-templates.ts` imports `./contract.json` with `with { type: 'json' }`. -- `@prisma/orm-postgres` exports only `.mjs` files. -- Example project at tag `step-2`: `tsconfig.json` uses `NodeNext`, `package.json` has `"type": "module"`, `"start": "node dist/src/server.js"`, and `src/app.ts` imports end in `.js`. diff --git a/projects/orm-init-prisma7-detection/manual-qa-reports/2026-09-15-fable.md b/projects/orm-init-prisma7-detection/manual-qa-reports/2026-09-15-fable.md deleted file mode 100644 index 231396178535..000000000000 --- a/projects/orm-init-prisma7-detection/manual-qa-reports/2026-09-15-fable.md +++ /dev/null @@ -1,124 +0,0 @@ -# Manual QA report: `orm init` on a real Prisma 7 project — 2026-09-15 (runner: fable) - -Script: `projects/orm-init-prisma7-detection/manual-qa.md`. Logs: `wip/qa/logs/`. QA project: `wip/qa/prisma7-app/` (left in place, currently in the S9 `--skip-install` initialised state). All log paths below are relative to `wip/qa/logs/`. - -## Setup facts - -- Branch CLI built with `pnpm --filter @internal/config-loader build && pnpm --filter @internal/cli build` (`setup-build.log`, exit 0). Binary: `packages/1-framework/3-tooling/cli/dist/bin.mjs`. -- Toolchain: Node v26.8.1, pnpm 10.27.0. Prisma 7's preinstall prints a box saying "Prisma only supports Node.js versions 20.19+, 22.12+, 24.0+." but installs and runs fine. -- Starting project: `prisma` 7.10.0, `@prisma/client` 7.10.0, `@prisma/adapter-pg` 7.10.0, `pg` 8.23.0, `dotenv` 17.4.2, `@prisma/dev` 0.25.1 (root catalog version), `tsx` 4.23.13, `typescript` 5.9.3. Installed with `pnpm install --ignore-workspace` (`setup-install.log`). `pnpm.onlyBuiltDependencies` was added to the QA `package.json` so Prisma's engine postinstall could run. -- Database: `@prisma/dev` in-process Postgres started with `pnpm db:start` in the background (`setup-db.log`), `DATABASE_URL=postgres://postgres:postgres@127.0.0.1:62629/template1?sslmode=disable`, kept alive for the whole session. -- Pre-init: `pnpm migrate --name init` created and applied `20260915051451_init`, `pnpm generate` produced the client, `pnpm dev` printed `user count: 1` (`setup-prisma7.log`). -- Baseline committed in a throwaway `git init` repository inside the QA project; `prisma/` checksums recorded to `wip/qa/prisma-before.txt`. -- Packages init installed in S4/S7: `@prisma/orm-postgres` 8.0.0-rc.11 (dep), `prisma` 8.0.0-rc.15, `@prisma/prisma7` 7.10.0, `@prisma/cli-engine` 0.4.0 (devDeps). `@prisma/prisma7` exists on npm with `latest: 7.10.0` (`setup-npm-view.log`). -- QA environment deviation: on the first S4 attempt init's own `pnpm add` ran in the *repository* workspace (pnpm walked up to the repo's `pnpm-workspace.yaml`; the progress prefix was `../../..`) and failed on the repo's `trustPolicy: no-downgrade` with `ERR_PNPM_TRUST_DOWNGRADE High-risk trust downgrade for "prisma@7.10.0"` (`S4-attempt1-install-failed.log`). Per the script's instruction I added `pnpm-workspace.yaml` (`packages: []`) to the QA project so it is its own workspace root, reset the project, and re-ran. The repository itself was not modified (`git status` clean at the root apart from the untracked `manual-qa.md`). -- Pseudo-terminal runs (S7, S9) needed `expect` with an explicit `stty columns 200 rows 50`; with `script -q /dev/null` alone the prompt library wrapped one character per line. - -## S1. Non-interactive run without the flag — pass - -Command: `node orm init --skip-install` with stdin closed. Exit 2. Log: `S1.log`. - -`CLI.INIT_MISSING_FLAGS`: "This session cannot prompt, so the answers have to arrive as flags. Missing required flag(s): --target, --authoring." `git status` clean. Init did not enter the Prisma 7 path. - -Note: output is JSON even without `--json` because stdin is not a TTY. That is existing CLI behaviour, not specific to this branch. The error does not hint that the project is a Prisma 7 project or that `--from-prisma7-schema` exists; see finding F9. - -## S2. Flag conflicts — pass - -Commands: `... --from-prisma7-schema prisma/schema.prisma --authoring psl --skip-install` (`S2a.log`) and `... --schema-path x.prisma --skip-install` (`S2b.log`). Both exit 2, `CLI.INIT_FLAG_CONFLICT`, nothing written. - -Message names both flags: "`--from-prisma7-schema` and `--authoring` cannot be combined: the first reads an existing schema as the contract source, the second describes a starter schema to write." `meta.flags` lists both. - -## S3. Consent declined — pass - -Command: `... --from-prisma7-schema prisma/schema.prisma --skip-install < /dev/null`. Exit 2. Log: `S3.log`. - -`CLI.CONSENT_REQUIRED`: "... requires explicit consent, and the session is not interactive. Grant it by passing --confirm prisma7-app." Nothing written. The token is the package name (`consentToken(cwd)`), which here happens to equal the script's `prisma7-app`. - -## S4. The real run, JSON — pass with findings - -Command: `node orm init --from-prisma7-schema prisma/schema.prisma --confirm prisma7-app --json`. Attempt 1 exit 4 (install failed, QA environment, see setup). Attempt 2 exit 5. Logs: `S4-attempt1-install-failed.log`, `S4.log`, written files in `S4-files.log`. - -Checked against expectations: - -- `prisma.config.ts` renamed to `prisma7.config.ts`, import now `@prisma/prisma7/config`, body otherwise identical. New `prisma.config.ts` contains `contract: prisma7Schema('prisma/schema.prisma', { output: 'src/prisma/contract.json' })`. Correct. -- `package.json`: `generate`, `migrate`, `migrate:deploy`, `studio` now call `prisma7`; `dev` and `db:start` untouched; `contract:emit` added; devDependencies gained `@prisma/prisma7: ^7.10.0` and `@prisma/cli-engine: 0.4.0`; `prisma` moved to `8.0.0-rc.15`; `@prisma/client` unchanged at `^7.10.0`; `@prisma/orm-postgres: 8.0.0-rc.11` added to dependencies. Correct. -- `src/prisma/db.ts`, `prisma-8.md`, `.env.example`, `.gitattributes` written; `tsconfig.json` and `.gitignore` merged. Correct, with notes F5, F6, F7. -- `README.md` was also written, which the script did not list. Its content is the generic scaffold README and is wrong for this project (F4). -- `prisma/` identical (checksum diff empty); `generated/` untouched (mtimes unchanged). -- JSON document: `authoring: "prisma7"`, `filesRenamed: [{from: "prisma.config.ts", to: "prisma7.config.ts"}]`, `prisma7` block filled (`scriptsRewritten` lists the four scripts, `packagesMoved: ["@prisma/prisma7@7"]`), `nextSteps` in the transition order (emit, `db sign`, move routes, after each `prisma7 migrate dev` re-emit and re-sign, cutover), `warnings: []`. -- Emit failed with exit 5 and `CLI.INIT_EMIT_FAILED` as expected, but the recorded cause is wrong (F1). Cause text as reported by init: "`prisma contract emit` exited with code 2: Prisma agent skills are out of date (installed @prisma/orm-postgres 8.0.0-rc.11, synced none). Run: prisma skills sync". The actual error, seen in S6, is `CLI.CONFIG_UNREADABLE ... prisma7Schema is not a function`. - -Two install steps run: `pnpm add @prisma/orm-postgres dotenv`, then `pnpm add -D prisma@latest @prisma/prisma7@7`, then `pnpm add -D @prisma/cli-engine@0.4.0`. pnpm printed peer-dependency warnings from `prisma 8.0.0-rc.15 → @prisma/composer-cli → alchemy / @effect/vitest` ("missing peer vitest"); noise from the published CLI, not from init. - -## S5. Prisma 7 still works after the run — pass - -Log: `S5.log`. All exit 0. - -- `pnpm prisma7 --version`: "Loaded Prisma config from prisma7.config.ts." `prisma7: 7.10.0`, `@prisma/client: 7.10.0`. Prisma 7 found `prisma7.config.ts` without `--config`. -- `pnpm migrate:deploy` (now `prisma7 migrate deploy`): "1 migration found in prisma/migrations", "No pending migrations to apply." -- `pnpm generate`: "Generated Prisma Client (7.10.0) to ./generated/prisma". -- `pnpm dev`: `user count: 1`. -- `pnpm prisma7 migrate status`: "Database schema is up to date!" -- `prisma/` still identical. - -## S6. Prisma 8 CLI state — finding (known limitation, plus F1) - -Logs: `S6.log`, `S6-skills-sync.log`, `S6-branch-help.log`. - -- `pnpm prisma --version`: `8.0.0-rc.15` (JSON envelope, non-TTY). Exit 0. -- `pnpm prisma contract emit`: exit 2, `CLI.CONFIG_UNREADABLE`: "/…/prisma7-app/prisma.config.ts could not be evaluated: (0 , _config2.prisma7Schema) is not a function", nextAction "Fix the error in the file, then run the command again." Then a trailing stderr line: "Prisma agent skills are out of date (installed @prisma/orm-postgres 8.0.0-rc.11, synced none). Run: prisma skills sync". This is the known limitation (published `@prisma/orm-postgres` 8.0.0-rc.11 lacks `prisma7Schema`). Would a user understand? Not from this message alone: "is not a function" plus "fix the error in the file" reads as a mistake in the file init just wrote. Nothing says the installed `@prisma/orm-postgres` is too old. See F2. -- Following the trailing advice, `pnpm prisma skills sync` fails with the same `CLI.CONFIG_UNREADABLE` (`S6-skills-sync.log`), so the advice is a dead end. -- `pnpm exec prisma orm init --help` (published rc.15): does not mention `--from-prisma7-schema` at all; expected, that binary is not this branch. This branch's `node orm init --help` (`S6-branch-help.log`) has a paragraph "In a Prisma 7 project, pass --from-prisma7-schema (or answer yes when asked) to use the existing schema.prisma as the contract source.", the option line "--from-prisma7-schema Use an existing Prisma 7 schema.prisma as the contract source", and the example `prisma orm init --from-prisma7-schema prisma/schema.prisma --confirm my-app`. Clear enough; it does not say what the consent token is (the package name), and the example's `my-app` only makes sense once you know that. - -## S7. Human output — pass with findings - -Command (via `expect`, sized pty): `node orm init --from-prisma7-schema prisma/schema.prisma --confirm prisma7-app` on a fresh pre-init copy (reset with `git checkout -- . && git clean -fd -e node_modules -e generated -e .env` then `pnpm install --ignore-workspace` to put Prisma 7 back in `node_modules`). Exit 5. Logs: `S7.log` (cleaned), `S7-raw.log`, earlier attempts `S7-attempt1-hung-on-prompt.log`, `S7-attempt2-script-enter.log`. - -Surprises: - -- In a TTY the consent prompt is shown even though `--confirm prisma7-app` was passed (F3). It renders the token as the prefilled value. Pressing Enter alone was rejected ("… or press Ctrl-C." visible in attempt 2); typing the token worked. With stdin at `/dev/null` under `script`, attempt 1 sat on the prompt indefinitely. -- The `.env` question ("Also write a .env file from .env.example? (gitignored)") is asked even though `.env` already exists with `DATABASE_URL`. - -Prose judged from `S7.log`: - -- Every written file is listed under "written" (9 entries, including `README.md`), the rename is listed as `prisma.config.ts → prisma7.config.ts`, and installs are listed with `@prisma/prisma7@7 (dev, Prisma 7)` marked. Good. `prisma@latest (dev)` shows the requested spec, not the resolved `8.0.0-rc.15`. -- The `db sign` step: "→ Adopt your existing database: `prisma db sign` verifies it against the contract and records the signing marker and the `db` ref. It makes no change to the database schema.: prisma db sign". Content is clear; the rendering glues the command onto the end of a full sentence with ".: ", which reads badly (F8). -- "⚠ Scaffold written. Finish the steps above to complete setup." is printed *before* the steps, so "above" is wrong (F8). -- "→ Set DATABASE_URL in your environment" is printed even though `.env` already has it and the run just answered the `.env` question; misleading for this path (F8). -- The emit failure block repeats the wrong cause (F1): "why: `prisma contract emit` failed: … Prisma agent skills are out of date … Run: prisma skills sync". - -## S8. Re-run on the initialised project — finding - -Command: S4's command again on the S4 result. Exit 2. Log: `S8.log`. - -Init never reached the re-init consent. It failed with `CLI.INIT_PRISMA7_CONFIG_UNREADABLE`: "`prisma.config.ts` failed to evaluate, so init cannot tell whether it is a Prisma 7 config to rename or a Prisma 8 config to replace: (0 , _config2.prisma7Schema) is not a function", nextAction: "Install the project's dependencies so `prisma.config.ts` can be evaluated (a Prisma 7 config imports `prisma/config`), or rename it to `prisma7.config.ts` by hand, then re-run `prisma orm init`." - -Nothing was written (package.json, prisma.config.ts, prisma7.config.ts byte-identical to before; `prisma/` identical), so there was no second rename and no duplicate scripts. But the expected behaviour (consent for init's own files only) could not be observed because the new config cannot be evaluated against the published packages. The nextAction is actively dangerous here: `prisma7.config.ts` already exists, so "rename it to `prisma7.config.ts` by hand" would tell the user to overwrite their working Prisma 7 config with the Prisma 8 one (F2, second half). Root cause is the known limitation; the advice text is not. - -## S9. Interactive question path — pass with findings - -Command (via `expect`): `node orm init --skip-install` on a fresh pre-init copy; answered "y" to the Prisma 7 question, typed the token, "n" to `.env`. Logs: `S9.log` (cleaned), `S9-raw.log`, hang checks `S9-hangcheck-*.log`. - -- First prompt: "prisma/schema.prisma is a Prisma 7 schema. Use it as the Prisma 8 contract source?" with `○ Yes / ● No` (defaults to No). Note it detects via the schema, not the config, because `prisma.config.ts` imports `prisma/config`, which at that point still resolves to Prisma 7 — fine. -- Second: the same consent prompt as S7, token typed. -- Third: `.env` question, answered No. -- Result: same nine files written and the same rename as S4, `package.json` scripts rewritten and `contract:emit` added, no dependency changes (install skipped), `prisma/` identical. Next steps say "Install the project dependencies with your package manager: @prisma/orm-postgres, dotenv (and prisma@latest, @prisma/prisma7@7 plus @prisma/cli-engine at the version prisma declares as its dependency, as development dependencies)". Correct and matches S4 minus installs. -- After printing "✔ Done. Open prisma-8.md to get started." and the next steps, the process did not exit. It sat idle (0% CPU) for 10 minutes until killed (F10). Re-checked: with `--from-prisma7-schema … --confirm prisma7-app --skip-install` in the same pty harness it printed Done at 2 s and was still alive at 92 s (`S9-hangcheck-expect.log`); the plain path `orm init --target postgres --authoring psl --skip-install` in an empty project behaves the same (`S9-hangcheck-plain.log`), so this is pre-existing, not introduced by this branch. Non-TTY the same command exits in 0.38 s (`S9-hangcheck-nontty.log`), and under `script` with a pipe it exits when stdin closes (`S9-hangcheck-pty.log`). S7 (exit 5 path) exited normally, so it is the success path in a TTY that lingers. Worth reproducing in a real terminal before treating as a blocker. - -## Findings - -| # | Severity | Finding | Log | -|---|---|---|---| -| F1 | ⚠ should fix | `CLI.INIT_EMIT_FAILED` reports the wrong cause. Init captured the trailing stderr line "Prisma agent skills are out of date … Run: prisma skills sync" as the cause, and dropped the real error `CLI.CONFIG_UNREADABLE … prisma7Schema is not a function`. A user following init's advice runs `prisma skills sync`, which fails with the same hidden error. | `S4.log`, `S7.log`, `S6.log`, `S6-skills-sync.log` | -| F2 | ⚠ should fix | Known limitation, but the messages around it are not survivable. (a) `contract emit` says "prisma7Schema is not a function … Fix the error in the file", which points at the file init just wrote instead of at the too-old `@prisma/orm-postgres`; nothing tells the user which package version is needed. (b) Re-running init on the initialised project (S8) fails with `CLI.INIT_PRISMA7_CONFIG_UNREADABLE` whose nextAction says to rename `prisma.config.ts` to `prisma7.config.ts` by hand, which would overwrite the existing, working `prisma7.config.ts`. That advice should check whether `prisma7.config.ts` already exists. | `S6.log`, `S8.log` | -| F3 | ⚠ should fix | `--confirm prisma7-app` is not honoured in an interactive session: the consent prompt is still shown, prefilled with the token, and Enter alone is rejected; the user must retype the token. With a non-interactive stdin under a TTY the command hangs on the prompt. Scripts that pass `--confirm` for a documented reason (the `--help` example does) get a prompt anyway. | `S7-attempt1-hung-on-prompt.log`, `S7-attempt2-script-enter.log`, `S7.log` | -| F4 | ⚠ should fix | `README.md` is written on the Prisma 7 path with the generic scaffold text: "Generated by `prisma orm init` with the Minimal template", "First run: 1. `pnpm run db:init` — applies your contract to the database (creates tables, signs the marker)", lists scripts `db:init`, `db:update`, `migration:plan`, `db:seed` that do not exist in `package.json`, and says Prisma is scaffolded in `prisma/db.ts` and `src/lib/prisma.ts`, which do not exist. For a Prisma 7 project `db:init` is the opposite of what the next steps say (`db sign`, no schema changes). The script's expected file list did not include README.md either. | `S4-files.log` | -| F5 | ⚠ should fix | `tsconfig.json` merge changed the user's `module: nodenext` / `moduleResolution: nodenext` to `module: preserve` / `moduleResolution: bundler` with only the message "Updated tsconfig.json with required compiler options." This project's `src/index.ts` still runs under tsx, but a Prisma 7 project that relies on NodeNext semantics (explicit `.js` import extensions, ESM/CJS detection by `package.json` type) has had its resolution mode silently switched. The JSON document lists `tsconfig.json` under `filesWritten` but does not say what changed. | `S4-files.log` | -| F6 | ℹ note | `.gitignore` merge appended `node_modules/` and `dist/` although `node_modules` was already present; the result has both `node_modules` and `node_modules/`. Cosmetic. | `S4-files.log` | -| F7 | ℹ note | `.env.example` and the "Also write a .env file" question are produced even though `.env` already exists with `DATABASE_URL`, and the next steps still start with "Set DATABASE_URL in your environment". On the Prisma 7 path the database URL is already configured by definition (the Prisma 7 config reads it), so this step is noise. | `S4.log`, `S7.log` | -| F8 | ℹ note | Human-output wording: "⚠ Scaffold written. Finish the steps above to complete setup." is printed before the steps; the `db sign` step renders as a full sentence followed by ".: prisma db sign"; the install list shows `prisma@latest (dev)` rather than the resolved `8.0.0-rc.15`. | `S7.log` | -| F9 | ℹ note | S1's `CLI.INIT_MISSING_FLAGS` in a Prisma 7 project does not mention that the project was detected as Prisma 7 or that `--from-prisma7-schema` exists; a CI user gets the generic "supply --target and --authoring" advice. Behaviour is as the script expects (no silent entry into the Prisma 7 path). | `S1.log` | -| F10 | ⚠ should fix (pre-existing) | After a successful interactive run (`--skip-install`, exit 0 path) the process prints Done and the next steps but does not exit while the terminal stays open (idle 10 minutes, killed). Reproduced on the plain `--target postgres --authoring psl` path too, so not introduced by this branch. The failure path (S7, exit 5) exits normally. Verify in a real terminal. | `S9.log`, `S9-hangcheck-expect.log`, `S9-hangcheck-plain.log`, `S9-hangcheck-nontty.log` | -| F11 | ℹ note (QA environment) | Init's `pnpm add` inherits whatever workspace pnpm finds by walking up from `cwd`. Inside this repository that meant the repo's `trustPolicy: no-downgrade` rejected `prisma@7.10.0` (`ERR_PNPM_TRUST_DOWNGRADE`). A user's own project would not hit this, but a Prisma 7 app that lives inside a monorepo with a strict trust policy would; `prisma-8.md` already mentions catalogs but not trust policy. | `S4-attempt1-install-failed.log` | - -Nothing under `packages/`, `docs/`, or `projects/` was modified except this report. `wip/qa/` is left in place; the `@prisma/dev` database process (started 07:14 local) is still running from `pnpm db:start`. diff --git a/projects/orm-init-prisma7-detection/manual-qa-reports/2026-09-24-opus.md b/projects/orm-init-prisma7-detection/manual-qa-reports/2026-09-24-opus.md deleted file mode 100644 index 7875b2254208..000000000000 --- a/projects/orm-init-prisma7-detection/manual-qa-reports/2026-09-24-opus.md +++ /dev/null @@ -1,110 +0,0 @@ -# Manual QA report: `orm init` on a real Prisma 7 project — 2026-09-24 (runner: opus) - -Script: `projects/orm-init-prisma7-detection/manual-qa.md`. Logs: `wip/qa/logs/`. QA project: `wip/qa/prisma7-app/` (left in place). All log paths below are relative to `wip/qa/logs/`. - -## Setup facts - -- Branch CLI built with `pnpm --filter @internal/config-loader build && pnpm --filter @internal/cli build`, rebuilt after each fix below. Binary: `packages/1-framework/3-tooling/cli/dist/bin.mjs`. -- Toolchain: Node v24.13.0, pnpm 10.27.0. -- Starting project: `prisma` 7.10.0, `@prisma/client` 7.10.0, `@prisma/adapter-pg` 7.10.0, `pg` 8.22.0, `dotenv` 17.4.2, `@prisma/dev` 0.25.1, `tsx` 4.23.15, `typescript` 5.9.3 (`setup-install.log`). The project has its own `pnpm-workspace.yaml` (`packages: []`) so installs never reach the repository workspace. -- `scripts/db-start.ts` is `examples/prisma7-adoption/scripts/db-start.ts` from `main`, with `createDevDatabase` from `@repo/test-utils` replaced by `startPrismaDevServer` from `@prisma/dev`. -- Database: `@prisma/dev` Postgres at `127.0.0.1:55591`, started once and kept running (`setup-db.log`). `pnpm migrate --name init`, `pnpm generate`, and `pnpm dev` (`user count: 1`) all succeeded (`setup-prisma7.log`). -- Published packages: `@prisma/orm-postgres` `latest` is 8.0.0-rc.11 (published 2026-09-13), `dev` is 8.0.0-rc.11-dev.49 (2026-09-24). `prisma` `latest` is 8.0.0-rc.15 and pins `@prisma/orm-toolchain` 8.0.0-rc.11. `prisma7Schema` merged on 2026-09-16, so only the `dev` build has it. -- QA environment deviation: for S4b the emit ran the published `prisma` with a `pnpm.overrides` entry pinning `@prisma/orm-toolchain` to the `dev` build, so the project's CLI and target package came from the same commit. -- Mid-run the branch was merged with `main` (see F3). Every scenario from S4b on ran against the merged build. - -## S1. Non-interactive run without the flag — pass - -`node orm init --skip-install < /dev/null`. Exit 2, `CLI.INIT_MISSING_FLAGS` naming `--target` and `--authoring`, and the message names `--from-prisma7-schema prisma/schema.prisma`. Nothing written. Log: `S1.log`. - -## S2. Flag conflicts — pass - -`--authoring psl` and `--schema-path x.prisma` beside `--from-prisma7-schema`: both exit 2, `CLI.INIT_FLAG_CONFLICT`, message names both flags, nothing written. Logs: `S2a.log`, `S2b.log`. - -## S3. Consent declined — pass with a note - -`... --from-prisma7-schema prisma/schema.prisma --skip-install < /dev/null`. Exit 2, `CLI.CONSENT_REQUIRED` naming `--confirm prisma7-app`. Nothing written. With `--skip-install` and no target package installed, the check could not run; its warning is not in the error output (F7). Log: `S3.log`. - -## S4. The real run against the `latest` target package — pass - -`node orm init --from-prisma7-schema prisma/schema.prisma --confirm prisma7-app --json`. Exit 2, `CLI.INIT_PRISMA7_SOURCE_UNAVAILABLE`: "@prisma/orm-postgres does not provide a Prisma 7 contract source, so it cannot read prisma/schema.prisma." Log: `S4.log`. - -The first attempt told the user init added `@prisma/orm-postgres and dotenv`, although the project already declared `dotenv`, and printed a remove command that would have removed the user's own `dotenv` (F1, fixed). After the fix: "init added @prisma/orm-postgres to package.json before checking; remove it with `pnpm remove @prisma/orm-postgres`." Nothing else changed: no `prisma7.config.ts`, no `src/prisma`, scripts unchanged. Running the printed command restored the lockfile; `package.json` kept only pnpm's own reformatting of the `onlyBuiltDependencies` array (F11). - -## S4b. The check passes — pass - -With `@prisma/orm-postgres@dev` installed first: `node orm init --from-prisma7-schema prisma/schema.prisma --confirm prisma7-app --skip-install --json`, exit 0. Log: `S4b.log`. - -- `prisma.config.ts` renamed to `prisma7.config.ts`, import now `@prisma/prisma7/config`. The new `prisma.config.ts` has `contract: prisma7Schema("prisma/schema.prisma")` and `output: "src/prisma"`. -- Scripts `generate`, `migrate`, `migrate:deploy`, `studio` call `prisma7`; `dev` and `db:start` untouched; `contract:emit` added. -- `src/prisma/db.ts`, `prisma-8.md`, `.env.example`, `.gitattributes` written; `tsconfig.json` and `.gitignore` merged. -- `prisma/` checksums match `wip/qa/prisma-before.txt`. -- JSON document: `authoring: "prisma7"`, `filesRenamed` lists the config, `prisma7` block filled, eight next steps with no cutover step, `warnings: []`. -- After installing the listed packages (`S4b-install.log`), `pnpm contract:emit` wrote `src/prisma/contract.json` and `contract.d.ts` (`S4b-emit.log`). - -Before the merge with `main`, this run was refused with `PSL.PRISMA7_UNKNOWN_DEFAULT` on `@default(false)` (F3). - -## S5. Prisma 7 still works after the run — pass - -`pnpm prisma7 --version`, `pnpm migrate:deploy` ("No pending migrations to apply."), `pnpm generate`, `pnpm dev` (`user count: 1`), `pnpm prisma7 migrate status` ("Database schema is up to date!"). Every command printed "Loaded Prisma config from prisma7.config.ts." without `--config`. Log: `S5.log`. - -## S6. Prisma 8 CLI state — pass - -`pnpm prisma --version` is 8.0.0-rc.15. `pnpm prisma db sign` succeeded: marker created, `db` ref advanced, no diagnostics (`S6-sign.log`). `orm init --help` names `--from-prisma7-schema` and the Prisma 7 behaviour (`S6-help.log`). - -## S7. Human output — pass with notes - -Read from the S9 pseudo-terminal run (`S9.log`). Every file written and the rename are listed; the next steps are clear and have no cutover step. Notes: the `db sign` step still ends with ".: prisma db sign" (F9, pre-existing); the typed consent was rejected once when `expect` sent the whole token in one write, then accepted (F8). - -## S8. Re-run on the initialised project — pass after a fix - -`node orm init --from-prisma7-schema prisma/schema.prisma --confirm prisma7-app --skip-install --json` on the S4b result. The first run replaced `prisma.config.ts` without naming it in the re-init consent (F2, fixed). After the fix the question is "Re-initializing replaces prisma.config.ts, src/prisma/db.ts and prisma-8.md with a fresh scaffold, losing anything you wrote in them." No second rename, no duplicate scripts, `prisma/` identical. The run deletes the emitted `contract.json` and `contract.d.ts`, and with `--skip-install` nothing re-emits them (F10, pre-existing). Logs: `S8-noconfirm.log`, `S8.log`. - -## S9. Interactive question path — pass - -Driven through `expect` (`../s9.exp`) with the `dev` package installed: yes to the Prisma 7 question, the consent token, no to `.env`. Same files as S4b. The process did not exit within 30 seconds after its last line (F5). Logs: `S9.log`, `S9-expect.out`. - -## S10. A schema the source refuses — pass - -The `dev` package installed and a `view UserInfo` block added and committed. - -- With `--skip-install`: exit 2, `CLI.INIT_PRISMA7_SCHEMA_REFUSED`, `why` ends with `prisma/schema.prisma:26:1 PSL.PRISMA7_VIEW_UNSUPPORTED View "UserInfo" is not supported; ...`. `git status` clean. Log: `S10a.log`. -- Without `--skip-install`: the same refusal. pnpm kept the already-declared `dev` version, so the check ran against a package with `prisma7Schema`, nothing new was declared, and no remove command was printed. `git status` clean. Log: `S10b.log`. - -## S11. `--target mongodb` — pass - -On the baseline: exit 2, `CLI.INIT_PRISMA7_TARGET_MISMATCH`, "`--target mongodb` does not match `prisma/schema.prisma`, which declares `provider = "postgresql"`." Nothing installed or written. Log: `S11.log`. This replaces the brief's expected `CLI.INIT_PRISMA7_SOURCE_UNAVAILABLE`, because Will ruled that a mismatched `--target` fails early. - -S11b, the no-source case through the question: on the baseline with the `latest` package, `expect` answered yes, `psl`, the default schema path, the token, and no to `.env`. Init warned "@prisma/orm-postgres cannot read Prisma 7 schemas, so init sets up a fresh Prisma 8 project and leaves prisma/schema.prisma alone." and ran a fresh init, replacing the Prisma 7 `prisma.config.ts` under the re-init consent. The process did not exit afterwards (F5). Logs: `S11b.log`, `S11b-expect.out`. - -## S12. Exit after "Done" — partly recorded - -A real terminal cannot be driven through prompts without a person, so S12 used runs that take every answer as a flag: - -| Run | Prompts answered | Exited | -|---|---|---| -| Real terminal, Prisma 7 path (Prisma 7 already moved aside, `--write-env`) | none | yes, exit 0 | -| Real terminal, fresh init with every flag | none | yes, exit 0 | -| Pseudo-terminal, fresh init with every flag | none | yes (`S12-pty-noprompt.out`) | -| Pseudo-terminal, S9 | 3 | no | -| Pseudo-terminal, S11b | 5 | no | - -The process stays alive only after a prompt was answered, whatever the terminal. A real-terminal run with answered prompts still needs a person: in a fresh copy of the QA project, run `node orm init` and answer the questions. - -## Findings - -| # | Severity | Finding | Status | -|---|---|---|---| -| F1 | 🛑 blocker | The remove command named `dotenv` although the project declared it before init, so following the advice removed the user's own dependency. | Fixed in 9b398a5518. | -| F2 | ⚠ should fix | A re-run replaced init's own `prisma.config.ts` without naming it in the re-init consent, because detection reports the renamed `prisma7.config.ts` as the Prisma 7 config. | Fixed in 25976f9383. | -| F3 | ⚠ should fix | Version skew between the CLI running init and the installed target package can refuse a valid schema. The branch CLI (based on `main` of 2026-09-16) refused `@default(false)` when run against the 2026-09-24 `dev` package, which expects the `dataTypeLookup` that `main` added. Merging `main` fixed the QA run. Published releases pin the toolchain inside `prisma` to the target package version, so matching `latest` releases agree; a user running an older CLI against a newer target package would get a false refusal. | Reported; accepted cost of the in-process check. | -| F4 | ℹ note | The `latest` `@prisma/orm-postgres` predates `prisma7Schema`, so until the next release every real user gets `CLI.INIT_PRISMA7_SOURCE_UNAVAILABLE` from the flag, or a fresh init from the question. | Expected; needs a release. | -| F5 | ⚠ should fix (pre-existing, engine) | After answered prompts, init prints "Done" and the process does not exit. See S12. | Engine brief updated. | -| F6 | ⚠ should fix (pre-existing, engine) | `--confirm` does not answer the consent in an interactive session (cause found in the engine's `consent`). Not re-driven here. | Engine brief. | -| F7 | ℹ note | When a refusal follows, the warnings collected during input resolution (for example the unchecked-schema warning) are not shown. | Open. | -| F8 | ℹ note | The typed consent was rejected once when the whole token arrived in one write; a person typing is unaffected. | QA artifact, likely. | -| F9 | ℹ note (pre-existing) | The `db sign` next step renders as a sentence followed by ".: prisma db sign". | Open. | -| F10 | ℹ note (pre-existing) | A re-run deletes the emitted contract; with `--skip-install` it is not re-emitted. | Open. | -| F11 | ℹ note | pnpm reformats the `onlyBuiltDependencies` array in `package.json` when it adds a package; removing the package does not undo that. | Package manager behaviour. | - -Other: in one full CLI test run after the merge, two tests in `test/orm/migration-snapshot-content.test.ts` (from `main`) failed with `CLI.UNEXPECTED`; they passed alone and in the next full run. diff --git a/projects/orm-init-prisma7-detection/manual-qa.md b/projects/orm-init-prisma7-detection/manual-qa.md deleted file mode 100644 index 09004028ff60..000000000000 --- a/projects/orm-init-prisma7-detection/manual-qa.md +++ /dev/null @@ -1,90 +0,0 @@ -# Manual QA: `orm init` on a real Prisma 7 project - -Audience: an end user with a Prisma 7 Postgres project who follows the public upgrade guide and runs `prisma orm init` from this branch instead of the guide's hand edits. Runs against the CLI built from this branch, real Prisma 7 packages from npm, and an in-process Postgres from `@prisma/dev`. - -Known limitation to record, not a finding: the `latest` release of `@prisma/orm-postgres` (8.0.0-rc.11, published 2026-09-13) predates `prisma7Schema` (PR #30287, merged 2026-09-16). Init's schema check therefore finds no Prisma 7 source in the package it installs, and S4 expects `CLI.INIT_PRISMA7_SOURCE_UNAVAILABLE`. S4b exercises the success path with the package's `dev` build installed beforehand and `--skip-install`. - -## Setup - -1. Build the CLI from this branch: `pnpm --filter @internal/config-loader build && pnpm --filter @internal/cli build`. The binary is `packages/1-framework/3-tooling/cli/dist/bin.mjs`; run it as `node ...`. -2. Create `wip/qa/prisma7-app/` (not a workspace member; install with `pnpm install --ignore-workspace`) as the guide's starting project: - - `package.json`: `"type": "module"`, scripts `dev: tsx src/index.ts`, `generate: prisma generate`, `migrate: prisma migrate dev`, `migrate:deploy: prisma migrate deploy`, `studio: prisma studio`, `db:start: tsx scripts/db-start.ts`; dependencies `@prisma/client ^7.10.0`, `@prisma/adapter-pg ^7.10.0`, `pg`, `dotenv`; devDependencies `prisma ^7.10.0`, `@prisma/dev` (version from the root `pnpm-workspace.yaml` catalog), `tsx`, `typescript`, `@types/node`, `@types/pg`. - - `prisma.config.ts`: `import 'dotenv/config'; import { defineConfig } from 'prisma/config'; export default defineConfig({ schema: 'prisma/schema.prisma', migrations: { path: 'prisma/migrations' }, datasource: { url: process.env['DATABASE_URL']! } });` - - `prisma/schema.prisma`: `generator client { provider = "prisma-client"; output = "../generated/prisma" }`, `datasource db { provider = "postgresql" }`, the guide's `User` (id autoincrement, email unique, name optional, posts) and `Post` (id, title, content optional, published default false, author relation) models. - - `src/index.ts`: constructs `PrismaClient` from `../generated/prisma/client` with `PrismaPg`, inserts one user if none exist, prints `user count: N`. - - `scripts/db-start.ts`: copy `examples/prisma7-adoption/scripts/db-start.ts` from `main`. It imports `createDevDatabase` from `@repo/test-utils`, which is not installable outside the workspace; replace that call with `startPrismaDevServer` from `@prisma/dev`, as `test/utils/src/exports/index.ts` does. It starts an in-process Postgres and writes `DATABASE_URL` to `.env`. - - `tsconfig.json`: `module: nodenext`, `moduleResolution: nodenext`, `strict`, `types: ["node"]`, include `src`, `scripts`, `prisma.config.ts`. -3. `pnpm install --ignore-workspace`. Start the database in the background (`pnpm db:start &`, keep it running for the whole QA), then `pnpm migrate --name init`, `pnpm generate`, `pnpm dev` (expect `user count: 1`). -4. `git init` inside the project and commit everything except `node_modules`, `generated`, `.env`, so `git status` shows exactly what init changes. Also record `find prisma -type f | sort | xargs shasum` to `wip/qa/prisma-before.txt`. - -## Scenarios - -Record for each: the exact command, exit code, stdout and stderr (save to `wip/qa/logs/.log`), and a verdict: pass, finding, or note. - -### S1. Non-interactive run without the flag - -`node orm init --skip-install`. Expected: init behaves as today, refuses for missing `--target`/`--authoring` (exit 2), writes nothing (`git status` clean). It must not enter the Prisma 7 path silently. - -### S2. Flag conflicts - -`node orm init --from-prisma7-schema prisma/schema.prisma --authoring psl --skip-install` and the same with `--schema-path x.prisma`. Expected: `CLI.INIT_FLAG_CONFLICT`, exit 2, nothing written, message names both flags. - -### S3. Consent declined - -`node orm init --from-prisma7-schema prisma/schema.prisma --skip-install` with stdin closed (`< /dev/null`). Expected: the side-by-side consent cannot be answered, init aborts, nothing written, and the message tells the user to pass `--confirm prisma7-app`. - -### S4. The real run, JSON - -`node orm init --from-prisma7-schema prisma/schema.prisma --confirm prisma7-app --json`. This installs from npm and spawns the project's `prisma` for emit. Expected: - -- With the `latest` `@prisma/orm-postgres`: exit 2, `CLI.INIT_PRISMA7_SOURCE_UNAVAILABLE` naming `@prisma/orm-postgres`; the project is unchanged apart from `@prisma/orm-postgres` and `dotenv` in `package.json`, and the printed remove command removes them. Stop S4 here and continue with S4b. - -### S4b. The check passes, JSON - -On a fresh pre-init copy, install the `dev` build of the target package first: `pnpm add @prisma/orm-postgres@dev dotenv --ignore-workspace`. Then `node orm init --from-prisma7-schema prisma/schema.prisma --confirm prisma7-app --skip-install --json`. Then install what init lists and run `pnpm prisma contract emit`. Expected: - -- `prisma.config.ts` renamed to `prisma7.config.ts` with the import now `@prisma/prisma7/config`; the new `prisma.config.ts` has `contract: prisma7Schema('prisma/schema.prisma')` and `output: 'src/prisma'`. -- `package.json`: scripts `generate`, `migrate`, `migrate:deploy`, `studio` now call `prisma7`; `dev` and `db:start` untouched; `contract:emit` added. -- `src/prisma/db.ts`, `prisma-8.md`, `.env.example` written; `tsconfig.json` merged; `.gitignore` and `.gitattributes` merged. -- `prisma/` identical: `find prisma -type f | sort | xargs shasum` matches `wip/qa/prisma-before.txt`; `generated/` untouched. -- The JSON document: `authoring: "prisma7"`, `filesRenamed` lists the config, `prisma7` block filled, `nextSteps` in the transition order with no cutover step, no warning about an unchecked schema. -- After the installs, `contract emit` writes `src/prisma/contract.json` and `contract.d.ts`. - -### S5. Prisma 7 still works after the run - -`pnpm prisma7 --version`, `pnpm migrate:deploy` (rewritten script; expect "already in sync"), `pnpm generate`, `pnpm dev` (expect `user count: 1`), `pnpm prisma7 migrate status`. Prisma 7 must find `prisma7.config.ts` on its own, without `--config`. - -### S6. Prisma 8 CLI state - -`pnpm prisma --version` (expect 8.x), `pnpm prisma contract emit` (expect the same failure as in S4; record the message and judge it), `pnpm exec prisma orm init --help` (does the help mention the flag and the Prisma 7 behaviour clearly?). - -### S7. Human output - -Re-run S4b's command in a pseudo-terminal without `--json` on a fresh copy of the pre-init project (restore with `git stash` is forbidden; instead `git checkout -- . && git clean -fd -e node_modules -e generated -e .env` inside `wip/qa/prisma7-app`, which is the QA project's own throwaway repository, then re-run). Use `script -q /dev/null node orm init ...` so the CLI sees a TTY. Judge the prose: is every file written and renamed named, is the `db sign` step clear, is anything misleading. - -### S8. Re-run on the initialised project - -Run S4b's command again on the initialised project. Expected: re-init consent for init's own files only (never the Prisma 7 schema, never `prisma7.config.ts`), no second rename, no duplicate scripts, `prisma/` still identical. - -### S9. Interactive question path - -On a fresh pre-init copy with the `dev` target package installed as in S4b, in a pseudo-terminal, `script -q /dev/null node orm init --skip-install` and answer the prompts by hand through `expect` or by piping timed answers: yes to the Prisma 7 question, the consent token, no to `.env`. Expected: same files as S4b. If driving the prompts is not feasible in the time box, record that and skip. - -### S10. A schema the source refuses - -On a fresh pre-init copy with the `dev` target package installed as in S4b, add a `view UserInfo { id Int @unique email String }` block to `prisma/schema.prisma` and commit it in the QA repository. Run it twice: - -- `node orm init --from-prisma7-schema prisma/schema.prisma --confirm prisma7-app --skip-install`. The check runs against the installed `dev` package. Expected: `CLI.INIT_PRISMA7_SCHEMA_REFUSED` naming the view with its line and column; `prisma.config.ts` unchanged; scripts unchanged; no `prisma7.config.ts`; `package.json` unchanged. -- The same without `--skip-install`. Init installs the `latest` release over the `dev` one, so until a release carries `prisma7Schema` the expected result is `CLI.INIT_PRISMA7_SOURCE_UNAVAILABLE`. Expected either way: the project unchanged apart from `@prisma/orm-postgres` and `dotenv` in `package.json`, and the printed remove command removes them. - -### S11. A target with no Prisma 7 source - -The S4 project with `--target mongodb`. Expected: `CLI.INIT_PRISMA7_TARGET_MISMATCH`, exit 2, before anything is installed. This replaces the brief's expectation of `CLI.INIT_PRISMA7_SOURCE_UNAVAILABLE`, because Will ruled on 2026-09-24 that a mismatched `--target` fails early. To see the no-source outcome for a target, the Prisma 7 question path of S4 with the `latest` package is the case: answer yes, and init warns and runs as a fresh init. - -### S12. Exit after "Done" in a real terminal - -In a real terminal, not a pseudo-terminal, run the S4b command without `--json` on a fresh copy and record whether the process exits after printing "Done". Report the result to Will. - -## Report - -Write `projects/orm-init-prisma7-detection/manual-qa-reports/-.md`: setup facts (versions installed, database), one section per scenario with command, exit code, verdict, and a plain-English description of anything surprising, then a findings list with severity (🛑 blocker, ⚠ should fix, ℹ note) and the log path for each. Do not fix anything in `packages/`; the report is the deliverable. Leave `wip/qa/` in place for the orchestrator to inspect; do not commit it. diff --git a/projects/orm-init-prisma7-detection/plan.md b/projects/orm-init-prisma7-detection/plan.md deleted file mode 100644 index 61b5cdddfa06..000000000000 --- a/projects/orm-init-prisma7-detection/plan.md +++ /dev/null @@ -1,46 +0,0 @@ -# `orm init` detects a Prisma 7 project — Plan - -**Spec:** `projects/orm-init-prisma7-detection/spec.md` -**Tracker:** none, at the operator's request. - -## At a glance - -Two slices, one per family. Slice 1 delivers the whole Postgres path: detection, the side-by-side setup with consent, the scaffold variant, the next steps, the fixture, and the docs. Slice 2 needs no change to init's logic: init checks the schema with whatever `prisma7Schema` the chosen target package exports (design notes D10), so the Mongo path works once the parallel project's Mongo source ships. - -## Composition - -### Stack - -1. **Slice `01-postgres`** — `slices/01-postgres/` — **all five dispatches built; end-to-end proof green after #30287 merged; dispatches 6 and 7 replaced by the target selection and schema check recorded in D10; PR https://github.com/prisma/orm/pull/30291** - - **Outcome:** a Prisma 7 Postgres project run through `orm init` ends up as § At a glance in the spec, and `db sign` succeeds against the database its Prisma 7 migrations built. - - **Builds on:** [PR #30287](https://github.com/prisma/orm/pull/30287) merged (`prisma7Schema` in `@prisma/orm-postgres/config`). - - **Hands to:** the detection, consent, config-evaluation, and scaffold code paths parameterised by target; the fixture project layout; the next-steps text. - - **Focus:** `packages/1-framework/3-tooling/cli/src/orm/init*.ts`, `commands/init/`, `config-loader` (raw-export evaluation helper), `test/orm/init-*.test.ts`, `test/fixtures/`, CLI README. - -2. **Slice `02-mongo`** — `slices/02-mongo/` - - **Outcome:** the same for a Prisma 6 Mongo schema, through `@prisma/orm-mongo/config`'s `prisma7Schema`. - - **Builds on:** slice 1; `projects/prisma7-contract-source/` slice 2 merged. - - **Hands to:** project close-out. - - **Focus:** no init logic changes. A Mongo fixture and an end-to-end case; a Mongo variant of the Prisma 7 quick reference (`quick-reference-prisma7.md` shows Postgres examples); checking the next steps against the Mongo source's adoption flow. - -## Dependencies (external) - -- PR #30287 (parallel project slices 1 and 4). Slice 1 waits for it; the branch is not stacked on it. -- Parallel project slice 2 (Mongo source). Slice 2 waits for it. -- The docs brief hand-off to `prisma/web` is a close-out item, not a slice. - -## Sequencing rationale - -Slice 2 cannot be tested without the Mongo source and shares every code path with slice 1, so it follows. Nothing else in the project can run in parallel. - -## Model tiers - -Implementer dispatches: Fable. Reviewer dispatches: Opus 4.8, mid effort. Set by the operator. - -## Close-out (required) - -- [ ] Verify every project DoD item in `spec.md`. -- [ ] Lift the `git init` boundary and the Prisma 7 path into the CLI README's init section. -- [ ] Hand `docs-brief-module-settings.md` to the `prisma/web` docs owner. -- [ ] Strip repo-wide references to `projects/orm-init-prisma7-detection/**`. -- [ ] Delete `projects/orm-init-prisma7-detection/`. diff --git a/projects/orm-init-prisma7-detection/slices/01-postgres/plan.md b/projects/orm-init-prisma7-detection/slices/01-postgres/plan.md deleted file mode 100644 index 7e5edfc8f034..000000000000 --- a/projects/orm-init-prisma7-detection/slices/01-postgres/plan.md +++ /dev/null @@ -1,29 +0,0 @@ -# Slice 1 plan - -**Spec:** `slices/01-postgres/spec.md`. Test-first throughout: tests before implementation in every dispatch. - -**Validation gate (every dispatch):** in `packages/1-framework/3-tooling/cli`: `pnpm typecheck`, `pnpm lint`, `pnpm test`. Dispatch 1 also runs the same in `packages/1-framework/3-tooling/config-loader` and `pnpm lint:deps` at the root. Save test output to a file and read it (`.agents/rules/running-tests.mdc`). - -**Model tiers:** implementer Fable; reviewer Opus, mid effort. - -## Dispatches - -1. **Detection.** Outcome: `detectPrisma7Project` returns schema path, provider, Prisma 7 config file, and Prisma 7 CLI version for a project directory, with a config-loader export that evaluates a config module and returns its raw default export. Builds on: nothing. Hands to: the detection result type and its tests. Focus: `config-loader/src/load.ts` and `exports/index.ts`; new `cli/src/commands/init/prisma7-detect.ts`; tests beside them. - -2. **Inputs.** Outcome: init resolves the Prisma 7 path from the flag or the question, refuses the four error cases before any write, asks the side-by-side consent, and returns the extended `ResolvedInitInputs`. Builds on: 1. Hands to: the inputs shape the scaffold consumes; new errors in `commands/init/errors.ts` and `docs/reference/error-reference.md`. Focus: `orm/init.ts` flags, `orm/init-inputs.ts`, `commands/init/errors.ts`, `test/orm/init-inputs.test.ts`, `init-prompts.test.ts`. - -3. **Scaffold and installs.** Outcome: the Prisma 7 path writes the files in the spec, performs the side-by-side edits under consent, and installs the Prisma 7 packages; the fixture project exists. Builds on: 2. Hands to: the fixture project and a scaffold that the output layer reports. Focus: `orm/init-scaffold.ts`, `orm/init.ts` install specs, `commands/init/templates/*`, `commands/init/hygiene-*`, `test/fixture-app/fixtures/prisma7-project/`, `test/orm/init-scaffold.test.ts`, `init-install.test.ts`. - -4. **Output, next steps, docs.** Outcome: the result document, human output, and next steps describe the Prisma 7 run; the CLI README documents it; the harness tests run the fixture end to end with the fake emit. Builds on: 3. Hands to: slice DoD minus the real-emit item. Focus: `commands/init/output.ts`, `orm/init-blocks.ts`, `orm/init.ts`, README, `test/orm/init-*.test.ts`. - -5. **End to end with the real source.** Outcome: real `contract emit` and `prisma db sign` succeed against a dev database built from the fixture's Prisma 7 migration SQL. Builds on: 4 and PR #30287 merged into this branch. Hands to: slice DoD. Focus: a new `test/orm/init-prisma7.e2e.test.ts` and the fixture's `migration.sql`. - -## Open items - -- Dispatch 5 ran after PR #30287 merged; the merged `prisma7Schema` takes one argument and the facade owns `output`, which the template now reflects. - -## Added 2026-09-16: user-acceptability fixes (PR back to draft) - -6. **Check the schema before editing the project.** Done, in the form recorded in `design-notes.md` D10: the check installs the target package and `dotenv` into the project (not a temporary directory), runs the package's `prisma7Schema` from there before any consent question, and refuses with `CLI.INIT_PRISMA7_SCHEMA_REFUSED`. A package without `prisma7Schema` turns a run entered through the question into a fresh init and refuses a run given the flag with `CLI.INIT_PRISMA7_SOURCE_UNAVAILABLE`. The end-to-end test covers a fixture schema with a `view`. - -7. **Cutover next step and re-run QA.** Replaced: the cutover step is removed rather than repointed (D10). The provider-derived target stays, the Mongo refusal is removed, and a `--target` that disagrees with the provider is refused (`CLI.INIT_PRISMA7_TARGET_MISMATCH`). Manual QA scenarios S10 to S12 cover the check and the exit after "Done". diff --git a/projects/orm-init-prisma7-detection/slices/01-postgres/pr-body.md b/projects/orm-init-prisma7-detection/slices/01-postgres/pr-body.md deleted file mode 100644 index e9b5280cc552..000000000000 --- a/projects/orm-init-prisma7-detection/slices/01-postgres/pr-body.md +++ /dev/null @@ -1,109 +0,0 @@ -A Prisma 7 project on Postgres looks like this: - -``` -prisma/schema.prisma datasource db { provider = "postgresql" }, generator, models -prisma/migrations/ -prisma.config.ts import { defineConfig } from 'prisma/config' -package.json devDependencies: prisma ^7.10.0 - scripts: "migrate": "prisma migrate dev", "generate": "prisma generate" -``` - -With this PR its owner runs one command: - -```bash -prisma orm init --from-prisma7-schema prisma/schema.prisma --confirm my-app -``` - -or just `prisma orm init`, which notices the Prisma 7 project and asks two questions: use `prisma/schema.prisma` as the Prisma 8 contract source, and keep Prisma 7 installed as `@prisma/prisma7` while `prisma` moves to Prisma 8. Between the two, init checks that Prisma 8 can read the schema. Afterwards: - -``` -prisma7.config.ts the Prisma 7 config, renamed; its import now '@prisma/prisma7/config' -prisma.config.ts Prisma 8's config: contract: prisma7Schema('prisma/schema.prisma'), output: 'src/prisma' -src/prisma/db.ts the Prisma 8 client -src/prisma/contract.json emitted from prisma/schema.prisma -src/prisma/contract.d.ts -package.json devDependencies: prisma 8.x, @prisma/prisma7 7.x - scripts: "migrate": "prisma7 migrate dev", "generate": "prisma7 generate", "contract:emit": "prisma contract emit" -``` - -and init prints what to do next: set `DATABASE_URL`, run `prisma db sign`, move routes to the Prisma 8 client one at a time, and re-run `prisma contract emit` and `prisma db sign` after each `prisma7 migrate dev`. `prisma/`, `generated/`, and the database are untouched. - -If the schema has something Prisma 8 cannot represent, such as a `view` block, init stops before changing anything. From the manual QA run, the error's summary, reason, and next actions: - -``` -CLI.INIT_PRISMA7_SCHEMA_REFUSED Prisma 8 cannot read prisma/schema.prisma -Prisma 7 schema interpretation failed - prisma/schema.prisma:26:1 PSL.PRISMA7_VIEW_UNSUPPORTED View "UserInfo" is not supported; Prisma 8 has no views. Remove the view or replace it with a model over the underlying table. -Edit prisma/schema.prisma as each finding says (see the Prisma 7 contract source section of the @prisma/orm-postgres README), or run `prisma orm init` without `--from-prisma7-schema`. -``` - -When the check installed a package the project did not declare, one more next action names it and the command that removes it, for example ``init added @prisma/orm-postgres to package.json before checking; remove it with `pnpm remove @prisma/orm-postgres`.`` - -## The decision - -The public guide [Prisma ORM 7 to 8 (PostgreSQL)](https://www.prisma.io/docs/guides/upgrade-prisma-orm/postgresql) has two mechanical sections before the real work starts. Section 1 sets Prisma 7 aside: swap the `prisma` package for `@prisma/prisma7`, rename the config, change its import, rename the scripts. Section 2 adds Prisma 8: install it, write a config, produce a contract, emit. About fifteen edits, identical for every project. - -This PR moves both sections into `orm init`, under one explicit consent, and stops there. Init does not sign the database and does not touch application code. It sets up what Prisma 8 needs to operate in the project, the way `git init` sets up a repository, and hands the database step to `prisma db sign`. A database may not even be reachable when init runs. - -Init finds out whether Prisma 8 can read the schema before it changes the project. The reader lives in the target package, so init installs that package first, runs its Prisma 7 source in memory, and only then asks for consent and makes the edits. A refused schema leaves the project unchanged apart from that install, and the error says how to undo it. - -The contract source is the existing `schema.prisma`, read through `prisma7Schema` from the target package's `/config` entrypoint (#30287). There is no converted schema file to keep in sync; after each Prisma 7 migration the user re-emits and re-signs. - -## How a run works - -**Detection.** Init evaluates `prisma.config.ts` with the same loader every command uses. A Prisma 8 config carries a `$prismaConfig` marker; a Prisma 7 config does not, which is the check behind the existing `CONFIG.VERSION_MARKER_MISSING` error. From a Prisma 7 config init reads only `schema`, to default the schema path. The provider comes from the schema's `datasource` block, and the Prisma 7 CLI version from `package.json`. - -**Asking, never assuming.** The Prisma 7 path is entered only by the `--from-prisma7-schema` flag or by a yes to the question. The question has no default, so `--yes` and non-interactive runs never take the path by accident. Combining the flag with `--schema-path` or `--authoring` is an error, since those mean "write a starter schema". - -**Choosing the target.** The provider picks among the targets init knows: `postgresql` or `mongodb`. `--target` may only agree with it, or name the database when the provider is not a string literal. With `--from-prisma7-schema`, a mismatch (`CLI.INIT_PRISMA7_TARGET_MISMATCH`) and a provider with no target (`CLI.INIT_PRISMA7_PROVIDER_UNSUPPORTED`) are refused before anything is asked or installed. Without the flag, init does not ask its Prisma 7 question in those cases and runs as a fresh init. Init no longer refuses Mongo by name. - -**Checking the schema first.** Before any consent question, init installs the target package and `dotenv` (both are installed by every init), loads the package's `/config` entrypoint from the project, and runs its `prisma7Schema` source through the new `loadContractSource`, the part of `contract emit` that builds the control stack and runs the source, without writing anything. The CLI carries no target code, so the installed package is the only one that can answer. Outcomes: - -- The source reads the schema: init continues. -- The source refuses: `CLI.INIT_PRISMA7_SCHEMA_REFUSED` with each diagnostic as `:: `, and the command that removes the packages the project did not declare before. Any later error, including the engine's own consent and cancellation errors, carries the same remove command. -- The package has no `prisma7Schema`: after a yes to the question init warns before its next question and runs as a fresh init, and the warning says when that replaces the Prisma 7 `prisma.config.ts` (after asking) and the Prisma 7 CLI; with the flag it stops with `CLI.INIT_PRISMA7_SOURCE_UNAVAILABLE`. When `@prisma/orm-mongo/config` exports `prisma7Schema`, the Mongo path works with no change to init. -- `--skip-install` and the package is not installed: a warning, and init continues. -- The install fails: exit 4 with nothing written. - -**Setting Prisma 7 aside.** Init installs `prisma@latest`, which in an untouched project would replace the Prisma 7 CLI and make `prisma migrate dev` run Prisma 8. So after the check, and only after the consent token, init renames `prisma.config.` to `prisma7.config.`, rewrites its import, rewrites every script that invokes the `prisma` binary to `prisma7` (including after `--`, `&&`, and environment assignments), and adds `@prisma/prisma7`. Prisma 7 since 7.10.0 looks for `prisma7.config.*` first, so it keeps working without `--config`. This is the one place init edits files it did not write; it is bounded to the guide's section 1 and planned before any write, and the rename is refused if the target file already exists. - -**Scaffolding.** Everything else is init as it is today, with one different `contract:` line and no starter schema. `db.ts` and the emitted artifacts go under `src/prisma/`, the same layout a fresh init produces, so an upgraded project ends up shaped like a new one. Nothing is written under `prisma/`. The later install skips the packages the check already installed. - -**Reporting.** The `--json` document gains `authoring: "prisma7"`, a `filesRenamed` list, and a `prisma7` block naming the renamed config, the rewritten scripts, and the packages moved. The human output shows the same. New error codes cover the refusals: flag conflict, target mismatch, unsupported provider, a schema without a `datasource` block, an unreadable config, a config-name collision, a refused schema, and a target package without a Prisma 7 source. No refusal writes a file; the check's install is the only change before one. - -## What init never does - -- Connect to the database beyond the opt-in `--probe-db` version check, or sign it. -- Write under `prisma/`, replace the Prisma 7 schema, or touch `prisma/migrations` or the Prisma 7 client output. -- Read anything but `schema` from the Prisma 7 config, or carry its `datasource.url` expression into the new config. The new config uses `process.env['DATABASE_URL']`, as init writes today. -- Change `package.json#type` or tsconfig handling. Both keep init's existing behaviour. -- Print a cutover step. The next steps list only what the user runs right after init. - -## Proof - -- `test/orm/init-prisma7.e2e.test.ts` copies a checked-in Prisma 7 fixture project, applies its Prisma 7 migration SQL to a dev database, runs init with the real check and the real spawned `contract emit`, and then runs `prisma db sign` and `prisma db verify` against that database: zero findings, marker written, `db` ref advanced, and `prisma/` holds only the schema and migrations. A second case runs init on a copy whose schema has a `view` block, with the real `@prisma/orm-postgres`: `CLI.INIT_PRISMA7_SCHEMA_REFUSED` with the view diagnostic, `prisma.config.ts` and `package.json` byte-identical, no `prisma7.config.ts`, and one install call. -- Harness tests in `test/orm/init-prisma7-check.test.ts` inject the module loader instead of mocking modules: a refused schema, a package without `prisma7Schema` under the flag and under the question, `--skip-install` with the package missing, a project that already declares `dotenv`, and a schema the source reads. Input-level tests pin that the check runs before any consent and that a declined consent names the installed packages. -- `loadContractSource` has its own tests, and every existing emit test passes unchanged. -- Each new test was checked to fail without its implementation; the notes are in the PR conversation. -- Manual QA on 2026-09-24 (`manual-qa-reports/2026-09-24-opus.md`) against a Prisma 7.10.0 project, scenarios S1 to S12. It found two bugs, both fixed here: the remove command named a `dotenv` the project already declared, and a re-run replaced init's own `prisma.config.ts` without naming it in the consent. It also found that the process stays alive after "Done" only when a prompt was answered, in a real terminal or a pseudo-terminal alike; that and `--confirm` being ignored in an interactive session are engine issues, briefed separately. -- An independent review of the diff found three more problems, fixed here: the remove command was missing from the engine's consent and cancellation errors, the Prisma 7 question was asked when a `--target` mismatch made a yes pointless, and the fresh-init fallback did not say before its questions what it would replace. - -## Known limitation - -The `latest` `@prisma/orm-postgres` on npm (8.0.0-rc.11) was published before `prisma7Schema` merged. Until the next release, the check finds no Prisma 7 source in the package init installs: the flag stops with `CLI.INIT_PRISMA7_SOURCE_UNAVAILABLE`, and a yes to the question runs a fresh init. The check runs with the CLI that invoked init, so an older CLI against a newer target package can refuse a valid schema; published releases pin the two to the same version. - -## Alternatives considered - -- **A separate `prisma upgrade` command** that automated the whole guide, including a schema converter and signing. Dropped: it could not find its inputs reliably in arbitrary projects (computed config values, multi-file schemas, monorepos, callers of `prisma migrate` in CI files it must not edit), and #30287 removed the need for a converter. -- **Making changes first and printing undo instructions when emit refuses the schema.** Rejected: a schema with a construct that has no fix, such as a view, left the project renamed and rewired for nothing. -- **Installing the target package into a temporary directory for the check,** so a refusal leaves `package.json` untouched. Not chosen: it downloads the package twice on success, and init installs the package into the project anyway. -- **Bundling the target packages into the CLI** so the check needs no install. Rejected: both target packages depend on the CLI package, so bundling creates a dependency cycle, and the layering rules forbid framework code from importing target packages. -- **Taking the target only from `--target` or the "What database are you using?" question,** never from the schema's provider. Rejected: choosing among the targets init knows is fine, and the schema already says which database it is for. A `--target` that disagrees is refused instead. -- **Refusing a `mongodb` provider by name** until the Mongo source exists. Replaced by the check, so Mongo needs no init change when its source ships. -- **Stopping and telling the user to do section 1 by hand, then re-run init.** Rejected because nobody should have to run init twice; the four edits are deterministic, so init does them under consent instead. -- **Signing inside init** so the run ends with a working query. Rejected: it makes init a command that writes to the database, and the database may not be reachable. -- **Carrying the Prisma 7 `datasource.url` expression** into the new config, or matching its resolved value back to an environment variable. Deferred: the first needs a TypeScript rewrite of user code, the second assumes the URL came from an environment variable. -- **The guide's `prisma8/` layout or the example app's `generated/prisma8/`.** Not adopted; `src/prisma/` is what a fresh init produces. -- **A cutover step pointing at the guide** or at the Postgres README. Removed: the guide's cutover describes the workflow `prisma7Schema` replaces, and a command modelled on `git init` should not carry that much instruction. - -🤖 Generated with [Claude Code](https://claude.com/claude-code) diff --git a/projects/orm-init-prisma7-detection/slices/01-postgres/spec.md b/projects/orm-init-prisma7-detection/slices/01-postgres/spec.md deleted file mode 100644 index 72f543a53125..000000000000 --- a/projects/orm-init-prisma7-detection/slices/01-postgres/spec.md +++ /dev/null @@ -1,95 +0,0 @@ -# Slice 1: `orm init` on a Prisma 7 Postgres project - -_Parent project: `projects/orm-init-prisma7-detection/`. Outcome: a Prisma 7 Postgres project run through `prisma orm init`, by flag or by answering two questions, ends up set up for Prisma 8 beside Prisma 7 with `schema.prisma` as the contract source, and `prisma db sign` then succeeds against its database._ - -## At a glance - -```bash -prisma orm init --from-prisma7-schema prisma/schema.prisma --confirm my-app -``` - -Writes `prisma.config.ts`, `src/prisma/db.ts`, `prisma-8.md`, `.env.example`; merges `tsconfig.json`, `.gitignore`, `.gitattributes`, `package.json`; renames `prisma.config.ts` to `prisma7.config.ts`; installs `@prisma/orm-postgres`, `dotenv`, `prisma@latest`, `@prisma/prisma7`; emits `src/prisma/contract.json` and `contract.d.ts`. The interactive run reaches the same state through one yes/no question and one consent prompt. - -## Chosen design - -### Detection (`commands/init/prisma7-detect.ts`, new) - -One function reads the project and returns what init would otherwise ask for: - -- **Schema path.** The flag value when given. Else the `schema` field of the Prisma 7 config when one evaluates. Else `prisma/schema.prisma`. The path may be a file or a directory of `.prisma` files. -- **Prisma 7 config.** `prisma.config.{ts,mts,cts,js,mjs,cjs}` in the working directory, or `prisma7.config.*` when only that exists (a project that already did the guide's section 1). Evaluated through a new `@internal/config-loader` export that returns the raw default export of a config module using the same c12 evaluation `loadConfig` uses. The export is a Prisma 7 config when it is an object without the `$prismaConfig` marker. Only `schema` is read from it. An evaluation failure is a warning, not an error; detection continues with the default path. -- **Provider.** The `provider` value of the `datasource` block, found by scanning the schema text (all files when a directory). No `datasource` block means the path is not a Prisma 7 schema. -- **Prisma 7 CLI.** `prisma` in `package.json` `devDependencies` or `dependencies` with a major below 8, read from `node_modules/prisma/package.json` when installed, else from the declared range. `@prisma/prisma7` already declared means section 1 is done. - -### Inputs (`orm/init-inputs.ts`) - -- New flag `--from-prisma7-schema `. Given together with `--schema-path` or `--authoring` is `CLI.INIT_FLAG_CONFLICT` (new), naming both flags. -- Without the flag, when neither `--authoring` nor `--schema-path` was given and detection finds a Prisma 7 config or a schema with a `datasource` block, the interactive run asks ` is a Prisma 7 schema. Use it as the Prisma 8 contract source?` (default yes). No, or a non-interactive session, runs init as today. This question comes before the target and authoring questions, which it replaces. -- The provider picks among the known targets (`postgresql`, `mongodb`). A provider with no known target is `CLI.INIT_PRISMA7_PROVIDER_UNSUPPORTED` listing both; a `--target` that disagrees with the provider is `CLI.INIT_PRISMA7_TARGET_MISMATCH`; a path with no `datasource` block is `CLI.INIT_PRISMA7_SCHEMA_INVALID`. With the flag, all exit 2 before anything is asked, installed, or written; without it, the Prisma 7 question is not asked when the target cannot be resolved. `--target` can name the database only when the provider is not a string literal. -- Before any consent question, init installs the target package and `dotenv`, loads the package's `/config` entrypoint from the project, and runs its `prisma7Schema` without writing (design notes D10). A refused schema is `CLI.INIT_PRISMA7_SCHEMA_REFUSED` with the source's diagnostics and the command that removes the two packages. A package without `prisma7Schema` turns a run entered through the question into a fresh init and refuses a run given the flag with `CLI.INIT_PRISMA7_SOURCE_UNAVAILABLE`. -- When a Prisma 7 CLI is declared and `@prisma/prisma7` is not, init asks the existing token consent: `Prisma 7 is installed as \`prisma\`. Keep it as @prisma/prisma7 (binary prisma7) and move \`prisma\` to Prisma 8?` Declined is `errorInitUserAborted`. `--confirm ` answers it non-interactively, as for re-init. -- `ResolvedInitInputs` gains the contract source (`starter` with authoring and schema path, or `prisma7-schema` with the schema path) and the side-by-side plan (`null`, or the config file to rename and the scripts to rewrite). Re-init consent applies to init's own files only; the Prisma 7 schema is never in the replace list. - -### Scaffold (`orm/init-scaffold.ts`, `commands/init/templates/*`) - -- No starter schema. `prisma.config.ts` imports `prisma7Schema` beside `defineConfig` from the target's `/config` entrypoint and writes `contract: prisma7Schema('', { output: 'src/prisma/contract.json' })`. Everything else in the template is unchanged. -- `db.ts` goes to `src/prisma/db.ts`. Gitattributes lines and stale-artifact cleanup use `src/prisma/`. -- `prisma-8.md` says the contract source is the Prisma 7 schema and shows the transition loop instead of a starter schema sample. -- **Side-by-side edits**, only after consent, planned before any write like the rest of the scaffold: rename the Prisma 7 config to `prisma7.config.` and replace its `prisma/config` import specifier with `@prisma/prisma7/config` (warn when the specifier is not found; rename anyway); in `package.json` scripts, replace the `prisma` binary with `prisma7` in every command that invokes it (scripts init adds keep `prisma`). Both are reported as files written, and the rename is reported as such. -- **Installs.** The existing dependency install adds `@prisma/prisma7@7` as a development dependency and `@prisma/client@7` as a dependency when `@prisma/client` is declared below the Prisma 7 CLI's version, alongside `prisma@latest`, so the package manager moves `prisma` to 8 and keeps client and CLI at the same Prisma 7 version. `--skip-install` lists them as next steps instead. - -### Output and next steps (`commands/init/output.ts`, `orm/init-blocks.ts`) - -- `authoring` gains the value `prisma7`. `schemaPath` is the Prisma 7 schema path. A new `prisma7` block records the renamed config, the rewritten scripts, and the Prisma 7 packages installed; `null` on a normal run. -- Next steps on the Prisma 7 path, in order: set `DATABASE_URL`; `prisma db sign`; move routes one at a time to the client in `src/prisma/db.ts`; after each `prisma7 migrate dev`, run `prisma contract emit` then `prisma db sign`; when `@prisma/client` moved, `prisma7 generate`. There is no cutover step. The typed `nextActions` carry the same steps. - -### Docs - -`packages/1-framework/3-tooling/cli/README.md` gains an `orm init` section covering the flag, the two questions, what is written and renamed, the `git init` boundary, and the next steps. `docs/reference/error-reference.md` gains the new codes (`pnpm check:error-reference`). - -## Coherence rationale - -One PR: a reviewer follows one path (detect, ask, scaffold, install, emit, report) through files that already exist for the normal path, with the fixture project as the single worked example. The side-by-side edits are the only new kind of write and sit in one planned block. - -## Scope - -**In:** everything above, unit tests per module, CLI-harness tests through `createTestCli` with the fake emit, a checked-in Prisma 7 fixture project under `test/fixture-app/fixtures/prisma7-project/`, and the end-to-end test that runs real emit and `db sign` against a dev database once `prisma7Schema` is on `main`. - -**Deliberately out:** Mongo (slice 2), reading anything but `schema` from the Prisma 7 config, the connection expression, `contract convert`, any change to `"type": "module"` or tsconfig handling, the `prisma/config` import (orphan slice). - -## Pre-investigated edge cases - -| Case | Disposition | -|---|---| -| `prisma.config.ts` evaluates and carries the `$prismaConfig` marker | It is a Prisma 8 config; no Prisma 7 config exists. Detection still runs on the schema path. | -| Both `prisma.config.ts` (Prisma 7) and `prisma7.config.ts` exist | Warn, treat `prisma7.config.ts` as the Prisma 7 config, and stop with `CLI.INIT_PRISMA7_CONFIG_COLLISION`: init cannot rename onto an existing file. | -| Prisma 7 config imports from `@prisma/prisma7/config` already but is still named `prisma.config.ts` | Rename only. | -| `prisma` declared at major 8 or `@prisma/prisma7` declared | No consent prompt, no side-by-side edits. | -| `--skip-install` | Side-by-side file edits still happen under consent; package changes are listed as next steps. | -| `src/prisma/db.ts` or `prisma.config.ts` written by an earlier init exist | Normal re-init consent. The Prisma 7 schema never appears in the list. | -| Schema directory with the `datasource` block in one file | Provider found across files. | -| Prisma 7 config is `.mts`/`.cts`/`.js` | Renamed with the same extension. | -| F14: gates must mirror CI | Run `pnpm lint` in the CLI package as well as typecheck and tests. | -| F13: refusal tests must discriminate | Each refusal test asserts the code and that no file was written. | - -## Slice definition of done - -Inherits `drive/calibration/dod.md`. Slice-specific: - -- [ ] The fixture project run non-interactively and interactively matches § At a glance; `prisma/` is byte-identical afterwards. -- [ ] Every refusal and the declined consent write nothing. -- [ ] With PR #30287 merged: real `contract emit` then `prisma db sign` succeeds with zero findings against a dev database built from the fixture's Prisma 7 migration SQL. -- [ ] CLI README and error reference updated. - -## Open questions - -None. - -## References - -- Project spec and `design-notes.md` (D3 for the side-by-side ruling, D4, D5). -- `packages/1-framework/3-tooling/config-loader/src/load.ts` (marker check, c12 evaluation). -- `packages/1-framework/3-tooling/cli/src/orm/init-inputs.ts`, `init-scaffold.ts`, `init.ts`, `init-packages.ts`, `commands/init/hygiene-package-scripts.ts`, `commands/init/templates/code-templates.ts`, `commands/init/output.ts`, `orm/init-blocks.ts`. -- Tests: `packages/1-framework/3-tooling/cli/test/orm/init-*.test.ts`, `test/utils/test-project-dir.ts`. -- `prisma7Schema` signature: `packages/3-extensions/postgres/src/config/prisma7-schema.ts` on branch `bot/prisma7-contract-source` (PR #30287); example project `examples/prisma7-adoption/` on the same branch. -- Fixture corpus with Prisma 7 migration SQL: `packages/2-sql/2-authoring/contract-prisma7/test/fixtures/` on the same branch. diff --git a/projects/orm-init-prisma7-detection/spec.md b/projects/orm-init-prisma7-detection/spec.md deleted file mode 100644 index f223ad137342..000000000000 --- a/projects/orm-init-prisma7-detection/spec.md +++ /dev/null @@ -1,115 +0,0 @@ -# `orm init` detects a Prisma 7 project - -> Shaped 2026-09-14 with Will. Decisions and rejected alternatives are in `design-notes.md`. Claims about the CLI were checked against `packages/1-framework/3-tooling/cli/src/orm/init*.ts` on `main` at c04c4d46d9. - -## Purpose - -A Prisma 7 user can add Prisma 8 to their existing project by running the same command a new user runs, `prisma orm init`, and come out with Prisma 8 set up beside Prisma 7, reading their existing `schema.prisma` as the contract source. Today the public upgrade guide asks for about fifteen hand edits before the first Prisma 8 command works; every one of them is mechanical and identical across projects. - -## At a glance - -An untouched Prisma 7 project on Postgres: - -``` -prisma/schema.prisma datasource provider = "postgresql" -prisma/migrations/ -prisma.config.ts defineConfig from 'prisma/config' -package.json devDependencies: prisma ^7.x; scripts call `prisma migrate dev`, `prisma generate` -``` - -The user runs `prisma orm init` and answers two questions: - -``` -? prisma/schema.prisma is a Prisma 7 schema. Use it as the Prisma 8 contract source? (Y/n) -? Prisma 7 is installed as `prisma`. Keep it as @prisma/prisma7 (binary `prisma7`) and move `prisma` to Prisma 8? Type "my-app" to confirm. -``` - -Or non-interactively: - -```bash -prisma orm init --from-prisma7-schema prisma/schema.prisma --confirm my-app -``` - -Afterwards: - -``` -prisma7.config.ts the Prisma 7 config, renamed, importing from '@prisma/prisma7/config' -prisma.config.ts contract: prisma7Schema('prisma/schema.prisma'), output: 'src/prisma' -src/prisma/db.ts the Prisma 8 client -src/prisma/contract.json emitted -src/prisma/contract.d.ts emitted -package.json @prisma/prisma7 added, prisma at 8, scripts prisma7 migrate dev / prisma7 generate, contract:emit added -``` - -and the printed next steps are: set `DATABASE_URL`, run `prisma db sign`, move routes to the Prisma 8 client one at a time, and re-run `prisma contract emit` and `prisma db sign` after each `prisma7 migrate dev`. - -## Non-goals - -- **Connecting to the database.** Init never signs, verifies, or probes beyond the existing opt-in `--probe-db`. `db sign` is the first printed next step. This is the `git init` boundary: init sets up what Prisma 8 needs to operate in the project and stops. -- **Rewriting application code.** Routes move by hand or through the upgrade skill. -- **Cutover.** `contract convert` and the switch to a Prisma 8 contract file belong to `projects/prisma7-contract-source/`. -- **Carrying the Prisma 7 connection expression.** The new config's `db.connection` is `process.env['DATABASE_URL']`, as init writes today. Reading `datasource.url` out of the Prisma 7 config is deferred. -- **Reading the Prisma 7 config for anything but `schema`.** Migrations path, seed, and everything else are Prisma 7's business. -- **Monorepo discovery.** Init works in the current directory, as it does today. -- **Prisma 6 SQL schemas, other ORMs, `_prisma_migrations`.** Unchanged from the parallel project's non-goals. -- **Changing what init does to `package.json#type` or `tsconfig.json`.** Init keeps its current merge behaviour in every mode. - -## Place in the larger world - -- **The transition story** is `projects/prisma-8-rc1/parallel-install.md` and the public guide [Prisma ORM 7 to 8 (PostgreSQL)](https://www.prisma.io/docs/guides/upgrade-prisma-orm/postgresql). This project automates the guide's section 1 (set Prisma 7 aside) and section 2 (add Prisma 8) inside `orm init`; sections 3 to 5 stay as they are. -- **The contract source** is `prisma7Schema` from `@prisma/orm-postgres/config`, built by `projects/prisma7-contract-source/` slice 1 and merged in [PR #30287](https://github.com/prisma/orm/pull/30287). Init writes the config line and, before changing the project, runs that source from the installed target package to check the schema (requirement 8). It does not parse the schema itself beyond finding the `datasource` block and its provider. -- **The unified CLI grammar** (`projects/consolidate-clis/cli-consolidation-plan.md` § Prisma 8 and classic Prisma) already says init detects a classic setup and offers a migration path, and that nothing is silently reinterpreted. This project is that paragraph, delivered. -- **`db sign` sets the `db` ref** since [PR #30251](https://github.com/prisma/prisma/pull/30251), so the next-steps text names one command for adoption. -- **Prisma 7 since 7.10.0 discovers `prisma7.config.*` before `prisma.config.*`** (prisma/prisma#30020), and `@prisma/prisma7` publishes the Prisma 7 CLI under the `prisma7` binary. Both are what make the side-by-side setup a rename and a package swap rather than a fork. - -## Cross-cutting requirements - -1. **Detection is explicit, never silent.** Init enters the Prisma 7 path only through `--from-prisma7-schema ` or a yes to one interactive question. The question is asked when init finds a Prisma 7 config, or a `.prisma` file (or directory) at the Prisma 7 default path with a `datasource` block. A no runs init exactly as today. A non-interactive run without the flag runs init as today. -2. **A Prisma 7 config is recognised by the engine's own check.** Init evaluates `prisma.config.ts` (or, after the rename, `prisma7.config.*`) with the same loader the CLI uses and treats a default export without the `$prismaConfig` marker as a Prisma 7 config, which is what `CONFIG.VERSION_MARKER_MISSING` already means. From it init reads `schema` and nothing else. A config that fails to evaluate is reported and init falls back to the default schema path. -3. **Prisma 7 inputs fill defaults only.** The schema path comes from the flag, else the Prisma 7 config's `schema`, else `prisma/schema.prisma`. The target comes from the schema's `datasource.provider`, choosing among the known targets. `--target` may only agree with the provider, or name the database when the provider is not a string literal; with `--from-prisma7-schema` a mismatch is refused before anything is asked or installed, and without it the Prisma 7 question is not asked. `--from-prisma7-schema` together with `--schema-path` or `--authoring` is an error, because those two mean "write a starter schema". -4. **Side-by-side setup happens with consent, in one run.** When the project declares `prisma` at a major below 8, init offers, under the existing `--confirm ` consent: add `@prisma/prisma7` (the published Prisma 7 CLI, pinned to its one version), move `prisma` to Prisma 8, move `@prisma/client` to the same Prisma 7 version because Prisma 7 requires CLI and client to match, rename the Prisma 7 config to `prisma7.config.` and change its `prisma/config` import to `@prisma/prisma7/config`, and rewrite scripts whose command invokes `prisma` so they invoke `prisma7`. Declined consent stops init before any write. The only change before it is the check's install (requirement 8): any error after the check, including a consent that cannot be given, names the packages the check added and the command that removes them. A project already on `@prisma/prisma7` skips the offer. -5. **Nothing Prisma 7 owns is replaced or deleted.** The Prisma 7 schema is never in init's replace list, `prisma/` is never written to, `prisma/migrations` is never touched, and the Prisma 7 client output is never removed. Init's own files go under `src/prisma/`. -6. **Everything else is init as today.** Same `prisma.config.ts` shape with one different `contract:` line and no starter schema, same `db.ts` beside the emitted artifacts, same tsconfig, gitignore, gitattributes, and `package.json` merges, same install of the target package and `prisma@latest`, same spawned `contract emit`, same re-init consent for files init wrote before. The result document and the human output name every file written and every package changed. -7. **Next steps are the transition routine.** Set `DATABASE_URL`; `prisma db sign`; move routes one at a time; after each `prisma7 migrate dev`, `prisma contract emit` then `prisma db sign`. `prisma7 generate` is listed when `@prisma/client` was moved. There is no cutover step: init lists only what the user runs right after it. -8. **Init checks the schema before it changes the project.** Once the target is known and before any consent question, init installs the target package and `dotenv`, loads the package's `/config` entrypoint from the project, and runs its `prisma7Schema` source without writing. A refusal stops init with the source's diagnostics; the project is unchanged apart from those two packages, and the error names the command that removes them. A target package without `prisma7Schema` turns a run entered through the question into a fresh init, announced by a warning before the next question, and refuses a run given `--from-prisma7-schema`. - -## Transitional-shape constraints - -- **Postgres lands first.** Only `@prisma/orm-postgres/config` exports `prisma7Schema` today. For a `mongodb` provider, the check finds no source (requirement 8). Once `@prisma/orm-mongo/config` exports `prisma7Schema` (parallel project slice 2), the path works for Mongo with no change to init's logic; the Prisma 7 quick reference `prisma-8.md` still shows Postgres examples and needs a Mongo variant then. Providers with no known target are refused with the list of supported ones. -- **The command stays `orm init`.** The unified CLI mounts it as `init`; no new command is added. -- **Nothing in this project may depend on `prisma`, `@prisma/prisma7`, `@prisma/client`, or `@prisma/get-dmmf`.** Detection reads text and evaluates the user's config; the Prisma 7 packages are only ever named as install targets. The CLI carries no target code: the schema check loads the target package the project installed. - -## Contract impact - -None. - -## Adapter impact - -None. The Postgres and Mongo extensions are consumed through their published `/config` entrypoints only. - -## ADR pointer - -None expected. The `git init` boundary for `orm init` is recorded in `design-notes.md` and should be lifted into the CLI README's init section at close-out. - -## Project definition of done - -Inherits `drive/calibration/dod.md`. Project-specific: - -- [ ] A checked-in Prisma 7 fixture project (schema, `prisma.config.ts` with the `prisma/config` import, `package.json` declaring `prisma` and `@prisma/client` at a 7.x below 7.10.0, scripts calling `prisma migrate dev` and `prisma generate`, `prisma/migrations/`) run through `orm init --from-prisma7-schema prisma/schema.prisma --confirm ` produces the files and `package.json` listed in § At a glance, emits `contract.json` and `contract.d.ts`, and leaves `prisma/` byte-identical. -- [ ] Against a database built from that fixture's Prisma 7 migration SQL, `prisma db sign` then succeeds with zero findings, proving the config init wrote is the one the parallel project's end-to-end proof uses. -- [ ] The interactive run reaches the same result through the two questions; a no to the first question runs init as today; a declined consent to the second writes nothing. -- [ ] A Prisma 7 config that fails to evaluate, a `--target` that disagrees with the provider, an unsupported provider, and a `--from-prisma7-schema` path with no `datasource` block each produce a structured error naming the fix, with nothing written. -- [ ] A schema the target package's source refuses (a `view` block) stops init with the source's diagnostics, and the project is unchanged apart from the target package and `dotenv`. -- [ ] Re-running init on the initialised project asks the usual re-init consent for init's own files and never lists the Prisma 7 schema among them. -- [ ] `packages/1-framework/3-tooling/cli/README.md` documents the Prisma 7 path of `orm init`, including the `git init` boundary, and the docs brief in `docs-brief-module-settings.md` has been handed to the `prisma/web` docs owner. - -## Open questions - -None; the questions raised on 2026-09-16 are answered in `design-notes.md` D10. Two points were decided by the orchestrator within Will's "generalise the side-by-side setup" ruling and are flagged for veto in `design-notes.md`: the consent also covers the config rename and the script rewrite, and `@prisma/client` is moved to the Prisma 7 CLI's version. - -## References - -- `docs-brief-module-settings.md` in this directory. -- `projects/prisma7-contract-source/` (worktree `prisma-schema-contract-converter-04eaed`, PR #30287): `spec.md`, `slices/01-postgres-source/spec.md`, `slices/04-prisma7-adoption-example/spec.md`, `examples/prisma7-adoption/`. -- `projects/consolidate-clis/cli-consolidation-plan.md` § Prisma 8 and classic Prisma. -- `packages/1-framework/3-tooling/config-loader/src/load.ts` (marker check), `packages/1-framework/3-tooling/cli/src/orm/init-inputs.ts`, `init-scaffold.ts`, `init-emit.ts`, `commands/init/hygiene-package-scripts.ts`, `commands/init/templates/code-templates.ts`. From 53b7fd9363417e02376f0c18316b3a252635e346 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 24 Sep 2026 15:59:16 +0200 Subject: [PATCH 5/8] docs: module settings page and init README corrected after review module preserve needs TypeScript 5.4; init also adds node to compilerOptions.types; the attribute-free JSON import only works where db.ts runs as CommonJS; and the README line about the side-by-side consent no longer claims init edits no other file it did not write. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/reference/typescript-module-settings.md | 8 ++++---- packages/1-framework/3-tooling/cli/README.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/reference/typescript-module-settings.md b/docs/reference/typescript-module-settings.md index cd94d6a5a97d..6b05ac362390 100644 --- a/docs/reference/typescript-module-settings.md +++ b/docs/reference/typescript-module-settings.md @@ -16,15 +16,15 @@ Prisma 8 does not require `NodeNext`. Under `"module": "NodeNext"` with `"type": | Project | Settings | Notes | | --- | --- | --- | | Already on an ESM-capable `module` | Keep it; add `resolveJsonModule` and the `include` entry | Nothing else changes. | -| CommonJS, staying CommonJS | `"module": "nodenext"` and `resolveJsonModule`; `db.ts` imports the JSON without the `with { type: "json" }` attribute | No `"type": "module"`, no `.js` extensions, no import attribute. | -| Running through `tsx`, `vite`, `next`, `esbuild`, or another bundler | `"module": "preserve"`, `"moduleResolution": "bundler"`, `resolveJsonModule` | Accepts extensionless imports and the import attribute. This is what `prisma orm init` writes. | +| CommonJS, staying CommonJS | `"module": "nodenext"` and `resolveJsonModule`; `db.ts` imports the JSON without the `with { type: "json" }` attribute | No `"type": "module"`, no `.js` extensions, no import attribute. The attribute-free import works only because `nodenext` emits `db.ts` as CommonJS here; a file emitted as an ES module needs the attribute. | +| Running through `tsx`, `vite`, `next`, `esbuild`, or another bundler | `"module": "preserve"`, `"moduleResolution": "bundler"`, `resolveJsonModule` | Accepts extensionless imports and the import attribute. Needs TypeScript 5.4 or later, the release that added `preserve`. This is what `prisma orm init` writes. | | Compiled with `tsc` and run with plain `node` as ES modules | `"module": "NodeNext"` | Every relative import needs its `.js` extension. | ## What `prisma orm init` writes -- It merges `module: 'preserve'`, `moduleResolution: 'bundler'`, and `resolveJsonModule: true` into an existing `tsconfig.json` (`packages/1-framework/3-tooling/cli/src/commands/init/templates/tsconfig.ts`). It does this whatever the project's current `module` setting is, and reports only that it updated the file. +- It merges `module: 'preserve'`, `moduleResolution: 'bundler'`, and `resolveJsonModule: true` into an existing `tsconfig.json`, and adds `node` to `compilerOptions.types` while keeping the entries already there, so `process.env` typechecks in a project with an explicit `types` list (`packages/1-framework/3-tooling/cli/src/commands/init/templates/tsconfig.ts`). It does this whatever the project's current `module` setting is, and reports only that it updated the file. - It scaffolds `db.ts` with the `with { type: 'json' }` import (`templates/code-templates.ts`). -- When `package.json` declares a `"type"` other than `"module"`, it keeps the user's value and warns that `db.ts` will not load under it (`commands/init/hygiene-package-scripts.ts`). For a CommonJS project, the attribute-free import from the table above would load in both module systems. +- When `package.json` declares a `"type"` other than `"module"`, it keeps the user's value and warns that `db.ts` will not load under it (`commands/init/hygiene-package-scripts.ts`). For a CommonJS project, the attribute-free import from the table above would load, because the file runs as CommonJS; Node requires the attribute when the file runs as an ES module. ## How the CommonJS option was verified diff --git a/packages/1-framework/3-tooling/cli/README.md b/packages/1-framework/3-tooling/cli/README.md index 0cfe99507375..8315e402aa67 100644 --- a/packages/1-framework/3-tooling/cli/README.md +++ b/packages/1-framework/3-tooling/cli/README.md @@ -104,7 +104,7 @@ It then writes `prisma.config.ts` with `contract: prisma7Schema("") - There is no separate upgrade command. Init already installs, scaffolds, and emits. A command that automated the whole upgrade guide could not find its inputs reliably in arbitrary projects (computed config values, multi-file schemas, monorepos, CI files that call `prisma migrate`), and the Prisma 7 contract source removes the need for a schema converter. - From a Prisma 7 config init reads only `schema`. The new config connects with `process.env['DATABASE_URL']!`: copying the Prisma 7 `datasource.url` expression would need a TypeScript rewrite of user code, and matching its resolved value back to an environment variable assumes the URL came from one. - Init's files go under `src/prisma/`, where a fresh init puts them, so an upgraded project is shaped like a new one. `prisma/` belongs to Prisma 7. -- The side-by-side edits are the only edits init makes to files it did not write, all under one consent. Renaming the config alone would leave scripts calling a `prisma` binary that is now Prisma 8, and `@prisma/client` moves with the Prisma 7 CLI because Prisma 7 requires both at the same version. +- The Prisma 7-specific edits (renaming the config, changing its import, rewriting scripts that call `prisma`, moving the Prisma 7 packages) all happen under one consent; the file merges a fresh init makes happen as usual. Renaming the config alone would leave scripts calling a `prisma` binary that is now Prisma 8, and `@prisma/client` moves with the Prisma 7 CLI because Prisma 7 requires both at the same version. - `package.json#type` and `tsconfig.json` are handled as on a fresh init; see [TypeScript module settings for Prisma 8 projects](../../../../docs/reference/typescript-module-settings.md). - There is no cutover step. The next steps list only what the user runs right after init. - The schema check runs the target package's source inside the init process, so it uses the control stack of the CLI that invoked init, not the project's installed `prisma`. Published releases pin the two to the same version; an older CLI running against a newer target package can refuse a valid schema. From 1f77e755bd102a07e34ac23e0f47d62a86538452 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 24 Sep 2026 16:12:11 +0200 Subject: [PATCH 6/8] docs: init README scopes the config import rewrite; QA pre-run names what to rebuild Init renames a Prisma 7 config that does not import prisma/config with its imports unchanged and warns; the README now says so. The QA pre-run note says to rebuild every workspace package the run executes and to run the rebuilt binary. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- drive/qa/README.md | 2 +- packages/1-framework/3-tooling/cli/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drive/qa/README.md b/drive/qa/README.md index 6d135e5d3a46..66d751c2072b 100644 --- a/drive/qa/README.md +++ b/drive/qa/README.md @@ -27,7 +27,7 @@ Manual QA reads: A clean pre-QA tree means `pnpm typecheck && pnpm test:packages && pnpm fixtures:check` all green. QA against an unverified tree wastes the runner's time discovering broken assertions that a 1-minute `pnpm test:packages` would have surfaced. -When the QA project installs published builds made from `main` (for example a package's `dev` dist-tag), merge the current `origin/main` into the branch and rebuild before the run. A branch that is behind `main`, run against packages built from a later `main`, fails in ways neither side fails alone. (Added 2026-09-24: a Prisma 7 schema check on a branch based on a week-old `main` refused `@default(false)`, because the branch's CLI lacked a lookup the `dev` target package expected. The same run on the merged branch passed.) +When the QA project installs published builds made from `main` (for example a package's `dev` dist-tag), merge the current `origin/main` into the branch, rebuild every workspace package the run executes (`pnpm build`, or `pnpm --filter build` for each one), and run the rebuilt binary, not one installed earlier. A branch that is behind `main`, run against packages built from a later `main`, fails in ways neither side fails alone. (Added 2026-09-24: a Prisma 7 schema check on a branch based on a week-old `main` refused `@default(false)`, because the branch's CLI lacked a lookup the `dev` target package expected. The same run on the merged branch passed.) ## Sample coherence, not just placement, for content-preamble dispatches diff --git a/packages/1-framework/3-tooling/cli/README.md b/packages/1-framework/3-tooling/cli/README.md index 8315e402aa67..597e63dfb596 100644 --- a/packages/1-framework/3-tooling/cli/README.md +++ b/packages/1-framework/3-tooling/cli/README.md @@ -93,7 +93,7 @@ Before any consent question or file change, init checks that Prisma 8 can read t The second question is the consent token; `--confirm ` answers it non-interactively. Under it init: -- renames the Prisma 7 config to `prisma7.config.` and points its `prisma/config` import at `@prisma/prisma7/config`; +- renames the Prisma 7 config to `prisma7.config.` and points its `prisma/config` import at `@prisma/prisma7/config`; a config that does not import `prisma/config` is renamed with its imports unchanged, and init warns that any other import of the Prisma 7 config helper must be pointed at `@prisma/prisma7/config` by hand; - rewrites every `package.json` script that invokes `prisma` to invoke `prisma7` (scripts init adds keep `prisma`); - installs `@prisma/prisma7@7` as a development dependency alongside `prisma@latest`, and `@prisma/client@7` when the project declares a client below the 7 line. From 91c9965034a9a529324d5e4633fab77e9bb3faf8 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 24 Sep 2026 20:23:26 +0200 Subject: [PATCH 7/8] drive: land the remaining orm-init-prisma7-detection retro lessons drive/spec/README.md asks for a failure-state section in specs of commands that edit files they did not write. drive/project/README.md asks plans to name the release that ships each dependency to users. drive/retro/README.md records the retro in its pattern catalogue. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- drive/project/README.md | 1 + drive/retro/README.md | 1 + drive/spec/README.md | 1 + 3 files changed, 3 insertions(+) diff --git a/drive/project/README.md b/drive/project/README.md index e3cc23db1046..1815f30bc4ad 100644 --- a/drive/project/README.md +++ b/drive/project/README.md @@ -30,6 +30,7 @@ A stacked PR is justified only when its base PR is independently valuable to mer - Linear Project status: update at slice-merge (`drive-deliver-workflow` does this implicitly via `drive-check-health`). - Wider-team comms: optional, operator-set. Use `drive-post-update` for the cadence the project needs. - Cross-team dependencies: surface in the project plan's `Dependencies` section; ping owners explicitly when a dependency is blocking. +- Release dependencies: when users get a feature only through a package release, the plan's `Dependencies` section names that release (package, version, and whether it is published yet), so the plan says when users can use the feature, not only when it merges. (Added 2026-09-24, orm-init-prisma7-detection final retro: the Prisma 7 path of `orm init` merged while the published `@prisma/orm-postgres` still predated the Prisma 7 contract source it needs.) ## ADR cadence diff --git a/drive/retro/README.md b/drive/retro/README.md index 0410d377cf69..3964cf03f896 100644 --- a/drive/retro/README.md +++ b/drive/retro/README.md @@ -19,6 +19,7 @@ When a retro lesson could land in multiple surfaces, the team's preference: Patterns the team has seen multiple times. Each entry: pattern → severity → mitigation surface (where it landed). +- **2026-09-24 — Final orm-init-prisma7-detection retro:** Record an operator ruling as a quote and confirm its interpretation before asking questions that depend on it (F32). Make a test fake return only what the real surface returns (F33). A spec for a command that edits user files names the project's state after each failure; a plan names the release that ships each dependency to users. Merge `main` before manual QA against published builds made from `main`. Manual QA on a real project and an independent review found five bugs the tests missed. - **2026-09-23 — Final remove-dbgenerated retro:** A design that the operator has not settled is not implemented on an assumption; slice B's first PR built codec methods that took PSL text, was withdrawn after review, and was rebuilt on ADR 254 with one blocked decision named in the brief and asked before the code that depended on it. Name every unsettled decision in the brief as a stop, and write the answer into the slice spec before re-delegating. Also: order regeneration of committed generated artefacts before the deletion of the form they use (F30), and rebuild `dist` before trusting a journey failure (F31). - **2026-09-15 — Final editor-project retro:** Editor acceptance must verify the rendered editor, not just HTTP or LSP transport. Record invocation and acceptance separately; verify the applied edit, caret, and visible follow-up suggestions, including required client opt-ins. diff --git a/drive/spec/README.md b/drive/spec/README.md index a2f5bcb5cef4..ede8dbbbf46e 100644 --- a/drive/spec/README.md +++ b/drive/spec/README.md @@ -20,6 +20,7 @@ In addition to the canonical project-spec / slice-spec templates, this repo expe - **Contract-impact section** for any spec that touches the contract surface (`packages/0-shared/contract/**`, `packages/1-framework-core/**`). Names the contract entities affected, the new / changed kinds, the migration plan for downstream consumers. - **Adapter-impact section** for any spec that affects target adapters (`packages/3-targets/**`). Names which adapters are affected (postgres / sqlite / mongo / etc.). - **ADR pointer** for any architectural shift. Either link an existing ADR or commit to authoring one as part of the project's close-out. +- **Failure-state section** for any spec of a command that edits files it did not write. For each way the command can fail after it starts, name the state the user's project is left in, and require that a failure the command can predict is reported before the first edit. (Added 2026-09-24, orm-init-prisma7-detection final retro: `orm init` renamed the Prisma 7 config and rewrote scripts before finding out Prisma 8 could not read the schema, and the problem surfaced only in manual review after five dispatches.) ## Grounding illustrative snippets before execution From f17999bc423f41643e4a6537534f78ea6f160829 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 24 Sep 2026 20:23:27 +0200 Subject: [PATCH 8/8] docs(cli): drop the version-skew line from the orm init design constraints Init installs the latest target package beside the latest CLI, and releases pin the two to the same version, so the mismatch cannot arise for a user. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- packages/1-framework/3-tooling/cli/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/1-framework/3-tooling/cli/README.md b/packages/1-framework/3-tooling/cli/README.md index 597e63dfb596..dd79df43b066 100644 --- a/packages/1-framework/3-tooling/cli/README.md +++ b/packages/1-framework/3-tooling/cli/README.md @@ -107,7 +107,6 @@ It then writes `prisma.config.ts` with `contract: prisma7Schema("") - The Prisma 7-specific edits (renaming the config, changing its import, rewriting scripts that call `prisma`, moving the Prisma 7 packages) all happen under one consent; the file merges a fresh init makes happen as usual. Renaming the config alone would leave scripts calling a `prisma` binary that is now Prisma 8, and `@prisma/client` moves with the Prisma 7 CLI because Prisma 7 requires both at the same version. - `package.json#type` and `tsconfig.json` are handled as on a fresh init; see [TypeScript module settings for Prisma 8 projects](../../../../docs/reference/typescript-module-settings.md). - There is no cutover step. The next steps list only what the user runs right after init. -- The schema check runs the target package's source inside the init process, so it uses the control stack of the CLI that invoked init, not the project's installed `prisma`. Published releases pin the two to the same version; an older CLI running against a newer target package can refuse a valid schema. **Exit codes:** - `0`: set up (and, unless skipped, installed and emitted)