@prisma/orm-mongo 8.0.0-rc.4-dev.17 → 8.0.0-rc.5-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/package.json +11 -10
  2. package/skills/prisma-8/SKILL.md +84 -0
  3. package/skills/prisma-8/references/build.md +142 -0
  4. package/skills/prisma-8/references/contract.md +417 -0
  5. package/skills/prisma-8/references/debug.md +141 -0
  6. package/skills/prisma-8/references/feedback.md +251 -0
  7. package/skills/prisma-8/references/migration-review.md +224 -0
  8. package/skills/prisma-8/references/migrations.md +519 -0
  9. package/skills/prisma-8/references/queries-mongo.md +236 -0
  10. package/skills/prisma-8/references/queries-postgres.md +415 -0
  11. package/skills/prisma-8/references/queries.md +168 -0
  12. package/skills/prisma-8/references/quickstart.md +326 -0
  13. package/skills/prisma-8/references/runtime.md +344 -0
  14. package/skills/prisma-8/references/supabase.md +244 -0
  15. package/skills/prisma-8/references/upgrade-app.md +101 -0
  16. package/skills/prisma-8/references/upgrade-extension.md +105 -0
  17. package/skills/prisma-8/upgrading/app/upgrades/0.10-to-0.11/instructions.md +56 -0
  18. package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/instructions.md +381 -0
  19. package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-closed-mongo-contracts.ts +202 -0
  20. package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-domain-namespaced-contracts.ts +201 -0
  21. package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-postgres-public-default.ts +198 -0
  22. package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/strip-migration-labels-hints.ts +340 -0
  23. package/skills/prisma-8/upgrading/app/upgrades/0.12-to-0.13/instructions.md +339 -0
  24. package/skills/prisma-8/upgrading/app/upgrades/0.12-to-0.13/re-emit-mti-variant-link-columns.ts +229 -0
  25. package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/instructions.md +543 -0
  26. package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/migration-op-factories-to-methods.ts +290 -0
  27. package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/uuid-preset-rename.ts +43 -0
  28. package/skills/prisma-8/upgrading/app/upgrades/0.14-to-0.15/instructions.md +359 -0
  29. package/skills/prisma-8/upgrading/app/upgrades/0.15-to-0.16/instructions.md +173 -0
  30. package/skills/prisma-8/upgrading/app/upgrades/0.16-to-0.17/instructions.md +805 -0
  31. package/skills/prisma-8/upgrading/app/upgrades/0.16-to-0.17/strip-sha256-hash-prefixes.ts +382 -0
  32. package/skills/prisma-8/upgrading/app/upgrades/0.17-to-8.0.0-rc.1/instructions.md +72 -0
  33. package/skills/prisma-8/upgrading/app/upgrades/0.7-to-0.8/instructions.md +8 -0
  34. package/skills/prisma-8/upgrading/app/upgrades/0.8-to-0.9/instructions.md +36 -0
  35. package/skills/prisma-8/upgrading/app/upgrades/0.8-to-0.9/strip-inline-contracts.ts +226 -0
  36. package/skills/prisma-8/upgrading/app/upgrades/0.9-to-0.10/instructions.md +86 -0
  37. package/skills/prisma-8/upgrading/app/upgrades/0.9-to-0.10/stamp-storage-types-kind.ts +360 -0
  38. package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.1-to-8.0.0-rc.2/instructions.md +588 -0
  39. package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.2-to-8.0.0-rc.3/instructions.md +5 -0
  40. package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.3-to-8.0.0-rc.4/instructions.md +158 -0
  41. package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.4-to-8.0.0-rc.5/instructions.md +42 -0
  42. package/skills/prisma-8/upgrading/extension/upgrades/0.10-to-0.11/instructions.md +276 -0
  43. package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/instructions.md +738 -0
  44. package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/migrate-contract-testing-imports.ts +97 -0
  45. package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/regenerate-extension-public-baseline.ts +223 -0
  46. package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/strip-migration-labels-hints.ts +340 -0
  47. package/skills/prisma-8/upgrading/extension/upgrades/0.12-to-0.13/instructions.md +266 -0
  48. package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/instructions.md +522 -0
  49. package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/migration-op-factories-to-methods.ts +290 -0
  50. package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/uuid-preset-rename.ts +43 -0
  51. package/skills/prisma-8/upgrading/extension/upgrades/0.14-to-0.15/instructions.md +803 -0
  52. package/skills/prisma-8/upgrading/extension/upgrades/0.15-to-0.16/instructions.md +219 -0
  53. package/skills/prisma-8/upgrading/extension/upgrades/0.16-to-0.17/instructions.md +731 -0
  54. package/skills/prisma-8/upgrading/extension/upgrades/0.16-to-0.17/strip-sha256-hash-prefixes.ts +382 -0
  55. package/skills/prisma-8/upgrading/extension/upgrades/0.17-to-8.0.0-rc.1/instructions.md +194 -0
  56. package/skills/prisma-8/upgrading/extension/upgrades/0.7-to-0.8/instructions.md +8 -0
  57. package/skills/prisma-8/upgrading/extension/upgrades/0.8-to-0.9/instructions.md +57 -0
  58. package/skills/prisma-8/upgrading/extension/upgrades/0.8-to-0.9/strip-inline-contracts.ts +226 -0
  59. package/skills/prisma-8/upgrading/extension/upgrades/0.9-to-0.10/instructions.md +150 -0
  60. package/skills/prisma-8/upgrading/extension/upgrades/0.9-to-0.10/stamp-storage-types-kind.ts +360 -0
  61. package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.1-to-8.0.0-rc.2/instructions.md +746 -0
  62. package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.2-to-8.0.0-rc.3/instructions.md +5 -0
  63. package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.3-to-8.0.0-rc.4/instructions.md +137 -0
  64. package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.4-to-8.0.0-rc.5/instructions.md +129 -0
