| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
TML-3199: Docs hygiene — dead links, stale API instructions, and the payload label (#30016) # Docs hygiene: dead links, stale API instructions, and the payload label Main-based cleanup (not part of the raw-SQL stack, though its items were surfaced by that campaign's reviews). ## What changed - **ADR-INDEX's dead ADR 035 link** — a pure capitalization mismatch (`Dual authoring conflict resolution` vs the file's `Dual Authoring Conflict Resolution`); corrected to the file's actual name. - **`error-reference.md`: "Meta:" → "Payload:" (260 entries) + one preamble sentence.** The old label was wrong for half the file: `structuredError()` writes `error.meta` but `runtimeError()` writes `error.details`, and a scripted classification showed a per-code label is ill-defined — of the codes classifiable at all, eight are raised through *both* constructors. The neutral label plus a preamble stating the rule (meta from structuredError, details from runtimeError, some codes both ways) is accurate today and stays accurate as raise sites move. No tooling reads the label (verified against `list-error-codes.mjs`). - **Stale `validateContract<Contract>(contractJson)` instruction removed from four surfaces** (`AGENTS.md` § Key Patterns, Testing Guide ×3, the Runtime subsystem doc, and the `typed-contract-in-tests` rulecard) — no such export exists, and the stale pattern had already generated a false review finding. Replacements verified against current code: the client factory hydrates (`postgres<Contract>({ contractJson, url })`), and tests use `validateSqlContractFully<Contract>(contractJson)` (the idiom with 174 current usages). The `validateContract` in `family-instance-domain-actions` is deliberately untouched — that one is the real ADR 204 control-plane primitive, a different thing sharing the name. - **ADR 012's refs clause** now states ADR 205's own conclusion: the unindexed-predicate lint and refs-based budget heuristic ran off the removed sidecar and no longer run for any plan. (The previous wording invited a hunt for a `meta.refs` field that no longer exists.) - **One ticketed item needed nothing**: the four "dead" source links in the Runtime & Middleware doc were already fixed upstream — verified resolving, left alone. ## Known merge note This PR and the raw-SQL stack (#29997) both edit the tail of the same ADR 012 update note, for different reasons. The conflict is one line but **semantic**: whichever lands second must carry both intents (the stack scopes the wire-level-rows claim as historical; this states the refs heuristics gone). Taking either side wholesale silently drops the other. Out of scope, ticketed: 68 further dead links across `docs/` + the missing link checker, two orphaned error-reference entries the one-directional checker cannot see, and ADR 205's own upstream ambiguity (all on TML-3211). Refs: TML-3199 https://claude.ai/code/session_01NnNjsNcPMtbJZhnZz5Zzbe <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated contract hydration and validation guidance to reflect the current workflow. * Refreshed testing and runtime examples for standalone contract usage. * Corrected architecture decision record titles, links, and descriptions of removed raw-plan metadata. * Clarified that contract data can be passed directly through runtime setup. * **Tests** * Updated typed contract fixture guidance to use full SQL contract validation for parsed contract data. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Oleksii Orlenko <robot@aqrln.net> | 10 天前 | |
One config and one command language for the ORM: prisma.config.ts, driven by the unified CLI (#30058) Every surface in this repo now agrees on one config file and one command language. A freshly scaffolded project looks like this: ```ts // prisma.config.ts — the only config file the ORM reads, shared with the unified Prisma CLI import 'dotenv/config'; import { defineConfig } from '@prisma/cli-engine'; import { defineConfig as ormConfig } from '@prisma/orm-postgres/config'; export default defineConfig({ orm: ormConfig({ contract: './src/prisma/contract.prisma', db: { connection: process.env['DATABASE_URL']! }, }), }); ``` and is driven like this: ``` prisma-cli orm init # scaffold (init sits under `orm`; compute owns top-level init) prisma-cli contract emit # every other ORM command is top-level prisma-cli db init prisma-cli migration plan --name first prisma-cli migrate prisma-cli db verify ``` **The decision: the transition period is over.** Until now the loaders still accepted the retired `prisma-next.config.ts` filename and the old un-nested config shape (with deprecation warnings), and this repo still built its own `prisma-next` binary whose command tree didn't match the CLI users actually install. This PR deletes all of it. A config in the old spelling now fails loudly, and the workspace binary is a faithful stand-in for the real host — same command paths, same loader semantics. ## Why now, and what it flushed out The soft fallbacks weren't just clutter — they were hiding bugs. Because the workspace bin loaded config its own way and mounted commands at its own paths, the 22 ORM commands had never once run the way `@prisma/cli` actually runs them. Making the workspace bin mount the family exactly like the shipped host immediately surfaced three real defects, all fixed here: 1. **Mounted commands couldn't construct.** The family's retired-invocation redirects pointed at command paths that only existed in the old standalone tree, so the engine rejected the whole CLI at build time. 2. **Relative config paths crashed every path-consuming command.** The engine's loader hands commands the config exactly as authored, so under the real host `contract.output` arrived as `./src/prisma/contract.json` and `contract emit` died inside `createRequire`. This is the failure Shane hit with `bunx prisma@next orm init` — init succeeds, then the very next command falls over. The ORM command boundary (`defineOrmCommand`) now finalizes contract and migration paths idempotently, so both hosts hand handlers the same absolute paths. 3. **`init` installed a broken toolchain.** It added `@prisma/cli-engine` untagged, which resolves npm's lagging `latest` (0.0.9) instead of the version `@prisma/cli` actually runs against. It now reads the exact engine version from the installed CLI's own manifest. An end-to-end QA run (empty directory → init → emit → `db init` → typed queries → schema change → plan → migrate → verify, against the *published* `@prisma/cli@8.0.0-rc.5` with this branch's toolchain) is green top to bottom. That run also caught a fourth defect: the TypeScript starter contract triggered `PN_CONTRACT_TYPED_FALLBACK_AVAILABLE` warnings on its own first emit; it now uses the typed model-token form the warning recommends. ## What changed, piece by piece - **Loaders**: `@internal/config-loader` and the bin's loader read only `prisma.config.ts` with the `$prismaConfig` envelope. The deprecated-filename discovery, the flat-shape acceptance, the `CONFIG.DEPRECATED_*` codes, and the old Symbol-based format marker are deleted. The telemetry enricher's matching fallbacks too. - **Binary**: the workspace bin is named `prisma` and mounts the family the way the host does — commands top-level, `init` under `orm`. Examples, e2e journeys, and harnesses drive it through those paths, which is what finally puts the mounted tree under test. - **Strings**: every user-facing command string (errors, docs, READMEs, scaffolded scripts, next-step hints) reads `prisma <command>` / `prisma orm init`. - **Ratchet**: `scripts/lint-legacy-name.mjs` now *forbids* `prisma-next.config.ts` repo-wide, so the retired spelling can't creep back. Deliberate residuals stay allowed: `prisma-next.md`, `// use prisma-next` schema headers, `prisma-next-*` skill names, the per-user telemetry dir. - **Upgrade path**: `upgrades/8.0.0-rc.3-to-8.0.0-rc.4/` entries in both skill clusters walk consumers through the rename, the envelope rewrite, the dependency change, and the command grammar. Verified: 15,296 package tests, 2,075 integration tests, fixtures regenerated and stable, all repo lints green. One honest gap: the upgrade-instruction entries were authored from the applied diff, not validated by the full revert-and-replay flow. ## Alternatives considered - **Keep the deprecation fallbacks another release.** Rejected: every surface that still worked under the old spelling postponed exactly the couplings this change needed to surface, and the fallback paths themselves had no coverage in the real host. - **Mount the workspace commands under `prisma orm <command>`.** This PR briefly did that — the config *section* is named `orm`, so it looked right. The published rc.5 host proved otherwise: its tree is top-level with only `init` nested. The workspace bin now copies the host instead of guessing. - **Fix path finalization in the engine instead.** The cleaner home would be the engine handing validators the config file's path, but that's a prisma-cli-repo API change. The command-boundary fix works with today's engine, is idempotent, and stays correct if the engine later finalizes upstream. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 23 天前 | |
migration plan refuses to silently plan from an empty database when migrations exist (#30122) Here is the failure this PR removes. A project has one migration on disk and asks for the next one: ```console $ prisma migration plan --name add-user-role ✔ Planned migration 20260824_add-user-role from: (baseline) to: f3a9c1… ``` That plan looks fine and exits 0 — but `from: (baseline)` means "starting from an empty database", so the package contains `CREATE TABLE` for the entire schema. Applying it to any real database fails on the first statement. Nothing warned. A field tester shipped exactly this; our own gotchas file documents the trap. **The decision: `migration plan` now refuses to plan from an empty database when migrations already exist, unless you explicitly ask for that.** Silence becomes a structured error: ```console $ prisma migration plan --name add-user-role ✖ MIGRATION.PLAN_ORIGIN_UNKNOWN: no starting point for this plan. → Record where your database is: prisma migration ref set db 8c2fe0… → Or name the starting contract: prisma migration plan --from 8c2fe0… → Or plan from an empty database on purpose: --from @empty ``` To see why the silence existed, follow how `plan` picks its starting point. It is deliberately offline — it never connects to a database — so it reads the starting contract from `--from` if given, otherwise from the `db` ref: a small committed file recording which contract your dev database has been brought to. `db init` and `db update` maintain that file as you iterate. But a project that never runs those commands (the Composer-style workflow, where deploys apply migrations) never has a `db` ref — and plan's last resort was "assume empty database", silently, even with history sitting on disk. First plans are the legitimate case for that assumption, so the refusal only fires when migrations already exist on disk; a first plan in a fresh project still proceeds silently, including the auto-baseline (the from-empty starter migration `plan` writes alongside your first real change). The refusal immediately proved its worth inside this repo: three e2e journeys were walking the exact trap — planning follow-up migrations with no ref and no `--from`, producing from-empty migrations while their comments claimed incremental ones. The divergence journey was not testing divergence at all. They now chain with `--from` and prove what they claim. Also here: the gotchas entry is marked resolved; the roadmap's stale-ref data-loss item is narrowed, not closed (a ref that exists but points at the wrong contract is a different case this refusal does not cover); and a docs sweep against ADR 218 fixed six statements teaching the old behavior — four claiming `db update` advances no ref, two describing `migration plan` as advancing refs (that is TML-2560, still unimplemented; they now say so). Verification: 1436 CLI tests, 124 journey tests, typecheck, lint, and the error-reference check all green; every behavior above exercised against the built binary. **Alternatives considered.** *Warn instead of refuse*: a warning above a plausible-looking plan gets scrolled past — and the roadmap already classifies the sibling case as data-loss risk; an error with exits gets acted on, by agents especially. *Advance the ref at plan time so the situation can't arise*: rejected — the `db` ref means "where the dev database has been brought", and moving it for an unapplied plan corrupts that meaning; plan-time advancement as an explicit flag is tracked separately (TML-2560). *Teach the workflow and change nothing*: the prisma-8 skill rewrite does teach it (sibling PR), but the error channel reaches whoever the skill doesn't. Coordination: the sibling skill PR truthfully states today's lack of this refusal (one doc line, one journey-test assertion). Whichever PR merges second updates those two spots. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added `@empty` as the supported origin for planning migrations from an empty database. * `db update` on the default development database now advances the `db` reference automatically. * **Bug Fixes** * Migration planning now reports `MIGRATION.PLAN_ORIGIN_UNKNOWN` when migrations exist without a known origin. * Prevented `@empty` from being used as a migration destination. * **Documentation** * Updated CLI help, error guidance, roadmap, and migration documentation to reflect the new behavior and terminology. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 16 天前 | |
ci: shard package tests without weakening coverage gates (#30156) ## Linked issue n/a — infrastructure change without a Linear ticket. ## At a glance ```yaml # Package Tests (1/4 ... 4/4) - run: pnpm coverage:packages --reporter=blob --shard=${{ matrix.index }}/4 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # Coverage - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: pattern: package-coverage-* merge-multiple: true - run: pnpm coverage:packages:merge - run: pnpm coverage:report ``` Package tests now execute on four runners, while a separate `Coverage` job evaluates thresholds once against the combined native Vitest report. `Test Examples` runs concurrently, and a lightweight final `Test` job preserves the required status name. ## Summary The package-test portion of `Test` was the remaining serial CI bottleneck. This change shards that work horizontally while keeping one authoritative coverage gate and the existing required `Test` status context. ## Decision This PR ships three connected CI changes: 1. Run package tests and V8 coverage across four native Vitest shards, each uploading one uniquely named blob artifact. 2. Download and require all four blob reports in a separate `Coverage` fan-in job, merge their coverage counters with Vitest, and only then apply the existing package-level thresholds and warning policy. 3. Run examples concurrently and preserve assertion failures, inert-diff behavior, and the required `Test` check name through a lightweight final gate. ## Reviewer notes - Shards use SHA-pinned `actions/upload-artifact`; `Coverage` uses SHA-pinned `actions/download-artifact` with `pattern: package-coverage-*` and `merge-multiple: true`. Both are GitHub-created actions in the `actions` organization, so the existing GitHub-actions category permits them without individual allow-list entries. - Uploads set `include-hidden-files: true` because Vitest writes blobs below `.vitest/blob`, and `if-no-files-found: error` prevents a shard from silently publishing nothing. - GitHub does not expose artifacts from an earlier workflow attempt to rerun jobs. Use **Re-run all jobs**, not **Re-run failed jobs**; a partial rerun fails safely when `Coverage` verifies the four expected files. - Coverage thresholds and file reporters are deliberately disabled only in partial shard processes. The merge process runs without the shard marker and therefore restores the complete policy. - The stable required status remains `Test`; the four `Package Tests (N/4)` jobs, `Coverage`, and `Test Examples` are implementation details behind its final result. - The hosted four-runner transport can only execute in GitHub Actions. A local two-shard smoke test proved Vitest's blob names, merged counters, and final-only 100% threshold behavior. ## How it fits together 1. [`vitest.config.ts`](vitest.config.ts) recognizes shard collection through `VITEST_COVERAGE_SHARD`, keeps the full include/exclude policy, and suppresses only partial-run thresholds and coverage output. 2. [`.github/workflows/ci.yml`](.github/workflows/ci.yml) runs `vitest --coverage --reporter=blob --shard=N/4` on four PostgreSQL-backed runners. Each shard still reports test failures, uploads exactly one hidden blob file, and explicitly propagates a failing outcome after the upload. 3. `Coverage` downloads all `package-coverage-*` artifacts into `.vitest/blob` and checks for `blob-1-4.json` through `blob-4-4.json` before doing any merge. 4. [`pnpm coverage:packages:merge`](package.json) invokes Vitest's native `--merge-reports` path, which combines Istanbul counters rather than averaging percentages and replays failed tests. 5. The existing [`pnpm coverage:report`](scripts/coverage-report.mjs) attributes merged source entries to packages and enforces their thresholds. `Test Examples` runs concurrently, while the final `Test` job fails if any package shard, coverage, example, or prerequisite job failed. ## Behavior changes & evidence - **Package tests execute across four CI runners instead of one.** The matrix and fan-in are in [`.github/workflows/ci.yml`](.github/workflows/ci.yml), with the expected orchestration locked by [`scripts/coverage-config.test.mjs`](scripts/coverage-config.test.mjs). - **Coverage gates see the complete combined run.** Shard-aware configuration lives in [`vitest.config.ts`](vitest.config.ts), while the native merge command is declared in [`package.json`](package.json) and existing package aggregation remains in [`scripts/coverage-report.mjs`](scripts/coverage-report.mjs). - **Missing shards, assertion failures, and partial reruns cannot silently pass.** Unique artifact names, hidden-file uploads, all four required filenames, and the final failure fan-in are asserted by [`scripts/coverage-config.test.mjs`](scripts/coverage-config.test.mjs). - **The CI contract is documented for future changes.** The rationale and operational flow are recorded in [`docs/oss/ci-pipeline.md`](docs/oss/ci-pipeline.md) and the package coverage guides. ## Testing performed - `pnpm build` — 85 tasks passed - `pnpm test:scripts` — 498 tests passed - `node --test scripts/coverage-config.test.mjs scripts/coverage-report.test.mjs` — 34 tests passed - `pnpm lint:workflows` - `pnpm exec biome check vitest.config.ts scripts/coverage-config.test.mjs package.json turbo.json` - `pnpm exec turbo run build --dry=json` - Parsed `.github/workflows/ci.yml` with the installed `yaml` package - `git diff --check` - Synthetic two-shard Vitest 5 smoke test — generated both expected blob files, merged both source maps, replayed both tests, and passed combined 100% thresholds ## Skill update n/a — internal CI orchestration only; no user-facing CLI, API, configuration, error, or terminology changes. ## Alternatives considered - **Use cache transport:** cache prefix matching restores only one matching entry rather than all shard outputs, which would require four explicit restores. Cache fallback also suggests cross-attempt reuse that GitHub's artifact model intentionally avoids. - **Merge raw JSON manually:** Vitest's blob merger already preserves test failures, project metadata, and Istanbul hit counters, avoiding a custom coverage-merging implementation. - **Apply thresholds in every shard:** each shard sees only partial execution, so this would create false failures and would not represent repository coverage. - **Keep the single runner and increase workers:** package coverage is already worker-capped to protect PGlite/PostgreSQL stability; horizontal runners improve wall time without oversubscribing one machine. ## Checklist - [x] All commits are signed off (`git commit -s`) per the DCO. - [x] I read `CONTRIBUTING.md` and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form — n/a, this infrastructure change has no Linear ticket and follows the repository's conventional-title precedent. - [x] The **Skill update** section is filled in. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Package tests now run across four parallel CI shards. * Added coverage report merging for sharded test runs. * Coverage thresholds are applied after all shard results are combined. * Example tests now run as a dedicated CI check. * **Documentation** * Updated testing and CI guides with the new sharded coverage workflow and command. * **Chores** * Excluded Vitest cache files from version control. * Improved CI checks and diagnostics for incomplete or failed test shards. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 13 天前 | |
ci: shard package tests without weakening coverage gates (#30156) ## Linked issue n/a — infrastructure change without a Linear ticket. ## At a glance ```yaml # Package Tests (1/4 ... 4/4) - run: pnpm coverage:packages --reporter=blob --shard=${{ matrix.index }}/4 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # Coverage - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: pattern: package-coverage-* merge-multiple: true - run: pnpm coverage:packages:merge - run: pnpm coverage:report ``` Package tests now execute on four runners, while a separate `Coverage` job evaluates thresholds once against the combined native Vitest report. `Test Examples` runs concurrently, and a lightweight final `Test` job preserves the required status name. ## Summary The package-test portion of `Test` was the remaining serial CI bottleneck. This change shards that work horizontally while keeping one authoritative coverage gate and the existing required `Test` status context. ## Decision This PR ships three connected CI changes: 1. Run package tests and V8 coverage across four native Vitest shards, each uploading one uniquely named blob artifact. 2. Download and require all four blob reports in a separate `Coverage` fan-in job, merge their coverage counters with Vitest, and only then apply the existing package-level thresholds and warning policy. 3. Run examples concurrently and preserve assertion failures, inert-diff behavior, and the required `Test` check name through a lightweight final gate. ## Reviewer notes - Shards use SHA-pinned `actions/upload-artifact`; `Coverage` uses SHA-pinned `actions/download-artifact` with `pattern: package-coverage-*` and `merge-multiple: true`. Both are GitHub-created actions in the `actions` organization, so the existing GitHub-actions category permits them without individual allow-list entries. - Uploads set `include-hidden-files: true` because Vitest writes blobs below `.vitest/blob`, and `if-no-files-found: error` prevents a shard from silently publishing nothing. - GitHub does not expose artifacts from an earlier workflow attempt to rerun jobs. Use **Re-run all jobs**, not **Re-run failed jobs**; a partial rerun fails safely when `Coverage` verifies the four expected files. - Coverage thresholds and file reporters are deliberately disabled only in partial shard processes. The merge process runs without the shard marker and therefore restores the complete policy. - The stable required status remains `Test`; the four `Package Tests (N/4)` jobs, `Coverage`, and `Test Examples` are implementation details behind its final result. - The hosted four-runner transport can only execute in GitHub Actions. A local two-shard smoke test proved Vitest's blob names, merged counters, and final-only 100% threshold behavior. ## How it fits together 1. [`vitest.config.ts`](vitest.config.ts) recognizes shard collection through `VITEST_COVERAGE_SHARD`, keeps the full include/exclude policy, and suppresses only partial-run thresholds and coverage output. 2. [`.github/workflows/ci.yml`](.github/workflows/ci.yml) runs `vitest --coverage --reporter=blob --shard=N/4` on four PostgreSQL-backed runners. Each shard still reports test failures, uploads exactly one hidden blob file, and explicitly propagates a failing outcome after the upload. 3. `Coverage` downloads all `package-coverage-*` artifacts into `.vitest/blob` and checks for `blob-1-4.json` through `blob-4-4.json` before doing any merge. 4. [`pnpm coverage:packages:merge`](package.json) invokes Vitest's native `--merge-reports` path, which combines Istanbul counters rather than averaging percentages and replays failed tests. 5. The existing [`pnpm coverage:report`](scripts/coverage-report.mjs) attributes merged source entries to packages and enforces their thresholds. `Test Examples` runs concurrently, while the final `Test` job fails if any package shard, coverage, example, or prerequisite job failed. ## Behavior changes & evidence - **Package tests execute across four CI runners instead of one.** The matrix and fan-in are in [`.github/workflows/ci.yml`](.github/workflows/ci.yml), with the expected orchestration locked by [`scripts/coverage-config.test.mjs`](scripts/coverage-config.test.mjs). - **Coverage gates see the complete combined run.** Shard-aware configuration lives in [`vitest.config.ts`](vitest.config.ts), while the native merge command is declared in [`package.json`](package.json) and existing package aggregation remains in [`scripts/coverage-report.mjs`](scripts/coverage-report.mjs). - **Missing shards, assertion failures, and partial reruns cannot silently pass.** Unique artifact names, hidden-file uploads, all four required filenames, and the final failure fan-in are asserted by [`scripts/coverage-config.test.mjs`](scripts/coverage-config.test.mjs). - **The CI contract is documented for future changes.** The rationale and operational flow are recorded in [`docs/oss/ci-pipeline.md`](docs/oss/ci-pipeline.md) and the package coverage guides. ## Testing performed - `pnpm build` — 85 tasks passed - `pnpm test:scripts` — 498 tests passed - `node --test scripts/coverage-config.test.mjs scripts/coverage-report.test.mjs` — 34 tests passed - `pnpm lint:workflows` - `pnpm exec biome check vitest.config.ts scripts/coverage-config.test.mjs package.json turbo.json` - `pnpm exec turbo run build --dry=json` - Parsed `.github/workflows/ci.yml` with the installed `yaml` package - `git diff --check` - Synthetic two-shard Vitest 5 smoke test — generated both expected blob files, merged both source maps, replayed both tests, and passed combined 100% thresholds ## Skill update n/a — internal CI orchestration only; no user-facing CLI, API, configuration, error, or terminology changes. ## Alternatives considered - **Use cache transport:** cache prefix matching restores only one matching entry rather than all shard outputs, which would require four explicit restores. Cache fallback also suggests cross-attempt reuse that GitHub's artifact model intentionally avoids. - **Merge raw JSON manually:** Vitest's blob merger already preserves test failures, project metadata, and Istanbul hit counters, avoiding a custom coverage-merging implementation. - **Apply thresholds in every shard:** each shard sees only partial execution, so this would create false failures and would not represent repository coverage. - **Keep the single runner and increase workers:** package coverage is already worker-capped to protect PGlite/PostgreSQL stability; horizontal runners improve wall time without oversubscribing one machine. ## Checklist - [x] All commits are signed off (`git commit -s`) per the DCO. - [x] I read `CONTRIBUTING.md` and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form — n/a, this infrastructure change has no Linear ticket and follows the repository's conventional-title precedent. - [x] The **Skill update** section is filled in. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Package tests now run across four parallel CI shards. * Added coverage report merging for sharded test runs. * Coverage thresholds are applied after all shard results are combined. * Example tests now run as a dedicated CI check. * **Documentation** * Updated testing and CI guides with the new sharded coverage workflow and command. * **Chores** * Excluded Vitest cache files from version control. * Improved CI checks and diagnostics for incomplete or failed test shards. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 13 天前 | |
TML-3199: Docs hygiene — dead links, stale API instructions, and the payload label (#30016) # Docs hygiene: dead links, stale API instructions, and the payload label Main-based cleanup (not part of the raw-SQL stack, though its items were surfaced by that campaign's reviews). ## What changed - **ADR-INDEX's dead ADR 035 link** — a pure capitalization mismatch (`Dual authoring conflict resolution` vs the file's `Dual Authoring Conflict Resolution`); corrected to the file's actual name. - **`error-reference.md`: "Meta:" → "Payload:" (260 entries) + one preamble sentence.** The old label was wrong for half the file: `structuredError()` writes `error.meta` but `runtimeError()` writes `error.details`, and a scripted classification showed a per-code label is ill-defined — of the codes classifiable at all, eight are raised through *both* constructors. The neutral label plus a preamble stating the rule (meta from structuredError, details from runtimeError, some codes both ways) is accurate today and stays accurate as raise sites move. No tooling reads the label (verified against `list-error-codes.mjs`). - **Stale `validateContract<Contract>(contractJson)` instruction removed from four surfaces** (`AGENTS.md` § Key Patterns, Testing Guide ×3, the Runtime subsystem doc, and the `typed-contract-in-tests` rulecard) — no such export exists, and the stale pattern had already generated a false review finding. Replacements verified against current code: the client factory hydrates (`postgres<Contract>({ contractJson, url })`), and tests use `validateSqlContractFully<Contract>(contractJson)` (the idiom with 174 current usages). The `validateContract` in `family-instance-domain-actions` is deliberately untouched — that one is the real ADR 204 control-plane primitive, a different thing sharing the name. - **ADR 012's refs clause** now states ADR 205's own conclusion: the unindexed-predicate lint and refs-based budget heuristic ran off the removed sidecar and no longer run for any plan. (The previous wording invited a hunt for a `meta.refs` field that no longer exists.) - **One ticketed item needed nothing**: the four "dead" source links in the Runtime & Middleware doc were already fixed upstream — verified resolving, left alone. ## Known merge note This PR and the raw-SQL stack (#29997) both edit the tail of the same ADR 012 update note, for different reasons. The conflict is one line but **semantic**: whichever lands second must carry both intents (the stack scopes the wire-level-rows claim as historical; this states the refs heuristics gone). Taking either side wholesale silently drops the other. Out of scope, ticketed: 68 further dead links across `docs/` + the missing link checker, two orphaned error-reference entries the one-directional checker cannot see, and ADR 205's own upstream ambiguity (all on TML-3211). Refs: TML-3199 https://claude.ai/code/session_01NnNjsNcPMtbJZhnZz5Zzbe <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated contract hydration and validation guidance to reflect the current workflow. * Refreshed testing and runtime examples for standalone contract usage. * Corrected architecture decision record titles, links, and descriptions of removed raw-plan metadata. * Clarified that contract data can be passed directly through runtime setup. * **Tests** * Updated typed contract fixture guidance to use full SQL contract validation for parsed contract data. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Oleksii Orlenko <robot@aqrln.net> | 10 天前 | |
docs(skills): fix claims-vs-reality drift from the skills audit (TML-3223) (#30088) ## Linked issue Refs TML-3223 part 2 — the skills audit's docs findings. ## Summary Three auditors swept the 18 skills beside `record-upgrade-instructions` and found 40 claims that no longer match the tree. This PR fixes the documentation half: paths that resolve nowhere, examples that outlived the code they cite, and instructions with no runnable form. The corrections are mechanical and wide but shallow — each replaces a claim with what the tree, the scripts, or the PR template actually do: - **Dead references.** The visitor example pointed at a file that dispatches through polymorphic hooks now; it names the live Mongo DDL command set instead, across the three files that set really occupies. `create-pr` sent readers to `.agents/skills/drive-pr-walkthrough/SKILL.md`, which is installed from prisma/ignite and absent here — it now names the skill and says what to do when it is not installed. - **Phantom path segments.** Release-notes recipe URLs carried `skills/upgrade/…` and `skills/extension-author/…`; neither exists, so every migration link 404'd. Fixed in the skill and in `docs/releases/README.md`, along with the `prisma/prisma-next` → `prisma/prisma` slug. - **A template that grew a section.** `contrib-pr` and `create-pr` both enumerate the PR template's headers, and neither mentioned `## Skill update` — whose checkbox their own instructions then tell you to tick. - **Instructions with no runnable form.** Reacting 👍/👎 on a thread and detecting pending reviews each got the exact `gh api graphql` call. - **Smaller corrections.** Two agent files named models the harness cannot resolve; a documented table promised a Linear column its renderer does not emit; the release skill's PR title contradicted its own frontmatter; the biome rename step is now conditional on files that no longer exist. Two findings are held for an operator ruling and are **not** in this PR: `contrib-pr`'s conventional-commit-versus-`TML-NNNN` title-policy collision (the repo's own surfaces disagree), and renaming `skills-contrib/record-gotcha/` to match its installed plural name. `record-gotcha`'s broken bootstrap link and MCP plugin naming are fixed here. ## Testing performed `pnpm lint:skills` (green), `pnpm rules:sync` (no-op). ## Skill update This PR is entirely skill maintenance — it corrects 13 skill documents plus `docs/releases/README.md`. ## Notes for the reviewer Wide but shallow by design: 15 files, ~66 lines changed, no behavior. The script-side findings ship separately in the companion PR so this one stays reviewable as prose. https://claude.ai/code/session_01NnNjsNcPMtbJZhnZz5Zzbe <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated release-note guidance for migration links, transition labels, prerelease versions, and fallback examples. * Clarified AST, architecture, package export, Biome, and release-process instructions. * Refined contribution and pull request guidance, including templates and branch handling. * Improved review workflow guidance for pending reviews, reactions, action scaffolding, and output conventions. * Corrected integration references, repository links, skill paths, and checkout requirements. * Added guidance for documenting breaking changes when migration recipes are unavailable. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Oleksii Orlenko <robot@aqrln.net> | 10 天前 | |
One config and one command language for the ORM: prisma.config.ts, driven by the unified CLI (#30058) Every surface in this repo now agrees on one config file and one command language. A freshly scaffolded project looks like this: ```ts // prisma.config.ts — the only config file the ORM reads, shared with the unified Prisma CLI import 'dotenv/config'; import { defineConfig } from '@prisma/cli-engine'; import { defineConfig as ormConfig } from '@prisma/orm-postgres/config'; export default defineConfig({ orm: ormConfig({ contract: './src/prisma/contract.prisma', db: { connection: process.env['DATABASE_URL']! }, }), }); ``` and is driven like this: ``` prisma-cli orm init # scaffold (init sits under `orm`; compute owns top-level init) prisma-cli contract emit # every other ORM command is top-level prisma-cli db init prisma-cli migration plan --name first prisma-cli migrate prisma-cli db verify ``` **The decision: the transition period is over.** Until now the loaders still accepted the retired `prisma-next.config.ts` filename and the old un-nested config shape (with deprecation warnings), and this repo still built its own `prisma-next` binary whose command tree didn't match the CLI users actually install. This PR deletes all of it. A config in the old spelling now fails loudly, and the workspace binary is a faithful stand-in for the real host — same command paths, same loader semantics. ## Why now, and what it flushed out The soft fallbacks weren't just clutter — they were hiding bugs. Because the workspace bin loaded config its own way and mounted commands at its own paths, the 22 ORM commands had never once run the way `@prisma/cli` actually runs them. Making the workspace bin mount the family exactly like the shipped host immediately surfaced three real defects, all fixed here: 1. **Mounted commands couldn't construct.** The family's retired-invocation redirects pointed at command paths that only existed in the old standalone tree, so the engine rejected the whole CLI at build time. 2. **Relative config paths crashed every path-consuming command.** The engine's loader hands commands the config exactly as authored, so under the real host `contract.output` arrived as `./src/prisma/contract.json` and `contract emit` died inside `createRequire`. This is the failure Shane hit with `bunx prisma@next orm init` — init succeeds, then the very next command falls over. The ORM command boundary (`defineOrmCommand`) now finalizes contract and migration paths idempotently, so both hosts hand handlers the same absolute paths. 3. **`init` installed a broken toolchain.** It added `@prisma/cli-engine` untagged, which resolves npm's lagging `latest` (0.0.9) instead of the version `@prisma/cli` actually runs against. It now reads the exact engine version from the installed CLI's own manifest. An end-to-end QA run (empty directory → init → emit → `db init` → typed queries → schema change → plan → migrate → verify, against the *published* `@prisma/cli@8.0.0-rc.5` with this branch's toolchain) is green top to bottom. That run also caught a fourth defect: the TypeScript starter contract triggered `PN_CONTRACT_TYPED_FALLBACK_AVAILABLE` warnings on its own first emit; it now uses the typed model-token form the warning recommends. ## What changed, piece by piece - **Loaders**: `@internal/config-loader` and the bin's loader read only `prisma.config.ts` with the `$prismaConfig` envelope. The deprecated-filename discovery, the flat-shape acceptance, the `CONFIG.DEPRECATED_*` codes, and the old Symbol-based format marker are deleted. The telemetry enricher's matching fallbacks too. - **Binary**: the workspace bin is named `prisma` and mounts the family the way the host does — commands top-level, `init` under `orm`. Examples, e2e journeys, and harnesses drive it through those paths, which is what finally puts the mounted tree under test. - **Strings**: every user-facing command string (errors, docs, READMEs, scaffolded scripts, next-step hints) reads `prisma <command>` / `prisma orm init`. - **Ratchet**: `scripts/lint-legacy-name.mjs` now *forbids* `prisma-next.config.ts` repo-wide, so the retired spelling can't creep back. Deliberate residuals stay allowed: `prisma-next.md`, `// use prisma-next` schema headers, `prisma-next-*` skill names, the per-user telemetry dir. - **Upgrade path**: `upgrades/8.0.0-rc.3-to-8.0.0-rc.4/` entries in both skill clusters walk consumers through the rename, the envelope rewrite, the dependency change, and the command grammar. Verified: 15,296 package tests, 2,075 integration tests, fixtures regenerated and stable, all repo lints green. One honest gap: the upgrade-instruction entries were authored from the applied diff, not validated by the full revert-and-replay flow. ## Alternatives considered - **Keep the deprecation fallbacks another release.** Rejected: every surface that still worked under the old spelling postponed exactly the couplings this change needed to surface, and the fallback paths themselves had no coverage in the real host. - **Mount the workspace commands under `prisma orm <command>`.** This PR briefly did that — the config *section* is named `orm`, so it looked right. The published rc.5 host proved otherwise: its tree is top-level with only `init` nested. The workspace bin now copies the host instead of guessing. - **Fix path finalization in the engine instead.** The cleaner home would be the engine handing validators the config file's path, but that's a prisma-cli-repo API change. The command-boundary fix works with today's engine, is idempotent, and stays correct if the engine later finalizes upstream. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 23 天前 | |
Structured-error docs URLs move from orm/next to orm/v8 (#30126) ## Linked issue n/a — small change. This is the version-segment flip that [ADR 239](docs/architecture%20docs/adrs/ADR%20239%20-%20Errors%20are%20structural%20envelopes%20with%20dotted%20namespace%20codes.md) planned for the RC ("the `next` segment flips to `v8` when the RC ships"). ## At a glance ```ts export const DOCS_ERRORS_VERSION = 'v8'; export const DOCS_BASE = `https://docs.prisma.io/docs/orm/${DOCS_ERRORS_VERSION}/reference/error-reference`; ``` Before this PR the segment was `next`, so every structured error's `docsUrl` pointed at `docs.prisma.io/docs/orm/next/...`. ## Decision Flip `DOCS_ERRORS_VERSION` from `next` to `v8`. ADR 239 centralized the docs URL behind this one constant precisely so the RC flip would be a one-line edit; this PR makes that edit and updates the two docs pages that spelled out the old URL. ## Behavior changes & evidence - Every structured error's `docsUrl` (and `docsUrlFor(code)`) now links to `https://docs.prisma.io/docs/orm/v8/reference/error-reference#<CODE>`. Implementation: [structured-error.ts](packages/1-framework/0-foundation/utils/src/structured-error.ts). Evidence: [structured-error.test.ts](packages/1-framework/0-foundation/utils/test/structured-error.test.ts) asserts the full v8 URL. - [docs/reference/error-reference.md](docs/reference/error-reference.md) and [docs/CLI Style Guide.md](docs/CLI%20Style%20Guide.md) now state the v8 URL; the "flips to v8 at RC" note is dropped since the flip has happened. ## Reviewer notes - The hosted docs page at `docs.prisma.io/docs/orm/v8/reference/error-reference` must exist for these links to resolve. If the docs site hasn't published the v8 path yet, hold this until it has. ## Testing performed - `pnpm test` in `packages/1-framework/0-foundation/utils` — 162 tests, no type errors, all green. ## Skill update n/a — no skill spells out the docs URL (checked `packages/0-shared/skills/`). ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form — no Linear ticket exists for this flip; the title names the deliverable directly. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated CLI error guidance to link to the Prisma ORM v8 error reference. * Updated the error reference introduction with the direct v8 documentation URL. * Aligned architecture documentation with the finalized v8 error-reference URL. * **Bug Fixes** * Error messages now generate links to the v8 error documentation instead of the previous preview path. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 16 天前 | |
feat: an application depends on one Prisma package (ADR 242) (#29864) ## What changes for someone using Prisma Today, an application that talks to Postgres installs a long list of our packages: ```jsonc { "dependencies": { "@prisma-next/postgres": "...", "@prisma-next/sql-runtime": "...", "@prisma-next/sql-orm-client": "...", "@prisma-next/target-postgres": "...", "@prisma-next/adapter-postgres": "...", "@prisma-next/sql-contract": "..." // ...a dozen more } } ``` After this PR, it installs one: ```jsonc { "dependencies": { "@prisma/orm-postgres": "0.16.0" } } ``` Everything else arrives as that package's own dependencies. Three of our example apps are converted in this PR to prove it — one per database — and each has exactly one Prisma package in its `dependencies`. This implements [ADR 242](https://github.com/prisma/prisma/pull/29852), which is already merged. ## What gets published 17 packages, all under the `@prisma` scope: - **3 database packages** — `@prisma/orm-postgres`, `orm-sqlite`, `orm-mongo`. An application installs exactly one. We call these *facades*: each is a small package that wires its database together and re-exports everything an application needs. - **6 extension packs** — PostGIS, pgvector, ParadeDB, Supabase, arktype-json, middleware-cache. Optional, installed alongside a database package. - **7 platform packages** — the framework, the toolchain, one per database family, one per database target. Applications never install these directly; they arrive as dependencies. Extension authors do install them. - **the `prisma` command**, as a bin-only package. Every other workspace package — around 50 of them — stops being published. They still exist in the repo as the unit we organise code in; they just stop having a life on the registry. **This PR does not make that switch yet.** It builds and proves the new surface while leaving today's publish list exactly as it is. Flipping it is a separate change. ## The problem this design has to avoid A published package can't depend on packages that won't exist on the registry. So each published package *contains a compiled copy* of the internal packages it covers. That creates a trap. If one application ends up with the same code twice — once inside a published package, once as its own package — then classes, registries, and anything compared by reference exist twice too. An `instanceof` check quietly returns false. Nothing crashes, nothing fails to compile, and both copies behave identically in isolation. You find out much later, somewhere unrelated. So the rule the whole design follows is: **every piece of internal code is published from exactly one package.** Concretely, that means: - Each published package is built in one pass, so code shared between its own entry points exists once. Verified from the build's source maps: no module appears in more than one chunk, in any published package. - When one published package needs code from another, it imports it as a real dependency rather than compiling in a second copy. - A facade re-exports from the platform packages; it never carries its own copy. `@prisma/orm-postgres/orm-client` and `@prisma/orm-family-sql/orm-client` are two names for the same object, and there's a test that asserts exactly that from installed tarballs. - One table in `packages/0-shared/publish-surface` maps every internal package to where it's published. The build, the code generator, and the lint checks all read it, so there's one answer to "where does this live" rather than three that can drift. ## Generated code follows the application Prisma writes imports into your project — contract types and migration files. Those imports have to name packages your project actually depends on, or they won't resolve. So the generator now reads the `package.json` next to the config it's generating for. A project that depends on `@prisma/orm-postgres` gets imports from that package. A project on today's names keeps today's names. Nothing to configure, because the manifest already says which it is. Contract hashes are unaffected, and that isn't an assumption — hashes are computed from a structure that import text never enters, and there's a test asserting the hash is identical across naming schemes *while* the emitted imports demonstrably differ. ## What stops the trap coming back Two checks, because the failure is silent and won't show up in a test suite: - Every example app and test project must use one naming scheme, not a mix. `lint-single-import-root` scans them and fails the build if any project imports from both, since that's the situation that loads code twice. - `lint-consumer-internal-imports` counts how many internal-package imports remain in those projects and compares against a committed number. It fails if the number goes up (someone added one) and also if it goes down without the number being updated (so improvements get locked in). Target is zero. The build itself also refuses to proceed if the published-package map would put one module in two places, or if a published package's `package.json` no longer matches what its code actually needs. ## Reading this PR It's large — 257 files — because it's a migration. The commits are grouped and meant to be read in order: 1. **Platform packages** — the build mechanism, and the seven platform packages it produces. 2. **Database packages, extension packs, the `prisma` command** — completes the set of 17. 3. **Generated imports become configurable** — one place decides which names get written, with today's names still the default. 4. **Database-family symmetry, publishing the map, the identity checks.** 5. **One package per application** — the three converted examples, the re-exports they proved necessary, and the counting check. 6. **Migration files follow the project too.** One thing worth knowing while reading: re-exporting a package republishes all of its sub-paths, not just the one that was needed. This PR adds 115 published sub-paths across the three database packages. Two candidates were dropped for exactly that reason — see below. ## Alternatives considered **Let an application install platform packages alongside its facade.** Nothing would need re-exporting and the facades would stay thinner. Rejected: an application would again juggle several Prisma dependencies whose correct combination it maintains by hand, and getting it wrong — upgrading one and not the other — produces the silent two-copies failure above. Re-exporting costs a generated line and nothing at runtime. **Re-export everything an application might plausibly want.** Rejected in review: because re-exporting brings a package's entire sub-path surface, generosity is expensive and hard to undo. Migration tooling (54 sub-paths) was dropped because its only users are extension packs, which install platform packages anyway; the SQL driver re-export was dropped because nothing imported it at all. What remains is what a converted example actually needed. **Flip the publish list in this same PR.** Rejected: it would mix "does the new surface work" with "is it safe to stop publishing 50 packages" in one review. The switch is mechanical once this lands, and gets its own change. ## Verification `build`, `typecheck` (156 tasks), `test:packages` (1077 files / 14087 tests), `test:e2e`, `lint`, `lint:deps`, `lint:docs`, `lint:manifests`, `check:publish-deps`, `check:clean-tree`, `lint:casts` and `lint:throws` (no new instances), `test:scripts`, coverage, the tarball-install suites, and regenerating every committed artifact leaves the tree unchanged. Known-unstable and unrelated to this change: the `relation-mode-gh-*` port suites (TML-3140), and several test timeouts that are too tight under load. ## Follow-ups TML-3124 switch the publish list · TML-3127 build cache can validate a stale published package on CI · TML-3140 unstable port suites · TML-3141 a test-helper sub-path reaches a package that is never published. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added consolidated public ORM packages for PostgreSQL, MongoDB, SQLite, framework tooling, database targets, and extensions. - Generated contracts, migrations, and scaffolds now adapt imports to the consuming project’s package surface. - Added facade-provided `prisma-next` CLI access and consolidated migration entrypoints. - **Documentation** - Updated installation, package naming, public entrypoint, and migration scaffolding guidance. - **Tests** - Added coverage for package installation, exports, CLI behavior, module identity, and import compatibility. - **Chores** - Added checks preventing incompatible internal and public package imports. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 1 个月前 | |
TML-3164: targets and extensions contribute aggregate operations (#29922) ## Linked issue Refs [TML-3164](https://linear.app/prisma-company/issue/TML-3164/contributed-aggregate-operations-de-hardcode-the-sql-builder-and-orm) — slice 07 of the [Codec JSON projections](https://linear.app/prisma-company/project/codec-json-projections-a10fba2e9cd5) project. Parallel with slice 06 ([TML-3163](https://linear.app/prisma-company/issue/TML-3163/opt-in-number-representation-integer-codecs-bigintnumber-unboundedint), PR #29902) — the two share no implementation surfaces. Both unblock [TML-3165](https://linear.app/prisma-company/issue/TML-3165/native-number-aggregate-defaults-countcountbigint-sumsumbigint), which adds `countBigInt`/`sumBigInt`/`avgDecimal` without touching either client package. Follow-up filed: [TML-3182](https://linear.app/prisma-company/issue/TML-3182/reserved-name-validation-for-contributed-aggregate-operations-in-the). ## At a glance A target or extension contributes an aggregate operation the framework has never heard of, and it appears on the client under its own name, typed and decoded: ```ts // contributed by a test-only extension: an exact pg/int8@1 input match, // declared output pg/int8@1, lowering to bit_or(...) const stats = await db.orm.Reading.aggregate((agg) => ({ bits: agg.bitOr('mask'), // contributed — bigint rows: agg.tally(), // contributed, no input — bigint total: agg.sum('mask'), // built-in — decimal string })); // { bits: 9007199254740995n, rows: 3n, total: '9007199254740995' } ``` `bitOr` and `sum` fold the same rows to the same digits and come back differently typed, because the declared output codec is the only thing that decides. Neither `bitOr` nor `tally` exists on a stack the extension is not composed into. ## Decision The aggregate operation set becomes a target/extension contribution end to end. No literal operation name and no per-operation logic survives in the sql-builder lane or sql-orm-client. 1. **The operation namespace is open; the SQL alphabet stays closed.** `AggregateFn` (`relational-core/src/ast/types.ts`) is SQL's alphabet, not the operation namespace. Registry assembly enforces the bridge: an operation named outside the alphabet must carry a `lower` hook, or composition fails with `RUNTIME.AGGREGATE_LOWERING_MISSING` — at composition, before any query. 2. **Out-of-alphabet operations are projection-only.** They exist only in lowered form, and a lowered form (SQLite's `CAST(… AS TEXT)`, say) is unsound in a comparison. HAVING, ORDER BY, and comparison operands refuse them with `ORM.AGGREGATE_PROJECTION_ONLY`, statically (`HavingBuilder` is keyed by `AggregateOperationNames<TContract> & AggregateFn`) and at runtime. 3. **Every consumer surface derives.** The SQL DSL's aggregate functions, the ORM's `aggregate()`, `groupBy().aggregate()`, `having()`, and the collection's include reducers read their method set from the contract's emitted `aggregateTypes` at the type level and from the composed registry at runtime. Arity follows row presence: `withoutInput` ⇒ zero-arg, `byCodec`/`anyInput` ⇒ field-taking, both ⇒ both. 4. **Reducers install as generated own-properties**, not through a Proxy, so `orm(...)` can reject a contributed name that would shadow a collection member with `ORM.AGGREGATE_OPERATION_RESERVED`. ## Reviewer notes - **Two observable changes, both intended, both recorded in the slice spec.** (a) `count(field)` now renders `COUNT(<column>)` where it previously dropped the argument and rendered `COUNT(*)` — PostgreSQL declares `count` with `input: { kind: 'any' }`, so both arities are honest data. It flips a recorded Prisma-deviation port assertion from `it.fails` to `it`. (b) For a contract whose aggregate map is unknown — an in-code `defineContract`, or a pre-`aggregateTypes` contract — the derived surfaces resolve to a branded empty type rather than five literal methods, so calls that compiled with an `as never` argument now need the builder cast. Two integration tests in this diff show that shape, and both upgrade clusters carry declarations. - **Why generated own-properties instead of a Proxy.** A proxy synthesises members on access, so there is nothing for reserved-name validation to enumerate — the reserved list would have to be hand-maintained, which is the hardcoding this slice removes. It also breaks the prototype chain that subclassing relies on (examples subclass `Collection`) and forbids the private-field access the reducers need. The cost is per-instance installation: builder chaining clones per step, so a five-link chain does ~25 `defineProperty` calls — noise against plan compilation, but worth knowing. - **`Collection` is now a type alias + construct-signature const**, with `CollectionBase` as the runtime class. The interface must declare exactly one construct signature; intersecting the class's static face instead produces `TS2510` at every subclass site. - **The lowering rule is enforced on the runtime plane only** — a descriptor with a novel name and no hook emits fine and fails at execution-context assembly. Deliberate: the three sibling registry validations already sit on that plane, and emission builds no expressions. Moving this one check would make the split less coherent. - **Empty-input results are now derived, not name-checked**: `emptyAggregateResult(nullable, codec)` replaces `fn === 'count' ? 0n : null`. Equivalence was verified by hand against both descriptor matrices — every built-in `count` is non-nullable and both bigint codecs decode `'0'` to `0n`. - The first commits carry planning artefacts shared with slice 06 (specs, project plan, design notes); `projects/**/trace.jsonl` will conflict trivially with that branch at merge — resolve by line union. ## How it fits together 1. **Open the vocabulary** — the registry accepts any operation name and enforces the lowering rule ([aggregate-descriptor-registry.ts](packages/2-sql/4-lanes/relational-core/src/aggregate-descriptor-registry.ts), with `isAggregateFn`/`aggregateFnNames` beside the union in [ast/types.ts](packages/2-sql/4-lanes/relational-core/src/ast/types.ts)). 2. **The lane cut** — the method set derives from the contract map, one generic funnel dispatches, and alphabet membership is its only branch ([expression.ts](packages/2-sql/4-lanes/sql-builder/src/expression.ts), [runtime/functions.ts](packages/2-sql/4-lanes/sql-builder/src/runtime/functions.ts), [runtime/expression-impl.ts](packages/2-sql/4-lanes/sql-builder/src/runtime/expression-impl.ts)). 3. **The ORM cut** — the same derivation across include reducers, top-level and grouped aggregates, and HAVING ([types.ts](packages/3-extensions/sql-orm-client/src/types.ts), [collection.ts](packages/3-extensions/sql-orm-client/src/collection.ts), [aggregate-operations.ts](packages/3-extensions/sql-orm-client/src/aggregate-operations.ts)). 4. **Proof and record** — a contributed operation through a real query, plus ADR 020 and the descriptor guide. ## Behavior changes & evidence - **A contributed operation reaches the database and decodes through its declared codec.** Evidence: [contributed-aggregates.test.ts](test/integration/test/sql-orm-client/contributed-aggregates.test.ts) — top-level and include-reducer paths, the empty-input answer derived from declared nullability, the rendered SQL asserted to contain `bit_or(`, and the discriminator: the same query against a stack without the extension has no such method. - **`count(field)` counts the field.** Implementation: [aggregate-builder.ts](packages/3-extensions/sql-orm-client/src/aggregate-builder.ts). Evidence: the `legacy-aggregations` port assertion flips green. - **Out-of-alphabet operations are refused in comparison positions.** Implementation: [expression-impl.ts](packages/2-sql/4-lanes/sql-builder/src/runtime/expression-impl.ts). Evidence: [contributed-aggregates.test.ts](packages/3-extensions/sql-orm-client/test/contributed-aggregates.test.ts) (HAVING refusal) and the lane's [runtime/contributed-aggregates.test.ts](packages/2-sql/4-lanes/sql-builder/test/runtime/contributed-aggregates.test.ts). - **Reserved names are rejected at composition.** Implementation: [orm.ts](packages/3-extensions/sql-orm-client/src/orm.ts). Evidence: a test walks a live collection's own property names and fails if the guarded list misses one — the set cannot silently drift. ## Testing performed - `pnpm build`, `pnpm typecheck:all` (packages + examples), `pnpm lint:deps` (1922 modules, 0 violations), `pnpm lint` on touched packages — green - `pnpm test` for sql-orm-client / sql-builder / relational-core — green (717 / 157 / 448) - Integration suite in four shards, all 318 files — green apart from two host-environment failures confirmed by signature and passing in isolation (`issues-28192-pg-historical-dates`, host timezone; `init-journey.e2e`, host pnpm) - `pnpm fixtures:check` — **zero movement**; `pnpm check:upgrade-coverage`, `pnpm check:error-reference` (260 codes), `pnpm lint:docs`, `pnpm lint:skills` — green - Slice gates: `rg "'(count|sum|avg|min|max)'"` over both client src trees and `rg "createIncludeScalar\('"` over all code — both empty. Cast ratchet `delta=-5`. ## Skill update Both upgrade clusters carry declarations for `8.0.0-rc.1-to-8.0.0-rc.2`: the extension-author cluster covers stub execution contexts needing an aggregate registry, the map-less-contract surface change, `count(field)`, and the contributed-operation rules; the app cluster covers the two changes reachable from the TS-authored no-emit path. The shipped query guide (`skills/prisma-8/references/queries-postgres.md`) gains the include-reducer documentation it never had. ## Follow-ups - [TML-3182](https://linear.app/prisma-company/issue/TML-3182/reserved-name-validation-for-contributed-aggregate-operations-in-the) — the lane's `fn` namespace has the same shadowing property as the collection surface and no reserved-name check; a contributed `bit_and → 'and'` would quietly shadow the built-in. ## Alternatives considered - **A Proxy for runtime dispatch** (the original working position) — rejected on three counts during implementation: nothing to enumerate for reserved-name validation, a broken prototype chain for subclassers, and no private-field access from a proxy receiver. - **Opening the AST `AggregateFn` union to `string`** — unnecessary. Lowering hooks and the existing function nodes cover contributed operations, and the closed union keeps renderers exhaustive. - **Supporting out-of-alphabet operations in HAVING** — a lowered form is unsound in a comparison, so honest support needs design work nothing currently requires. Projection-only with a structured refusal is the truthful shape. - **Moving the lowering check to emission** — would make the plane split less coherent, not more; the sibling registry validations all live on the runtime plane. - **Bare `unknown` as the map-less guard** — worked, but deleted the named diagnostic. A branded empty type preserves intersection identity and restores "the contract declares no aggregate operations" in the hover. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). The DCO status check will block merge if any commit is missing a `Signed-off-by:` trailer. - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated (or `n/a` if the change is doc-only / refactor with no behavioural delta). - [x] The PR title is in `TML-NNNN: <sentence-case title>` form (Linear ticket prefix + concise title naming the concrete deliverable). See `.claude/skills/create-pr/SKILL.md` for the full convention. - [x] The **Skill update** section above is filled in (or stated `n/a — internal only`). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for custom aggregate operations contributed through extensions. * Aggregate methods, result types, nullability, and valid call shapes are now derived dynamically. * Added field-aware and zero-input aggregates, including relation include reductions. * Added lowering support for non-standard aggregates and projection-only operation restrictions. * Empty aggregate results now respect declared codecs and nullability. * **Bug Fixes** * Added validation and structured errors for unsupported, reserved, or improperly lowered operations. * **Documentation** * Expanded aggregate guides, error references, architecture rules, and upgrade instructions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net> | 1 个月前 | |
feat(orm): rename take/skip to limit/offset (#30112) ## Linked issue n/a — no Linear ticket. ## At a glance ```ts const page2 = await db.orm.User .orderBy((u) => u.id.asc()) .offset(10) .limit(10) .all(); ``` The same ORM query previously used `.skip(10).take(10)`. ## Decision This PR ships a breaking rename of ORM collection pagination from `.take(n)` / `.skip(n)` to `.limit(n)` / `.offset(n)` across root SQL collections, relation refinements, grouped SQL collections, and Mongo ORM collections. The old ORM names are removed, while Mongo's lower-level query builder continues to expose `.skip(n)` for the native `$skip` pipeline stage. ## Reviewer notes - The implementation is concentrated in the SQL, grouped SQL, and Mongo collection classes; most of the broad diff migrates repository call sites and documentation. - Pagination semantics are unchanged. The renamed methods write the same `limit` and `offset` collection state, which still lowers to SQL `LIMIT` / `OFFSET` and Mongo `$limit` / `$skip`. - Grouped SQL pagination still requires a prior non-empty `orderBy`; only the method names changed. - This is intentionally breaking and includes rc.6-to-rc.7 app and extension upgrade instructions. ## How it fits together 1. [SQL collections](packages/3-extensions/sql-orm-client/src/collection.ts) expose `limit` and `offset`, with `first()` using the renamed limiter internally. 2. [Grouped SQL collections](packages/3-extensions/sql-orm-client/src/grouped-collection.ts) carry the same vocabulary through post-group pagination while preserving their ordering gate and separate pre-group/post-group windows. 3. [Mongo ORM collections](packages/2-mongo-family/5-query-builders/orm/src/collection.ts) expose the shared ORM names while retaining native `$skip` / `$limit` lowering and updated mutation-windowing diagnostics. 4. Examples, reference material, scorecards, and [upgrade instructions](skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.6-to-8.0.0-rc.7/instructions.md) move with the API so consumers get one consistent migration path. ## Behavior changes & evidence - **SQL ORM callers now paginate with `.limit(n)` and `.offset(n)` across root collections, includes, combinators, and aggregate input windows.** The implementation lives in [collection.ts](packages/3-extensions/sql-orm-client/src/collection.ts) and [query-plan-select.ts](packages/3-extensions/sql-orm-client/src/query-plan-select.ts); [pagination.test.ts](test/integration/test/sql-orm-client/pagination.test.ts) verifies database results and [aggregate-pagination.test.ts](packages/3-extensions/sql-orm-client/test/aggregate-pagination.test.ts) verifies aggregate scoping. - **Grouped SQL ORM callers use the renamed methods without weakening deterministic ordering.** [grouped-collection.ts](packages/3-extensions/sql-orm-client/src/grouped-collection.ts) preserves the gate, while [grouped-collection.test.ts](packages/3-extensions/sql-orm-client/test/grouped-collection.test.ts) and [grouped-pagination-gate.test-d.ts](packages/3-extensions/sql-orm-client/test/grouped-pagination-gate.test-d.ts) cover runtime planning and type-level availability. - **Mongo ORM callers use `.limit(n)` and `.offset(n)`, while plans still contain `$limit` and `$skip`.** [collection.ts](packages/2-mongo-family/5-query-builders/orm/src/collection.ts) implements the rename; [collection.test.ts](packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts) verifies immutable stage construction and diagnostics, and [orm.test.ts](test/integration/test/mongo/orm.test.ts) verifies the resulting subset against MongoDB. ## Compatibility / migration / risk This is a source-breaking API rename with no deprecated aliases. Consumers must translate ORM `.take(n)` to `.limit(n)` and ORM `.skip(n)` to `.offset(n)`, including calls inside relation refinements, combinator branches, and grouped SQL chains. Mongo query-builder `.skip(n)` calls must remain unchanged. Runtime pagination behavior, cursor semantics, ordering requirements, and generated query-plan shapes do not otherwise change. ## Testing performed - `pnpm --filter @internal/sql-orm-client typecheck` - `pnpm --filter @internal/sql-orm-client test` — 771 tests - `pnpm --filter @internal/mongo-orm typecheck` - `pnpm --filter @internal/mongo-orm test` — 231 tests - Integration package typecheck plus targeted SQL, SQLite, and Mongo integration coverage — 33 tests - E2E package typecheck plus targeted SQLite ORM coverage — 18 tests - Typechecks for affected examples - `pnpm lint:deps` - `pnpm lint:skills` - `pnpm check:upgrade-coverage` - `pnpm lint:rules:symlinks` - `git diff --check` ## Skill update Updated the Prisma 8 query guidance for SQL and Mongo, and added app and extension upgrade instructions for `8.0.0-rc.6` → `8.0.0-rc.7`. The upgrade guidance explicitly preserves Mongo query-builder `.skip(n)`. ## Alternatives considered - **Keep deprecated `.take()` / `.skip()` aliases:** not chosen because Prisma Next is pre-1.0 and repository policy favors updating consumers over carrying compatibility shims. - **Rename Mongo query-builder `.skip()` too:** not chosen because the lower-level builder deliberately names native Mongo pipeline stages; `$skip` remains the correct vocabulary there. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [x] No Linear ticket exists; the title uses a concrete issue-free format. - [x] The **Skill update** section is filled in. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Renamed ORM pagination methods from `take()` and `skip()` to `limit()` and `offset()` across SQL and Mongo collections. * Preserved existing pagination behavior, including relation refinement, grouped queries, aggregation, and cursor scenarios. * **Documentation** * Updated guides, examples, reference material, scorecards, and upgrade instructions with the new terminology. * Added migration guidance for upgrading to the latest release. * **Tests** * Updated coverage to validate `limit()` and `offset()` pagination across supported query scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 17 天前 | |
One config and one command language for the ORM: prisma.config.ts, driven by the unified CLI (#30058) Every surface in this repo now agrees on one config file and one command language. A freshly scaffolded project looks like this: ```ts // prisma.config.ts — the only config file the ORM reads, shared with the unified Prisma CLI import 'dotenv/config'; import { defineConfig } from '@prisma/cli-engine'; import { defineConfig as ormConfig } from '@prisma/orm-postgres/config'; export default defineConfig({ orm: ormConfig({ contract: './src/prisma/contract.prisma', db: { connection: process.env['DATABASE_URL']! }, }), }); ``` and is driven like this: ``` prisma-cli orm init # scaffold (init sits under `orm`; compute owns top-level init) prisma-cli contract emit # every other ORM command is top-level prisma-cli db init prisma-cli migration plan --name first prisma-cli migrate prisma-cli db verify ``` **The decision: the transition period is over.** Until now the loaders still accepted the retired `prisma-next.config.ts` filename and the old un-nested config shape (with deprecation warnings), and this repo still built its own `prisma-next` binary whose command tree didn't match the CLI users actually install. This PR deletes all of it. A config in the old spelling now fails loudly, and the workspace binary is a faithful stand-in for the real host — same command paths, same loader semantics. ## Why now, and what it flushed out The soft fallbacks weren't just clutter — they were hiding bugs. Because the workspace bin loaded config its own way and mounted commands at its own paths, the 22 ORM commands had never once run the way `@prisma/cli` actually runs them. Making the workspace bin mount the family exactly like the shipped host immediately surfaced three real defects, all fixed here: 1. **Mounted commands couldn't construct.** The family's retired-invocation redirects pointed at command paths that only existed in the old standalone tree, so the engine rejected the whole CLI at build time. 2. **Relative config paths crashed every path-consuming command.** The engine's loader hands commands the config exactly as authored, so under the real host `contract.output` arrived as `./src/prisma/contract.json` and `contract emit` died inside `createRequire`. This is the failure Shane hit with `bunx prisma@next orm init` — init succeeds, then the very next command falls over. The ORM command boundary (`defineOrmCommand`) now finalizes contract and migration paths idempotently, so both hosts hand handlers the same absolute paths. 3. **`init` installed a broken toolchain.** It added `@prisma/cli-engine` untagged, which resolves npm's lagging `latest` (0.0.9) instead of the version `@prisma/cli` actually runs against. It now reads the exact engine version from the installed CLI's own manifest. An end-to-end QA run (empty directory → init → emit → `db init` → typed queries → schema change → plan → migrate → verify, against the *published* `@prisma/cli@8.0.0-rc.5` with this branch's toolchain) is green top to bottom. That run also caught a fourth defect: the TypeScript starter contract triggered `PN_CONTRACT_TYPED_FALLBACK_AVAILABLE` warnings on its own first emit; it now uses the typed model-token form the warning recommends. ## What changed, piece by piece - **Loaders**: `@internal/config-loader` and the bin's loader read only `prisma.config.ts` with the `$prismaConfig` envelope. The deprecated-filename discovery, the flat-shape acceptance, the `CONFIG.DEPRECATED_*` codes, and the old Symbol-based format marker are deleted. The telemetry enricher's matching fallbacks too. - **Binary**: the workspace bin is named `prisma` and mounts the family the way the host does — commands top-level, `init` under `orm`. Examples, e2e journeys, and harnesses drive it through those paths, which is what finally puts the mounted tree under test. - **Strings**: every user-facing command string (errors, docs, READMEs, scaffolded scripts, next-step hints) reads `prisma <command>` / `prisma orm init`. - **Ratchet**: `scripts/lint-legacy-name.mjs` now *forbids* `prisma-next.config.ts` repo-wide, so the retired spelling can't creep back. Deliberate residuals stay allowed: `prisma-next.md`, `// use prisma-next` schema headers, `prisma-next-*` skill names, the per-user telemetry dir. - **Upgrade path**: `upgrades/8.0.0-rc.3-to-8.0.0-rc.4/` entries in both skill clusters walk consumers through the rename, the envelope rewrite, the dependency change, and the command grammar. Verified: 15,296 package tests, 2,075 integration tests, fixtures regenerated and stable, all repo lints green. One honest gap: the upgrade-instruction entries were authored from the applied diff, not validated by the full revert-and-replay flow. ## Alternatives considered - **Keep the deprecation fallbacks another release.** Rejected: every surface that still worked under the old spelling postponed exactly the couplings this change needed to surface, and the fallback paths themselves had no coverage in the real host. - **Mount the workspace commands under `prisma orm <command>`.** This PR briefly did that — the config *section* is named `orm`, so it looked right. The published rc.5 host proved otherwise: its tree is top-level with only `init` nested. The workspace bin now copies the host instead of guessing. - **Fix path finalization in the engine instead.** The cleaner home would be the engine handing validators the config file's path, but that's a prisma-cli-repo API change. The command-boundary fix works with today's engine, is idempotent, and stays correct if the engine later finalizes upstream. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 23 天前 | |
Rekey the ORM CLI family to the unified CLI mount paths (#30102) ## Linked issue n/a — requested by brief (prisma-cli PR [#218](https://github.com/prisma/prisma-cli/pull/218) mounts this family and currently re-wraps it; that wrapper is deleted once this ships). ## At a glance ```ts const commands: Readonly<Record<string, AnyCommand>> = { 'contract emit': contractEmitCommand, 'contract format': formatCommand, // … 'db migrate': migrateCommand, // … 'migration ref delete': refDeleteCommand, 'migration ref list': refListCommand, 'migration ref set': refSetCommand, // … 'orm init': initCommand, }; ``` Before this PR the same six commands were keyed `format`, `migrate`, `ref delete|list|set`, and `init` — the retired standalone grammar — and prisma-cli respelled them at mount time with string arithmetic (`ORM_MOUNT_RESPELLINGS` / `respellMovedOrmCommands`). ## Decision The ORM command family ships the unified CLI's grammar directly, so the prisma-cli wrapper can be deleted: 1. The six moved commands are rekeyed to their unified mount paths (`contract format`, `db migrate`, `migration ref list|set|delete`, `orm init`), and their help examples spell those paths. 2. The `migration ref` → `ref …` redirect is deleted (that spelling is a live mount again); `migration apply`'s replacement is respelled to `{bin} db migrate --to <contract>`. The four `migration status` flag redirects were already correct. 3. Every shipped runtime string that references a command — error `fix` prose, `why` text, typed next actions, retry commands — is swept for retired spellings and hardcoded bin names (`prisma-cli`, `prisma-next`, `prisma`). Command references now use the `{bin}` placeholder with the unified grammar, per the convention on `NextAction` in `packages/1-framework/0-foundation/utils/src/structured-error.ts`. This sweep spans the cli package plus `@internal/migration-tools` and `@internal/errors`, whose copy bundles into the toolchain's dist. 4. This repo's stand-in bin (`createBinCommands` in `packages/1-framework/3-tooling/cli/src/orm/cli.ts`) mounts the family at the same paths, so examples and e2e tests exercise the commands the way the unified CLI serves them. 5. The user-facing skills (`skills/prisma-8`, `skills/journey-tests`), current docs (glossary, Telemetry, Serverless guide, the cli README), and three example package scripts are respelled to the same grammar. ## How it fits together 1. **The family speaks the mount grammar** — `packages/1-framework/3-tooling/cli/src/orm/family.ts` keys the six moved commands at their unified paths and drops the now-colliding `migration ref` redirect. The key set matches prisma-cli's `mount-coverage.test.ts` expected tree exactly (unmoved commands unchanged, `lsp` stays top-level). 2. **The stand-in bin mirrors the host** — `src/orm/cli.ts` mounts the same paths, replaces the top-level `ref` help group with `migration ref`, and updates the `orm` group text that claimed the other commands mount at the top level. 3. **Help examples respell themselves** — each moved command's `help.examples` spell the mounted path (`db migrate --show`, `migration ref set staging 4cb4256`, …), so prisma-cli's `respellHelpExamples` has nothing left to do. 4. **Runtime copy uses `{bin}` + unified grammar** — e.g. `src/utils/migrate-failure.ts` now says ``Fix the issue and re-run `{bin} db migrate --to <contract>` ``, and `@internal/migration-tools`' ref errors say `{bin} migration ref set <name> <hash>`. The one deliberate exception: `orm init`'s scaffold and spawn keep the literal `prisma-cli` where they name the real project-local binary the `@prisma/cli` package installs (its bin genuinely is `prisma-cli`). ## Reviewer notes - The bulk of the diff is a mechanical string sweep; the semantic core is `family.ts`, `cli.ts`, and the six command files' help examples. - `commandName`/`invocation` strings threaded into why-copy (`'db migrate'`, `'db sign'`, …) were respelled too — they render in sentences like "db migrate reads the emitted contract…". - The "known related defect" in the brief (`orm init` scaffolding `prisma-next.config.ts`) is already fixed on main — `src/orm/init-scaffold.ts` writes `prisma.config.ts`. Nothing to do here. - Version-pinned upgrade skills (`skills/prisma-next-upgrade/upgrades/*`, `skills/prisma-8-extension-upgrade/upgrades/*`) still say `prisma-next migrate` etc. deliberately: they document already-released versions where that grammar was correct. - `test/integration/test/utils/cli-test-helpers.ts` lost its `init` → `orm init` remap — the family key now is the mount path, so the helper mounts the family verbatim. - `docs/releases/*` and `docs/design/*` keep old spellings as history. ## Behavior changes & evidence - **Help renders the mounted spelling.** `--help` for the six moved commands shows `contract format`, `db migrate`, `migration ref …`, `orm init` examples ([src/orm/migrate.ts](packages/1-framework/3-tooling/cli/src/orm/migrate.ts), [src/orm/ref/set.ts](packages/1-framework/3-tooling/cli/src/orm/ref/set.ts); evidence: [test/orm/cli.test.ts](packages/1-framework/3-tooling/cli/test/orm/cli.test.ts)). - **`migration apply` redirects to `db migrate`.** The retired verb answers with `{bin} db migrate --to <contract>` ([src/orm/family.ts](packages/1-framework/3-tooling/cli/src/orm/family.ts); evidence: the redirect assertions in [test/orm/cli.test.ts](packages/1-framework/3-tooling/cli/test/orm/cli.test.ts)). - **Error remediation runs verbatim in the unified tree.** Fix strings and next actions name `{bin} db migrate`, `{bin} migration ref set …`, `{bin} orm init` ([src/utils/cli-errors.ts](packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts), [migration/src/refs.ts](packages/1-framework/3-tooling/migration/src/refs.ts); evidence: [test/cli-errors.test.ts](packages/1-framework/3-tooling/cli/test/cli-errors.test.ts), [test/integration/test/cli.migrate-external-space.e2e.test.ts](test/integration/test/cli.migrate-external-space.e2e.test.ts), which parses the presented remediation and executes it). - **The built dist carries no retired spelling.** A sweep of `dist/*.mjs` shows every command reference as `{bin} <unified path>`; the only remaining `prisma-cli` strings name the real project-local binary the init flow spawns. ## Verification - `pnpm build` — 85/85 tasks. - `pnpm lint` — 99/99 tasks (includes the no-bare-cast ratchet and rules/skills checks). - `pnpm lint:skills` — all skills valid. - `pnpm test:packages` — 15374 passed, 0 failed. - `pnpm test:integration` — 2052 passed; the only failure is `test/ports/prisma/functional/issues-28192-pg-historical-dates` (a 28-second local-mean-time offset on year-120 timestamptz values — machine-timezone dependent, pre-existing, untouched by this change). - `pnpm test:e2e` — 115 passed. ## Follow-ups Downstream in prisma-cli (out of this PR's scope, recorded in its ledger): bump the `@prisma/orm-toolchain` pin, delete `ORM_MOUNT_RESPELLINGS` / `respellHelpExamples` / `respellMovedOrmCommands` and the redirect filter/rewrite in `cli.ts`, and flip `orm-mount.test.ts`'s respell assertions to prove upstream stays clean. ## Alternatives considered - **Keep the family keys and let prisma-cli keep respelling.** Rejected: we control both packages; the wrapper is string arithmetic over shipped metadata, fails silently when either side drifts, and forces prisma-cli to filter redirects to satisfy its collision check. - **Rekey the family but keep the standalone bin on the old grammar via a key map.** Rejected: the stand-in bin exists to mirror the real host, and a second grammar would keep the retired spellings alive in examples, recordings, and e2e tests. - **Hardcode `prisma` in runtime copy instead of `{bin}`.** Rejected: `NextAction.command` documents the `{bin}` convention repo-wide, and the family runs under whatever name the hosting shell registers. ## Skill update `skills/prisma-8` (SKILL.md + references), `skills/journey-tests`, and `skills/DEVELOPING.md` are respelled to the unified grammar in this PR. Version-pinned upgrade skills are intentionally unchanged (they document old versions). ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form — no Linear ticket was resolvable from the branch, brief, or commits; retitle once the ticket is known. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Updated CLI command structure: migrations use `db migrate`, references use `migration ref`, formatting uses `contract format`, and initialization uses `orm init`. * Command guidance now adapts to the active executable name. * **Documentation** * Updated guides, examples, quickstarts, and workflows with current command names and usage. * Standardized CLI references across migration, build, runtime, and troubleshooting documentation. * **Tests** * Updated automated coverage for renamed commands and improved remediation messages. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 20 天前 | |
TML-3199: Docs hygiene — dead links, stale API instructions, and the payload label (#30016) # Docs hygiene: dead links, stale API instructions, and the payload label Main-based cleanup (not part of the raw-SQL stack, though its items were surfaced by that campaign's reviews). ## What changed - **ADR-INDEX's dead ADR 035 link** — a pure capitalization mismatch (`Dual authoring conflict resolution` vs the file's `Dual Authoring Conflict Resolution`); corrected to the file's actual name. - **`error-reference.md`: "Meta:" → "Payload:" (260 entries) + one preamble sentence.** The old label was wrong for half the file: `structuredError()` writes `error.meta` but `runtimeError()` writes `error.details`, and a scripted classification showed a per-code label is ill-defined — of the codes classifiable at all, eight are raised through *both* constructors. The neutral label plus a preamble stating the rule (meta from structuredError, details from runtimeError, some codes both ways) is accurate today and stays accurate as raise sites move. No tooling reads the label (verified against `list-error-codes.mjs`). - **Stale `validateContract<Contract>(contractJson)` instruction removed from four surfaces** (`AGENTS.md` § Key Patterns, Testing Guide ×3, the Runtime subsystem doc, and the `typed-contract-in-tests` rulecard) — no such export exists, and the stale pattern had already generated a false review finding. Replacements verified against current code: the client factory hydrates (`postgres<Contract>({ contractJson, url })`), and tests use `validateSqlContractFully<Contract>(contractJson)` (the idiom with 174 current usages). The `validateContract` in `family-instance-domain-actions` is deliberately untouched — that one is the real ADR 204 control-plane primitive, a different thing sharing the name. - **ADR 012's refs clause** now states ADR 205's own conclusion: the unindexed-predicate lint and refs-based budget heuristic ran off the removed sidecar and no longer run for any plan. (The previous wording invited a hunt for a `meta.refs` field that no longer exists.) - **One ticketed item needed nothing**: the four "dead" source links in the Runtime & Middleware doc were already fixed upstream — verified resolving, left alone. ## Known merge note This PR and the raw-SQL stack (#29997) both edit the tail of the same ADR 012 update note, for different reasons. The conflict is one line but **semantic**: whichever lands second must carry both intents (the stack scopes the wire-level-rows claim as historical; this states the refs heuristics gone). Taking either side wholesale silently drops the other. Out of scope, ticketed: 68 further dead links across `docs/` + the missing link checker, two orphaned error-reference entries the one-directional checker cannot see, and ADR 205's own upstream ambiguity (all on TML-3211). Refs: TML-3199 https://claude.ai/code/session_01NnNjsNcPMtbJZhnZz5Zzbe <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated contract hydration and validation guidance to reflect the current workflow. * Refreshed testing and runtime examples for standalone contract usage. * Corrected architecture decision record titles, links, and descriptions of removed raw-plan metadata. * Clarified that contract data can be passed directly through runtime setup. * **Tests** * Updated typed contract fixture guidance to use full SQL contract validation for parsed contract data. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Oleksii Orlenko <robot@aqrln.net> | 10 天前 | |
feat(orm): rename take/skip to limit/offset (#30112) ## Linked issue n/a — no Linear ticket. ## At a glance ```ts const page2 = await db.orm.User .orderBy((u) => u.id.asc()) .offset(10) .limit(10) .all(); ``` The same ORM query previously used `.skip(10).take(10)`. ## Decision This PR ships a breaking rename of ORM collection pagination from `.take(n)` / `.skip(n)` to `.limit(n)` / `.offset(n)` across root SQL collections, relation refinements, grouped SQL collections, and Mongo ORM collections. The old ORM names are removed, while Mongo's lower-level query builder continues to expose `.skip(n)` for the native `$skip` pipeline stage. ## Reviewer notes - The implementation is concentrated in the SQL, grouped SQL, and Mongo collection classes; most of the broad diff migrates repository call sites and documentation. - Pagination semantics are unchanged. The renamed methods write the same `limit` and `offset` collection state, which still lowers to SQL `LIMIT` / `OFFSET` and Mongo `$limit` / `$skip`. - Grouped SQL pagination still requires a prior non-empty `orderBy`; only the method names changed. - This is intentionally breaking and includes rc.6-to-rc.7 app and extension upgrade instructions. ## How it fits together 1. [SQL collections](packages/3-extensions/sql-orm-client/src/collection.ts) expose `limit` and `offset`, with `first()` using the renamed limiter internally. 2. [Grouped SQL collections](packages/3-extensions/sql-orm-client/src/grouped-collection.ts) carry the same vocabulary through post-group pagination while preserving their ordering gate and separate pre-group/post-group windows. 3. [Mongo ORM collections](packages/2-mongo-family/5-query-builders/orm/src/collection.ts) expose the shared ORM names while retaining native `$skip` / `$limit` lowering and updated mutation-windowing diagnostics. 4. Examples, reference material, scorecards, and [upgrade instructions](skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.6-to-8.0.0-rc.7/instructions.md) move with the API so consumers get one consistent migration path. ## Behavior changes & evidence - **SQL ORM callers now paginate with `.limit(n)` and `.offset(n)` across root collections, includes, combinators, and aggregate input windows.** The implementation lives in [collection.ts](packages/3-extensions/sql-orm-client/src/collection.ts) and [query-plan-select.ts](packages/3-extensions/sql-orm-client/src/query-plan-select.ts); [pagination.test.ts](test/integration/test/sql-orm-client/pagination.test.ts) verifies database results and [aggregate-pagination.test.ts](packages/3-extensions/sql-orm-client/test/aggregate-pagination.test.ts) verifies aggregate scoping. - **Grouped SQL ORM callers use the renamed methods without weakening deterministic ordering.** [grouped-collection.ts](packages/3-extensions/sql-orm-client/src/grouped-collection.ts) preserves the gate, while [grouped-collection.test.ts](packages/3-extensions/sql-orm-client/test/grouped-collection.test.ts) and [grouped-pagination-gate.test-d.ts](packages/3-extensions/sql-orm-client/test/grouped-pagination-gate.test-d.ts) cover runtime planning and type-level availability. - **Mongo ORM callers use `.limit(n)` and `.offset(n)`, while plans still contain `$limit` and `$skip`.** [collection.ts](packages/2-mongo-family/5-query-builders/orm/src/collection.ts) implements the rename; [collection.test.ts](packages/2-mongo-family/5-query-builders/orm/test/collection.test.ts) verifies immutable stage construction and diagnostics, and [orm.test.ts](test/integration/test/mongo/orm.test.ts) verifies the resulting subset against MongoDB. ## Compatibility / migration / risk This is a source-breaking API rename with no deprecated aliases. Consumers must translate ORM `.take(n)` to `.limit(n)` and ORM `.skip(n)` to `.offset(n)`, including calls inside relation refinements, combinator branches, and grouped SQL chains. Mongo query-builder `.skip(n)` calls must remain unchanged. Runtime pagination behavior, cursor semantics, ordering requirements, and generated query-plan shapes do not otherwise change. ## Testing performed - `pnpm --filter @internal/sql-orm-client typecheck` - `pnpm --filter @internal/sql-orm-client test` — 771 tests - `pnpm --filter @internal/mongo-orm typecheck` - `pnpm --filter @internal/mongo-orm test` — 231 tests - Integration package typecheck plus targeted SQL, SQLite, and Mongo integration coverage — 33 tests - E2E package typecheck plus targeted SQLite ORM coverage — 18 tests - Typechecks for affected examples - `pnpm lint:deps` - `pnpm lint:skills` - `pnpm check:upgrade-coverage` - `pnpm lint:rules:symlinks` - `git diff --check` ## Skill update Updated the Prisma 8 query guidance for SQL and Mongo, and added app and extension upgrade instructions for `8.0.0-rc.6` → `8.0.0-rc.7`. The upgrade guidance explicitly preserves Mongo query-builder `.skip(n)`. ## Alternatives considered - **Keep deprecated `.take()` / `.skip()` aliases:** not chosen because Prisma Next is pre-1.0 and repository policy favors updating consumers over carrying compatibility shims. - **Rename Mongo query-builder `.skip()` too:** not chosen because the lower-level builder deliberately names native Mongo pipeline stages; `$skip` remains the correct vocabulary there. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [x] No Linear ticket exists; the title uses a concrete issue-free format. - [x] The **Skill update** section is filled in. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Renamed ORM pagination methods from `take()` and `skip()` to `limit()` and `offset()` across SQL and Mongo collections. * Preserved existing pagination behavior, including relation refinement, grouped queries, aggregation, and cursor scenarios. * **Documentation** * Updated guides, examples, reference material, scorecards, and upgrade instructions with the new terminology. * Added migration guidance for upgrading to the latest release. * **Tests** * Updated coverage to validate `limit()` and `offset()` pagination across supported query scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 17 天前 |
Docs index
This directory contains the primary documentation for the repository.
Start here
- Architecture Overview — high-level design and layering
- Getting Started — build, test, and run the demo
- Testing Guide — testing philosophy and commands
Deploying
- Serverless Deployment Guide — deploying to per-request runtimes (Cloudflare Workers + Hyperdrive worked example, with pointers for AWS Lambda, Vercel, Deno, Bun)
Architecture deep dives
- ADRs — decisions (append-only)
- Subsystems — deeper technical guides by subsystem
- Package Layering — package boundaries and import constraints
Reference
- Glossary — user-facing terminology (source of truth for naming)
- Error reference — every published
NAMESPACE.SUBCODEerror code; completeness enforced bypnpm check:error-reference - Commands — command docs and entry points
- Reference docs — conventions and patterns used across the codebase
- Codec authoring guide — class-based codecs (
CodecImpl,CodecDescriptorImpl) and column helpers - Integer representation types — choosing
BigInt,BigIntNumber, orUnboundedIntby target, storage, application value, and aggregate behavior - Aggregate descriptor guide — how a target or extension declares aggregate operations and their result codecs (
SqlAggregateDescriptorontypes.aggregateDescriptors) - Mongo Pipeline Builder — typed builder for MongoDB aggregation pipelines, reads, writes, and find-and-modify
migration graph --treerendering — condensed annotated-tree rendering for offline migration topology- Why Prisma Next only supports externally-managed native Postgres enums — the rewrite/atomicity costs behind managed native enums being create/add-value-only
- CLI Style Guide — CLI UX conventions
Working with AI agents
- Cursor Cloud Agents — how cloud agents run against this repo, where config lives, how to change it, how to debug a failed run
OSS posture
- OSS posture overview — index of governance, supply-chain, and contribution policies
- Governance — maintainer team, decision-making, DCO basis
- Supply chain — license validation, NOTICE audit, npm provenance, Dependabot cooldown
- Versioning — source of truth, lockstep, dist-tag convention, release procedure
- Supported Versions — minimum Node, TypeScript, PostgreSQL, MongoDB, Bun, Deno versions
- Telemetry — what the CLI collects, the user-level config file, env-var opt-outs, the
initconsent prompt, agent detection