@@ -0,0 +1,738 @@
1
+ ---
2
+ from: "0.11"
3
+ to: "0.12"
4
+ changes:
5
+ - id: expr-visitor-add-window-func-method
6
+ summary: |
7
+ The `ExprVisitor<R>` interface in `@internal/sql-relational-core/ast` gained a required `windowFunc(expr: WindowFuncExpr): R` method (added to support `ROW_NUMBER() OVER (…)` lowering for `.distinct(cols)`). Every `ExprVisitor<R>` implementation in your extension — typically the object literal you pass to `expr.accept({ … })` — must add the new method or TypeScript will refuse the literal. The right body depends on what the visitor does: binding/encoding/transforming visitors usually treat `WindowFuncExpr` similarly to `AggregateExpr`; visitors that reject unsupported kinds in restricted contexts (e.g. grouped `HAVING`) should reject window functions there too. No automated codemod — author the body per visitor by hand.
8
+ detection:
9
+ glob: "**/*.ts"
10
+ contains:
11
+ - "ExprVisitor"
12
+ - "aggregate"
13
+ anyMatch: false
14
+ - id: any-expression-exhaustive-switch-add-window-func-case
15
+ summary: |
16
+ The `AnyExpression` discriminated union in `@internal/sql-relational-core/ast` gained a `WindowFuncExpr` variant (`kind: 'window-func'`). Exhaustive switches over `expr.kind` that use the `satisfies never` exhaustiveness pattern — typically in SQL renderers, AST walkers, and analysis passes — will fail to compile until they add a `case 'window-func':` arm. The arm's body depends on the switch's purpose; the most common shape is "render the window function as `fn() OVER (…)`" (matching Postgres/SQLite syntax) or "reject as unsupported in this context".
17
+ detection:
18
+ glob: "**/*.ts"
19
+ contains:
20
+ - "case 'aggregate':"
21
+ - "satisfies never"
22
+ anyMatch: false
23
+ - id: distinct-cols-now-collapses-by-specified-columns
24
+ summary: |
25
+ `.distinct(cols)` on `@internal/sql-orm-client` `Collection` (and on nested `.include(…, c => c.distinct(cols)…)`) now keeps **one representative row per `(cols)` group**, matching Prisma semantics. Prior to 0.12, `.distinct(cols)` did not actually collapse rows on the specified columns — when the projection contained any other distinguishing column (typically `id`), rows that differed only in those other columns were all returned. No code change is required for consumer call sites, but any extension tests or fixtures that asserted the pre-0.12 no-collapse output will fail and need updating to reflect the new collapsed shape. The representative within each partition is picked by the user's `.orderBy(…)` (if any); when the orderBy doesn't fully order rows in a partition the pick is implementation-defined, matching Prisma's documented behaviour.
26
+ detection:
27
+ glob: "**/*.{ts,tsx}"
28
+ contains:
29
+ - ".distinct("
30
+ anyMatch: true
31
+ - id: replace-runtime-verify-options-with-verify-marker
32
+ summary: |
33
+ The `@internal/sql-runtime` export `RuntimeVerifyOptions` is removed; replaced by `VerifyMarkerOption = 'onFirstUse' | false`. Extension convenience wrappers that expose a `*OptionsBase` interface must rename `verify?: RuntimeVerifyOptions` to `verifyMarker?: VerifyMarkerOption`, drop the hard-coded `verify: { mode: 'onFirstUse', requireMarker: false }` default in the wrapper's `createRuntime(...)` call, and thread the caller's value through via `...ifDefined('verifyMarker', options.verifyMarker)` so the runtime's own default (`'onFirstUse'`) applies when the option is omitted. The runtime no longer throws `CONTRACT.MARKER_MISMATCH` / `CONTRACT.MARKER_MISSING` on drift — it emits a structured `warn`-level log line once per runtime instance (single-flighted under concurrent first queries) and proceeds.
34
+ detection:
35
+ glob: "**/*.ts"
36
+ contains:
37
+ - "RuntimeVerifyOptions"
38
+ - "verify: {"
39
+ anyMatch: true
40
+ - id: define-contract-drop-capabilities-generic
41
+ summary: |
42
+ The `Capabilities` type parameter is removed from the framework `baseDefineContract` factory and from `ContractInput` in `@internal/contract`. Extension authors who ship their own target-facade-style `defineContract` (a thin wrapper that re-exports `baseDefineContract` with `family` / `target` pre-bound) must drop the `Capabilities` generic from every facade type alias (`*Result`, `*BaseScaffold`, `*Definition`, `*Scaffold`) and from every overload signature; the corresponding `ContractInput<…, Capabilities>` and `baseDefineContract<…, Capabilities>` instantiations lose their trailing argument. Extensions that don't ship a facade have no source change — their emitted `contract.json` / `contract.d.ts` will pick up two new auto-contributed capabilities (`postgres.distinctOn`, `sql.lateral`) on re-emit; re-run `pnpm fixtures:emit` (or the equivalent for your extension) to refresh fixtures.
43
+ detection:
44
+ glob: "**/*.ts"
45
+ contains:
46
+ - "baseDefineContract"
47
+ - "Capabilities"
48
+ anyMatch: false
49
+ - id: strip-migration-labels-hints
50
+ summary: |
51
+ The 0.12 migration manifest schema is closed (`'+': 'reject'`) and the metadata model no longer carries `labels` or `hints`; any on-disk `migration.json` your extension ships (e.g. an install-extension migration under `migrations/`) still holding either key fails to load with `INVALID_MANIFEST` naming the offending key. Both fields are also dropped from the content-addressed migration identity, so `migrationHash` is now computed over `{ from, to, providedInvariants, createdAt }` plus the sibling `ops.json`. Run the colocated codemod to strip both keys from every `migration.json` and recompute its `migrationHash` over the slimmed envelope.
52
+ detection:
53
+ glob: "**/migration.json"
54
+ contains:
55
+ - '"labels"'
56
+ - '"hints"'
57
+ anyMatch: true
58
+ script: ./strip-migration-labels-hints.ts
59
+ - id: extension-public-default-baseline
60
+ summary: |
61
+ Published Postgres extension packs' empty default namespace flips `__unbound__` → `public` (`postgres-unbound-schema` → `postgres-schema`), changing the extension's `storageHash`, `migrations/refs/head.json` hash, and each baseline `migration.json` `to` / `migrationHash`. Migration ops are unchanged. Re-emit the contract-space (`pnpm build:contract-space` / `prisma-next contract emit`) and regenerate the install migration baseline so the head ref matches the new contract hash.
62
+ detection:
63
+ glob: "**/contract.json"
64
+ contains:
65
+ - '"kind": "postgres-unbound-schema"'
66
+ anyMatch: true
67
+ script: ./regenerate-extension-public-baseline.ts
68
+ - id: domain-plane-spi-and-testing-subpath
69
+ summary: |
70
+ Contract SPI is namespaced: read models/value objects through `contract.domain.namespaces.<ns>` (helpers: `domainModelsAtDefaultNamespace(contract.domain)`, `ContractModelDefinitions`) instead of flat `contract.models`. The `@internal/contract/testing` subpath export was removed — test factories (`createContract`, `createSqlContract`, `DUMMY_HASH`, `applicationDomainOf`) now live in `@repo/test-utils`. Run the colocated import codemod and update SPI consumption to the namespaced contract shape.
71
+ detection:
72
+ glob: "**/*.{ts,tsx}"
73
+ contains:
74
+ - "@internal/contract/testing"
75
+ anyMatch: true
76
+ script: ./migrate-contract-testing-imports.ts
77
+ - id: default-namespace-domain-access-retire-projection-helpers
78
+ summary: |
79
+ The transitional `@internal/contract` helpers `contractModels`, `contractValueObjects`, `resolveSingleDomainNamespaceId`, `ContractModelsMap`, and `ContractValueObjectsMap` are removed. Read models/value objects through `domainModelsAtDefaultNamespace(contract.domain)` / `domainValueObjectsAtDefaultNamespace(contract.domain)` (these read the contract's sole namespace and throw on a multi-namespace contract). Typed model shapes use `ContractModelDefinitions<Contract>`. SQL namespace concretions must expose `qualifyTable`; hydrate migration scaffolds with `PostgresContractSerializer` (not `structuredClone`) so `qualifyTable` survives. Runtime SQL is namespace-qualified on Postgres.
80
+ detection:
81
+ glob: "**/*.{ts,tsx}"
82
+ contains:
83
+ - "contractModels"
84
+ anyMatch: true
85
+ ---
86
+
87
+ # 0.11 → 0.12 — Extension-author upgrade instructions
88
+
89
+ ## `expr-visitor-add-window-func-method`
90
+
91
+ Starting at the 0.12 release, the framework `ExprVisitor<R>` interface in `@internal/sql-relational-core/ast` gained a required method:
92
+
93
+ ```ts
94
+ windowFunc(expr: WindowFuncExpr): R;
95
+ ```
96
+
97
+ This method was added to support `WindowFuncExpr` — the new AST node for window functions, currently lowering `ROW_NUMBER() OVER (PARTITION BY … ORDER BY …)` used by `.distinct(cols)` (and reserved for `RANK` / `DENSE_RANK` as future additions).
98
+
99
+ Every `ExprVisitor<R>` implementation needs to add the new method. The natural body depends on what the visitor does:
100
+
101
+ - **Binding / encoding / transforming visitors** — usually treat `WindowFuncExpr` the same way they treat `AggregateExpr` (recurse into `args`, `partitionBy`, and `orderBy`).
102
+ - **Validating visitors** that restrict which expression kinds are allowed in a given context (e.g. grouped `HAVING` clauses) — typically reject window functions just like they reject aggregates in unrelated contexts.
103
+
104
+ ### Before 0.12
105
+
106
+ ```ts
107
+ expr.accept<AnyExpression>({
108
+ columnRef: (e) => bindExpression(contract, e),
109
+ identifierRef: (e) => e,
110
+ subquery: (e) => bindExpression(contract, e),
111
+ operation: (e) => bindExpression(contract, e),
112
+ aggregate: (e) => bindExpression(contract, e),
113
+ // … other methods …
114
+ });
115
+ ```
116
+
117
+ ### Starting at 0.12
118
+
119
+ ```ts
120
+ expr.accept<AnyExpression>({
121
+ columnRef: (e) => bindExpression(contract, e),
122
+ identifierRef: (e) => e,
123
+ subquery: (e) => bindExpression(contract, e),
124
+ operation: (e) => bindExpression(contract, e),
125
+ aggregate: (e) => bindExpression(contract, e),
126
+ windowFunc: (e) => bindExpression(contract, e), // ← new: required
127
+ // … other methods …
128
+ });
129
+ ```
130
+
131
+ ### Or, for a context that rejects unsupported kinds
132
+
133
+ ```ts
134
+ expr.accept<AnyExpression>({
135
+ // …
136
+ aggregate: rejectInThisContext,
137
+ windowFunc: rejectInThisContext, // ← new: required
138
+ // …
139
+ });
140
+ ```
141
+
142
+ TypeScript will report missing-property errors on every visitor literal after the bump; that's a reliable compile-time signal for every affected site. No automated codemod — the right body depends on what your visitor does, so author each one by hand.
143
+
144
+ ## `any-expression-exhaustive-switch-add-window-func-case`
145
+
146
+ Starting at the 0.12 release, the `AnyExpression` discriminated union in `@internal/sql-relational-core/ast` gained `WindowFuncExpr` (`kind: 'window-func'`). Exhaustive switches over `expr.kind` that use the `satisfies never` exhaustiveness pattern will fail to compile until they add a matching arm.
147
+
148
+ The most common case is in SQL renderers — Postgres and SQLite both render `WindowFuncExpr` as `fn() OVER (PARTITION BY … ORDER BY …)` (the syntax is identical across the two targets we ship).
149
+
150
+ ### Before 0.12
151
+
152
+ ```ts
153
+ function renderExpr(expr: AnyExpression): string {
154
+ switch (expr.kind) {
155
+ case 'column-ref':
156
+ return renderColumn(expr);
157
+ case 'aggregate':
158
+ return renderAggregate(expr);
159
+ // … other cases …
160
+ // v8 ignore next 4
161
+ default:
162
+ throw new Error(
163
+ `Unsupported expression node kind: ${(expr satisfies never as { kind: string }).kind}`,
164
+ );
165
+ }
166
+ }
167
+ ```
168
+
169
+ ### Starting at 0.12
170
+
171
+ ```ts
172
+ function renderExpr(expr: AnyExpression): string {
173
+ switch (expr.kind) {
174
+ case 'column-ref':
175
+ return renderColumn(expr);
176
+ case 'aggregate':
177
+ return renderAggregate(expr);
178
+ case 'window-func':
179
+ return renderWindowFunc(expr); // ← new: required
180
+ // … other cases …
181
+ default:
182
+ throw new Error(
183
+ `Unsupported expression node kind: ${(expr satisfies never as { kind: string }).kind}`,
184
+ );
185
+ }
186
+ }
187
+
188
+ function renderWindowFunc(expr: WindowFuncExpr): string {
189
+ const fn = expr.fn.toUpperCase();
190
+ const args = expr.args.map(renderExpr).join(', ');
191
+ const partition =
192
+ expr.partitionBy && expr.partitionBy.length > 0
193
+ ? `PARTITION BY ${expr.partitionBy.map(renderExpr).join(', ')}`
194
+ : '';
195
+ const order =
196
+ expr.orderBy && expr.orderBy.length > 0
197
+ ? `ORDER BY ${expr.orderBy.map((o) => `${renderExpr(o.expr)} ${o.dir.toUpperCase()}`).join(', ')}`
198
+ : '';
199
+ const over = [partition, order].filter((s) => s.length > 0).join(' ');
200
+ return `${fn}(${args}) OVER (${over})`;
201
+ }
202
+ ```
203
+
204
+ If your switch builds an `isAtomicExpressionKind` predicate or anything similar (used to decide whether the rendered expression needs surrounding parentheses), treat `'window-func'` as atomic — `fn() OVER (…)` is self-delimited by its own parentheses.
205
+
206
+ No automated codemod — the body of the new arm depends on what the switch does. TypeScript pinpoints every site at compile time.
207
+
208
+ ## `distinct-cols-now-collapses-by-specified-columns`
209
+
210
+ Starting at the 0.12 release, `.distinct(cols)` on the `@internal/sql-orm-client` `Collection` API — at the top level (`db.Post.distinct('title')`), on leaf includes (`include('posts', p => p.distinct('title'))`), and on non-leaf includes (`include('posts', p => p.distinct('title').include('comments'))`) — keeps one representative row per `(cols)` group, matching Prisma's documented semantics.
211
+
212
+ Prior to 0.12, `.distinct(cols)` did not actually collapse rows on the specified columns: when the projection contained any other distinguishing column (typically `id`), rows that differed only in those other columns were all returned. From 0.12 onwards, `.distinct(cols)` keeps one representative row per `(cols)` group, matching the way Prisma documents `distinct`.
213
+
214
+ ### No code change for consumer call sites
215
+
216
+ ```ts
217
+ // Both 0.11 and 0.12 — same call site, different runtime behaviour:
218
+ const posts = await db.Post
219
+ .orderBy([(p) => p.title.asc(), (p) => p.id.asc()])
220
+ .distinct('title')
221
+ .all();
222
+
223
+ // 0.11: returns every post (if seed has 3 posts including two sharing title='A',
224
+ // you get 3 back).
225
+ // 0.12: returns one post per title (you get 2 back — title='A' picks the
226
+ // lower-id row per the orderBy; title='B' is unaffected).
227
+ ```
228
+
229
+ The API surface is unchanged. Type-level signatures are unchanged. Only the SQL produced and the rows returned differ.
230
+
231
+ ### Tests and fixtures that assert pre-0.12 output
232
+
233
+ Any extension test that exercises `.distinct(cols)` and asserts the result set will fail under 0.12. Updates needed:
234
+
235
+ - **Seed data with duplicates** on every column passed to `.distinct(...)` so the test actually exercises dedup (a test with no duplicates is a no-op assertion in either era).
236
+ - **Pair `.distinct(...)` with an `.orderBy(...)`** that fully orders rows within each partition (e.g. `[distinctCol.asc(), id.asc()]`) so the picked representative is deterministic. When the orderBy doesn't fully order a partition the choice is implementation-defined — matches Prisma's behaviour, but makes assertions flaky.
237
+ - **Update `expect(rows).toEqual([…])` shapes** to match the post-collapse output. The dropped row's grandchildren (where `.distinct(cols).include(grandchild)` is in play) do not appear in the output either.
238
+
239
+ ### Representative-selection behaviour
240
+
241
+ The user's `.orderBy(…)` drives the OVER ORDER BY of the underlying `ROW_NUMBER()` — the row with rank 1 in each partition wins. When the orderBy doesn't fully order rows within a partition, the choice between tied rows is implementation-defined (Postgres and SQLite are each entitled to pick any row in the tie). This matches Prisma's documented behaviour; if your extension needs deterministic picks across partition ties, add a primary-key tiebreaker to the orderBy.
242
+
243
+ ### Validation
244
+
245
+ After updating fixture / test data, run your extension's standard `pnpm test` (or `pnpm test:integration` for tests that exercise live SQL). No type-level changes — TypeScript will not pinpoint sites; runtime assertions are the signal.
246
+
247
+ ## `replace-runtime-verify-options-with-verify-marker`
248
+
249
+ Starting at the 0.12 release, `@internal/sql-runtime` simplifies marker verification. The previous `RuntimeVerifyOptions` type and the `verify: { mode; requireMarker }` field on `RuntimeOptions` are removed; replaced by a single optional field `verifyMarker?: VerifyMarkerOption` where `VerifyMarkerOption = 'onFirstUse' | false` and `'onFirstUse'` is the runtime default.
250
+
251
+ If your extension ships a convenience wrapper around `createRuntime(...)` — the pattern used by `@internal/sqlite`, `@internal/postgres`, and `@internal/postgres/serverless` — you need four mechanical edits in the wrapper source:
252
+
253
+ 1. Rename the type import from `RuntimeVerifyOptions` to `VerifyMarkerOption`.
254
+ 2. Rename the option on your `*OptionsBase` interface from `verify?` to `verifyMarker?`.
255
+ 3. Drop the hard-coded default literal in the `createRuntime(...)` call.
256
+ 4. Thread the caller's value through with `ifDefined` so omitted options defer to the runtime default.
257
+
258
+ The runtime's read-side behaviour also changes: it no longer throws `CONTRACT.MARKER_MISMATCH` or `CONTRACT.MARKER_MISSING` when the database marker is absent or drifted. Instead, on the first `execute()` call per runtime instance, it emits one structured `warn`-level log line (payload includes `code`, `scope`, `expected`, `actual`, `message`) and proceeds with the query. Extension authors do not need to implement this behaviour — it lives inside `@internal/sql-runtime` — but tests that previously asserted thrown errors need retargeting (see *Tests and fixtures* below).
259
+
260
+ ### Before 0.12 — type import and options interface
261
+
262
+ ```ts
263
+ import type {
264
+ ExecutionContext,
265
+ Runtime,
266
+ RuntimeVerifyOptions,
267
+ SqlExecutionStackWithDriver,
268
+ SqlMiddleware,
269
+ SqlRuntimeExtensionDescriptor,
270
+ } from '@internal/sql-runtime';
271
+
272
+ export interface MyTargetOptionsBase {
273
+ readonly extensions?: readonly SqlRuntimeExtensionDescriptor<MyTargetId>[];
274
+ readonly middleware?: readonly SqlMiddleware[];
275
+ readonly verify?: RuntimeVerifyOptions;
276
+ }
277
+ ```
278
+
279
+ ### Starting at 0.12 — type import and options interface
280
+
281
+ ```ts
282
+ import type {
283
+ ExecutionContext,
284
+ Runtime,
285
+ SqlExecutionStackWithDriver,
286
+ SqlMiddleware,
287
+ SqlRuntimeExtensionDescriptor,
288
+ VerifyMarkerOption,
289
+ } from '@internal/sql-runtime';
290
+ import { ifDefined } from '@internal/utils/defined';
291
+
292
+ export interface MyTargetOptionsBase {
293
+ readonly extensions?: readonly SqlRuntimeExtensionDescriptor<MyTargetId>[];
294
+ readonly middleware?: readonly SqlMiddleware[];
295
+ readonly verifyMarker?: VerifyMarkerOption;
296
+ }
297
+ ```
298
+
299
+ Import `ifDefined` from `@internal/utils/defined` if your wrapper does not already use it for other optional fields.
300
+
301
+ ### Before 0.12 — `createRuntime(...)` call inside the wrapper
302
+
303
+ ```ts
304
+ const runtime = createRuntime({
305
+ stackInstance,
306
+ context,
307
+ driver,
308
+ verify: options.verify ?? { mode: 'onFirstUse', requireMarker: false },
309
+ ...ifDefined('middleware', options.middleware),
310
+ });
311
+ ```
312
+
313
+ The hard-coded `{ mode: 'onFirstUse', requireMarker: false }` default duplicated what the runtime already applied when `verify` was omitted. From 0.12 the wrapper should not inject a default — let the runtime's `'onFirstUse'` default stand.
314
+
315
+ ### Starting at 0.12 — `createRuntime(...)` call inside the wrapper
316
+
317
+ ```ts
318
+ const runtime = createRuntime({
319
+ stackInstance,
320
+ context,
321
+ driver,
322
+ ...ifDefined('verifyMarker', options.verifyMarker),
323
+ ...ifDefined('middleware', options.middleware),
324
+ });
325
+ ```
326
+
327
+ When the caller omits `verifyMarker`, the spread adds nothing and the runtime default (`'onFirstUse'`) applies. When the caller passes `verifyMarker: false`, verification is skipped entirely.
328
+
329
+ ### Semantics mapping for callers of your wrapper
330
+
331
+ | Before 0.12 (`verify`) | Starting at 0.12 (`verifyMarker`) |
332
+ | --- | --- |
333
+ | `{ mode: 'onFirstUse', requireMarker: false }` (or omitted — your wrapper defaulted to this) | omit `verifyMarker` (runtime default `'onFirstUse'`) |
334
+ | `{ mode: 'onFirstUse', requireMarker: true }` | `verifyMarker: 'onFirstUse'` — but the throw-on-missing-marker semantics are removed; use the `db-verify` CLI for fail-fast deploy checks |
335
+ | `{ mode: 'always', requireMarker: ... }` | `verifyMarker: 'onFirstUse'` — `'always'` mode is dropped; verification is once-per-runtime |
336
+ | `{ mode: 'startup', requireMarker: ... }` | `verifyMarker: 'onFirstUse'` — `'startup'` mode is dropped for the same reason |
337
+ | Explicit skip | `verifyMarker: false` |
338
+
339
+ ### Tests and fixtures
340
+
341
+ Extension wrapper tests that exercised the old surface need two kinds of updates:
342
+
343
+ **Option-forwarding tests** — rename the option and adjust assertions about defaults:
344
+
345
+ ```ts
346
+ // Before 0.12
347
+ it('forwards verify option to createRuntime', async () => {
348
+ const verify = { mode: 'always', requireMarker: true } as const;
349
+ const db = myTarget({ contract, verify });
350
+ await db.connect(/* … */);
351
+ expect(mocks.createRuntime).toHaveBeenCalledWith(expect.objectContaining({ verify }));
352
+ });
353
+
354
+ it('defaults verify to onFirstUse without requireMarker', async () => {
355
+ const db = myTarget({ contract });
356
+ await db.connect(/* … */);
357
+ expect(mocks.createRuntime).toHaveBeenCalledWith(
358
+ expect.objectContaining({ verify: { mode: 'onFirstUse', requireMarker: false } }),
359
+ );
360
+ });
361
+
362
+ // Starting at 0.12
363
+ it('forwards verifyMarker option to createRuntime', async () => {
364
+ const db = myTarget({ contract, verifyMarker: false });
365
+ await db.connect(/* … */);
366
+ expect(mocks.createRuntime).toHaveBeenCalledWith(
367
+ expect.objectContaining({ verifyMarker: false }),
368
+ );
369
+ });
370
+
371
+ it('omits verifyMarker from createRuntime when not provided (runtime default applies)', async () => {
372
+ const db = myTarget({ contract });
373
+ await db.connect(/* … */);
374
+ expect(mocks.createRuntime).toHaveBeenCalledTimes(1);
375
+ const callArg = mocks.createRuntime.mock.calls[0]?.[0] as Record<string, unknown>;
376
+ expect(callArg).not.toHaveProperty('verifyMarker');
377
+ });
378
+ ```
379
+
380
+ **Drift / missing-marker integration tests** — grep for `rejects.toMatchObject({ code: 'CONTRACT.MARKER_MISSING' })` or `rejects.toMatchObject({ code: 'CONTRACT.MARKER_MISMATCH' })`. These patterns no longer apply: the runtime logs instead of throwing. Retarget to assert on the `Log.warn` sink:
381
+
382
+ ```ts
383
+ // Before 0.12
384
+ await expect(runtime.execute(plan).toArray()).rejects.toMatchObject({
385
+ code: 'CONTRACT.MARKER_MISSING',
386
+ });
387
+
388
+ // Starting at 0.12
389
+ const log = { info: vi.fn(), warn: vi.fn(), error: vi.fn() } satisfies Log;
390
+ const runtime = createRuntime({ stackInstance, context, driver, log });
391
+
392
+ const rows = await runtime.execute(plan).toArray();
393
+ expect(rows).toEqual(/* expected rows — query proceeds */);
394
+ expect(log.warn).toHaveBeenCalledOnce();
395
+ expect(log.warn).toHaveBeenCalledWith({
396
+ code: 'CONTRACT.MARKER_MISSING',
397
+ scope: 'marker-verification',
398
+ expected: { storageHash: contract.storage.storageHash, profileHash: contract.profileHash ?? null },
399
+ actual: null,
400
+ message: 'Contract marker not found in database',
401
+ });
402
+ ```
403
+
404
+ Pass a `log` object into `createRuntime(...)` (or through your wrapper if you expose a `log` option) so tests can spy on `warn` without touching stdout.
405
+
406
+ ### Validation
407
+
408
+ After applying the edits above, run `pnpm typecheck` on your extension package. TypeScript flags every remaining `RuntimeVerifyOptions` import and every `verify?:` field on your options interface. Then run your extension's test suite — option-forwarding unit tests and any marker-drift integration tests are the sites most likely to need the retargeting described above.
409
+
410
+ ## `define-contract-drop-capabilities-generic`
411
+
412
+ Starting at the 0.12 release, the framework `baseDefineContract` factory in `@internal/contract` drops its `Capabilities` type parameter, and the `ContractInput<Family, Target, Types, Models, ExtensionPacks, Capabilities>` shape loses its trailing argument. Capabilities are no longer declared at authoring time — they are contributed automatically by target components and extension packs, and flow into the emitted `contract.json` / `contract.d.ts` from there.
413
+
414
+ There are two kinds of impact on an extension, depending on what your extension ships:
415
+
416
+ - **Extensions that ship their own target-facade `defineContract`** (the pattern used by `@internal/postgres`, `@internal/sqlite`, and any third-party adapter that pre-binds `family` + `target` for its consumers): you need to drop the `Capabilities` generic from every facade type alias and overload signature. TypeScript will pinpoint every site once you bump.
417
+ - **Extensions that only contribute pack metadata + emit fixtures** (the more common shape — `@internal/pgvector`, `@internal/paradedb`, etc.): no source change. Re-emit your contract fixtures (`pnpm fixtures:emit` or the equivalent script for your package) so the regenerated `contract.json` / `contract.d.ts` picks up the new auto-contributed capability keys — in the 0.12 line, `postgres.distinctOn: true` and `sql.lateral: true` appear in every SQL-target fixture that loads the relevant adapter.
418
+
419
+ ### Facade-style extensions — drop the generic
420
+
421
+ If your extension ships a `defineContract` that wraps `baseDefineContract` with `family` / `target` pre-bound, walk every type alias and every overload signature in your facade and remove the `Capabilities` parameter.
422
+
423
+ #### Before 0.12
424
+
425
+ ```ts
426
+ import { defineContract as baseDefineContract } from '@internal/contract';
427
+ import type { ContractInput, ExtensionPackRef } from '@internal/contract';
428
+
429
+ type MyTargetResult<
430
+ Types extends TypesConstraint,
431
+ Models extends ModelsConstraint,
432
+ ExtensionPacks extends Record<string, ExtensionPackRef<'sql', string>> | undefined,
433
+ Capabilities extends Record<string, Record<string, boolean>> | undefined,
434
+ > = Omit<
435
+ ReturnType<
436
+ typeof baseDefineContract<
437
+ MyFamily,
438
+ MyTargetPack,
439
+ Types,
440
+ Models,
441
+ ExtensionPacks,
442
+ Capabilities
443
+ >
444
+ >,
445
+ 'target' | 'targetFamily'
446
+ > & {
447
+ readonly target: MyTargetPack['targetId'];
448
+ readonly targetFamily: MyFamily['familyId'];
449
+ };
450
+
451
+ type MyTargetBaseScaffold<
452
+ ExtensionPacks extends Record<string, ExtensionPackRef<'sql', string>> | undefined,
453
+ Capabilities extends Record<string, Record<string, boolean>> | undefined,
454
+ > = Omit<
455
+ ContractInput<
456
+ MyFamily,
457
+ MyTargetPack,
458
+ Record<never, never>,
459
+ Record<never, never>,
460
+ ExtensionPacks,
461
+ Capabilities
462
+ >,
463
+ 'family' | 'target' | 'types' | 'models'
464
+ >;
465
+
466
+ export function defineContract<
467
+ const Types extends TypesConstraint = Record<never, never>,
468
+ const Models extends ModelsConstraint = Record<never, never>,
469
+ const ExtensionPacks extends
470
+ | Record<string, ExtensionPackRef<'sql', string>>
471
+ | undefined = undefined,
472
+ const Capabilities extends Record<string, Record<string, boolean>> | undefined = undefined,
473
+ >(
474
+ definition: MyTargetDefinition<Types, Models, ExtensionPacks, Capabilities>,
475
+ ): MyTargetResult<Types, Models, ExtensionPacks, Capabilities>;
476
+ ```
477
+
478
+ #### Starting at 0.12
479
+
480
+ ```ts
481
+ import { defineContract as baseDefineContract } from '@internal/contract';
482
+ import type { ContractInput, ExtensionPackRef } from '@internal/contract';
483
+
484
+ type MyTargetResult<
485
+ Types extends TypesConstraint,
486
+ Models extends ModelsConstraint,
487
+ ExtensionPacks extends Record<string, ExtensionPackRef<'sql', string>> | undefined,
488
+ > = Omit<
489
+ ReturnType<
490
+ typeof baseDefineContract<MyFamily, MyTargetPack, Types, Models, ExtensionPacks>
491
+ >,
492
+ 'target' | 'targetFamily'
493
+ > & {
494
+ readonly target: MyTargetPack['targetId'];
495
+ readonly targetFamily: MyFamily['familyId'];
496
+ };
497
+
498
+ type MyTargetBaseScaffold<
499
+ ExtensionPacks extends Record<string, ExtensionPackRef<'sql', string>> | undefined,
500
+ > = Omit<
501
+ ContractInput<
502
+ MyFamily,
503
+ MyTargetPack,
504
+ Record<never, never>,
505
+ Record<never, never>,
506
+ ExtensionPacks
507
+ >,
508
+ 'family' | 'target' | 'types' | 'models'
509
+ >;
510
+
511
+ export function defineContract<
512
+ const Types extends TypesConstraint = Record<never, never>,
513
+ const Models extends ModelsConstraint = Record<never, never>,
514
+ const ExtensionPacks extends
515
+ | Record<string, ExtensionPackRef<'sql', string>>
516
+ | undefined = undefined,
517
+ >(
518
+ definition: MyTargetDefinition<Types, Models, ExtensionPacks>,
519
+ ): MyTargetResult<Types, Models, ExtensionPacks>;
520
+ ```
521
+
522
+ Drop the same parameter from every other overload signature (the factory-form overload, any convenience overload). Drop the matching entry from the type alias for `*Definition` and `*Scaffold` shapes. Drop the `Capabilities` argument from every internal `baseDefineContract<…, Capabilities>` instantiation and every internal `ContractInput<…, Capabilities>` instantiation. TypeScript will flag any remaining occurrence after the bump.
523
+
524
+ ### Type tests that asserted authoring-time capability literals
525
+
526
+ If your facade ships a `define-contract.test-d.ts` (or similar) that asserts a `capabilities` literal is acceptable as an input to your `defineContract` — flip the assertion. The literal is now refused at the type level:
527
+
528
+ ```ts
529
+ // Starting at 0.12
530
+ // @ts-expect-error — capabilities are contributed by components, not authoring input
531
+ defineContract({ capabilities: { sql: { lateral: true } } });
532
+ ```
533
+
534
+ ### Extensions that only emit fixtures — re-emit
535
+
536
+ If your extension does not ship a facade, you have no source change. The contract fixtures your extension emits as part of its test suite will, however, gain new capability keys after the bump. Re-run your fixture-emit script (commonly `pnpm fixtures:emit` or `pnpm test:fixtures:emit`) and commit the regenerated `contract.json` / `contract.d.ts`. Expect to see:
537
+
538
+ - `postgres.distinctOn: true` (added when a Postgres adapter is in the component graph)
539
+ - `sql.lateral: true` (added when the SQL family + a supporting adapter is in the component graph)
540
+
541
+ No fixture-shape changes other than capability additions; if your re-emit produces diffs in other sections of `contract.json`, that's a separate framework change, not this entry.
542
+
543
+ ### Validation
544
+
545
+ After applying the edits, run `pnpm typecheck` and the matching test suite for your extension package. For facade-style extensions, the typecheck pinpoints every remaining occurrence of the `Capabilities` generic at compile time. For fixture-only extensions, the regenerated `contract.json` / `contract.d.ts` diff is the signal — review it to confirm the new capability keys landed where you expect.
546
+
547
+ ## `strip-migration-labels-hints`
548
+
549
+ Starting at the 0.12 release, the migration manifest schema is closed (`'+': 'reject'`) and the metadata model no longer carries `labels` or `hints`. If your extension ships on-disk migration packages — for example an install-extension migration (`migrations/<timestamp>_install_…/migration.json`) that provisions your extension's database objects — any manifest that still holds either key fails to load: the loader rejects it with `INVALID_MANIFEST`, naming the first offending key (`labels` or `hints`). The two fields are also removed from the content-addressed migration identity — `migrationHash` is now computed over `{ from, to, providedInvariants, createdAt }` plus the sibling `ops.json` — so every migrated manifest additionally needs its hash recomputed over the slimmed envelope, or it fails hash verification on the next load.
550
+
551
+ Run the colocated codemod from your extension's package root:
552
+
553
+ ```bash
554
+ pnpm exec tsx ./strip-migration-labels-hints.ts
555
+ ```
556
+
557
+ It walks every `migration.json` that has a sibling `ops.json` (a complete on-disk migration package), removes the `labels` and `hints` keys, and recomputes `migrationHash` over the slimmed metadata plus the operations. The edit is format-preserving — only the two key lines are removed and the hash value is swapped in place, so the rest of each manifest (key order, indentation, inline-vs-expanded arrays) is left untouched and the diff stays minimal. The codemod is idempotent: re-running it over already-migrated manifests makes no further changes.
558
+
559
+ ### Confirm every manifest is migrated
560
+
561
+ Run the codemod in dry-run mode to confirm no committed manifest still carries the removed keys or a stale hash:
562
+
563
+ ```bash
564
+ pnpm exec tsx ./strip-migration-labels-hints.ts --check
565
+ ```
566
+
567
+ `--check` lists every manifest that still needs fixing and exits non-zero if any remain, so wire it into your extension's CI alongside `prisma-8-check-pins`. A fully migrated tree reports `0 needing fix` and exits `0`.
568
+
569
+ ### Validation
570
+
571
+ After running the codemod, run your extension's migration-loading tests (the integration suite that applies your install migration, or whatever exercises the on-disk packages). The loader recomputes and verifies each manifest's `migrationHash` on read: a manifest that still carried `labels`/`hints` would have thrown `INVALID_MANIFEST`, and a manifest with a stale hash would fail verification. Once the codemod has run, every manifest loads cleanly and its recomputed hash verifies against the slimmed envelope.
572
+
573
+ ## `extension-public-default-baseline`
574
+
575
+ Starting at the 0.12 release, Postgres extension packs whose contract-space declares only storage types (no tables) emit their empty default namespace under `public` / `postgres-schema` instead of `__unbound__` / `postgres-unbound-schema`. The on-disk migration ops (`CREATE EXTENSION …`, invariant registration, etc.) are unchanged — only the contract hash envelope moves. Expect diffs in:
576
+
577
+ - `src/contract.json` / `src/contract.d.ts` — new `storageHash` and namespace keys
578
+ - `migrations/<baseline>/migration.json` — updated `to` and `migrationHash`
579
+ - `migrations/<baseline>/end-contract.json` / `end-contract.d.ts` — regenerated snapshots
580
+ - `migrations/<baseline>/migration.ts` — updated `describe().to` storage hash literal
581
+ - `migrations/refs/head.json` — updated `hash`
582
+
583
+ ### Regenerate contract-space and baseline
584
+
585
+ Run the colocated script from your extension package root (or monorepo root if it hosts multiple extension packs):
586
+
587
+ ```bash
588
+ pnpm exec tsx ./regenerate-extension-public-baseline.ts
589
+ ```
590
+
591
+ For each extension root whose `src/contract.json` still carries `"kind": "postgres-unbound-schema"`, the script runs `pnpm build:contract-space`, copies `src/contract.{json,d.ts}` into each baseline migration directory as `end-contract.{json,d.ts}`, patches the baseline `migration.ts` `to` hash, self-emits the migration (`pnpm exec tsx migrations/.../migration.ts`), and updates `migrations/refs/head.json`.
592
+
593
+ Use `--check` to list packs that still need regeneration:
594
+
595
+ ```bash
596
+ pnpm exec tsx ./regenerate-extension-public-baseline.ts --check
597
+ ```
598
+
599
+ Path B baselines (hand-authored install migrations with no planner scaffold) follow the same loop documented in your extension README: edit `describe().to`, then self-emit.
600
+
601
+ ### Validation
602
+
603
+ Run your extension's test suite and any migration-loading integration tests. Confirm `migrations/refs/head.json` `hash` matches `src/contract.json` `storage.storageHash`, and that the baseline `migration.json` `to` field matches as well.
604
+
605
+ ## `domain-plane-spi-and-testing-subpath`
606
+
607
+ Starting at the 0.12 release, two SPI changes affect extension authors:
608
+
609
+ 1. **Namespaced domain plane** — stop reading flat `contract.models` / `contract.valueObjects`. Models and value objects live under `contract.domain.namespaces.<ns>`. Use `domainModelsAtDefaultNamespace(contract.domain)` (reads the contract's sole namespace; throws on a multi-namespace contract — select explicitly per TML-2550) and `ContractModelDefinitions<C>` from `@internal/contract/types` for typed access. Storage remains under `contract.storage.namespaces.<ns>` (unchanged shape).
610
+
611
+ 2. **Removed `@internal/contract/testing` subpath** — test factories moved to `@repo/test-utils`. Add `@repo/test-utils` to your extension's `devDependencies` at the same version pin as your other `@internal/*` packages if it is not already present.
612
+
613
+ ### Migrate test imports
614
+
615
+ Run the colocated codemod from your extension root:
616
+
617
+ ```bash
618
+ pnpm exec tsx ./migrate-contract-testing-imports.ts
619
+ ```
620
+
621
+ It rewrites every `@internal/contract/testing` import to `@repo/test-utils`. Use `--check` for a dry-run:
622
+
623
+ ```bash
624
+ pnpm exec tsx ./migrate-contract-testing-imports.ts --check
625
+ ```
626
+
627
+ Exports are unchanged — only the package path moves:
628
+
629
+ ```diff
630
+ -import { createContract, createSqlContract } from '@internal/contract/testing';
631
+ +import { createContract, createSqlContract } from '@repo/test-utils';
632
+ ```
633
+
634
+ Subpath imports such as `@repo/test-utils/typed-expectations` were already on `@repo/test-utils` and are unaffected.
635
+
636
+ ### Update SPI reads to the namespaced domain shape
637
+
638
+ Walk extension source that constructs or reads contracts directly (tests, control adapters, planners). TypeScript will flag most stale reads after the bump; the mechanical rewrites are:
639
+
640
+ **Reading models** — resolve through the target's default domain namespace:
641
+
642
+ ```diff
643
+ -const models = contract.models;
644
+ +import { domainModelsAtDefaultNamespace } from '@internal/contract/types';
645
+ +
646
+ +const models = domainModelsAtDefaultNamespace(contract.domain);
647
+ ```
648
+
649
+ **Patching models in tests** — nest under the domain namespace:
650
+
651
+ ```diff
652
+ return {
653
+ ...contract,
654
+ - models: patch({ ...contract.models }),
655
+ + domain: {
656
+ + namespaces: {
657
+ + ...contract.domain.namespaces,
658
+ + [namespaceId]: {
659
+ + ...namespace,
660
+ + models: patch({ ...domainModelsAtDefaultNamespace(contract.domain) }),
661
+ + },
662
+ + },
663
+ + },
664
+ };
665
+ ```
666
+
667
+ **Hard-coded `__unbound__` namespace lookups for table resolution** — scan all storage namespaces (a table name is unique within the contract's default resolution path):
668
+
669
+ ```diff
670
+ -const table = contract.storage.namespaces['__unbound__']?.tables[tableName];
671
+ +const table = Object.values(contract.storage.namespaces).find(
672
+ + (ns) => ns.tables[tableName] !== undefined,
673
+ +)?.tables[tableName];
674
+ ```
675
+
676
+ After source updates, re-emit fixture contracts (`pnpm fixtures:emit` or your package's equivalent) so committed `contract.json` / `contract.d.ts` under `test/` pick up `domain.namespaces`.
677
+
678
+ ### Validation
679
+
680
+ Run `pnpm typecheck && pnpm test` on your extension package. The import codemod is deterministic; remaining errors indicate hand-edits for namespaced domain reads. Regenerated fixture diffs should show `domain.namespaces` and `ContractModelDefinitions` (or the emitted `Models` infer alias) in types.
681
+
682
+ ## `default-namespace-domain-access-retire-projection-helpers`
683
+
684
+ Starting at the 0.12 release (runtime qualification, [ADR 223](../../../../docs/architecture%20docs/adrs/ADR%20223%20-%20Target-owned%20default%20namespace.md)), the foundation `contract` package retires the transitional projection helpers introduced during the symmetric domain-plane migration. Extension code that still calls them will fail to compile after the bump.
685
+
686
+ The default namespace a bare name resolves through is **inferred** from the contract (sole namespace, else insertion order) — there are no `…ForSqlTarget` / `…ForMongo` helpers to import. A target's default namespace is declared on its descriptor (`defaultNamespaceId`) and consumed only by authoring; runtime code resolves target-agnostically.
687
+
688
+ ### Removed exports (old → new)
689
+
690
+ | Removed | Replacement |
691
+ |---|---|
692
+ | `contractModels(contract)` | `domainModelsAtDefaultNamespace(contract.domain)` (reads the sole namespace; throws on multi-namespace) |
693
+ | `contractValueObjects(contract)` | `domainValueObjectsAtDefaultNamespace(contract.domain)` |
694
+ | `resolveSingleDomainNamespaceId(domain)` | `soleDomainNamespaceId(domain)` (same fail-loud single-namespace behaviour) |
695
+ | `ContractModelsMap<C>` | `ContractModelDefinitions<C>` |
696
+ | `ContractValueObjectsMap<C>` | Read `contract.domain.namespaces[ns].valueObjects` for a specific namespace, or `domainValueObjectsAtDefaultNamespace(contract.domain)` for the default slot |
697
+
698
+ Import the replacements from `@internal/contract/types`.
699
+
700
+ ### `qualifyTable` on SQL namespace concretions
701
+
702
+ Storage namespace envelopes in SQL-family contracts must carry a `qualifyTable(tableName: string): string` method. The Postgres and SQLite packs in this repo already implement it on bound/unbound namespace concretions; custom serializers or hand-built namespace objects in tests must include it or rendering falls back incorrectly.
703
+
704
+ ### Hydrating contracts in tests
705
+
706
+ Do not `structuredClone` hydrated contracts — it strips functions such as `qualifyTable`. Round-trip through the target serializer instead:
707
+
708
+ ```ts
709
+ import { PostgresContractSerializer } from '@internal/target-postgres/runtime';
710
+
711
+ const serializer = new PostgresContractSerializer();
712
+ const hydrated = serializer.deserializeContract(serializer.serializeContract(rawContract));
713
+ ```
714
+
715
+ ### Namespace-qualified runtime SQL
716
+
717
+ Postgres query renderers now emit `"<schema>"."<table>"` (default schema `public`). Update extension integration tests that assert raw SQL strings (`FROM "user"` → `FROM "public"."user"`). SQLite remains unqualified. Application/extension call sites for `db.sql.*` / `db.*` are unchanged.
718
+
719
+ ### Emitter guard (unchanged for multi-namespace extensions)
720
+
721
+ `assertSingleDomainNamespaceForEmission` still fails when emitting `contract.d.ts` for contracts with multiple domain namespaces ([TML-2550](https://linear.app/prisma-company/issue/TML-2550)). Runtime execution does not throw for multi-namespace contracts; only emission stays fail-loud.
722
+
723
+ ### Validation
724
+
725
+ Run `pnpm typecheck && pnpm test` on your extension package. Grep for the removed symbol names should return no hits outside historical upgrade prose.
726
+
727
+ ## Validation by execution
728
+
729
+ Apart from `strip-migration-labels-hints` (which ships the colocated codemod described above, validated against the migration manifests under `packages/3-extensions/`), these entries are prose-only (no codemod scripts). The substrate diffs inside `packages/3-extensions/` in this transition are the same code translations downstream extension authors will replicate by hand:
730
+
731
+ - The `windowFunc` method literally added to `bindWhereExprNode`'s `ExprVisitor` literal in `where-binding.ts`.
732
+ - The `windowFunc: rejectHavingExpr` literally added to `validateGroupedHavingExpr`'s `ExprVisitor` literal in `query-plan-aggregate.ts`.
733
+ - The `case 'window-func':` arms in the Postgres and SQLite adapter renderers.
734
+ - Flipped fixture row counts in the distinct integration tests.
735
+ - The `RuntimeVerifyOptions` → `VerifyMarkerOption` import rename, `verify?` → `verifyMarker?` on `*OptionsBase`, and `...ifDefined('verifyMarker', options.verifyMarker)` thread-through in `packages/3-extensions/sqlite/src/runtime/sqlite.ts`, `packages/3-extensions/postgres/src/runtime/postgres.ts`, and `packages/3-extensions/postgres/src/runtime/postgres-serverless.ts`.
736
+ - Retargeted option-forwarding and marker-drift tests in `packages/3-extensions/postgres/test/postgres-serverless.test.ts`.
737
+
738
+ There is no scriptable transform — the right body for the `ExprVisitor` method and the right arm for the exhaustive switch depend on what the consumer's visitor / switch does; the right test retargeting for marker drift depends on whether the test asserted throws or option forwarding. The release-pipeline gate (`pnpm check:upgrade-coverage`) is satisfied by this directory existing with at least one entry; the substantive verification of the consumer-facing translation lives in the published extension-upgrade skill's per-step bump-install-instructions-validate-commit loop, which runs in extension authors' own CI.