@prisma-next/target-postgres 0.15.0 → 0.16.0-dev.10

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 (194) hide show
  1. package/dist/{authoring-BtsOWgI3.mjs → authoring-34L18kA1.mjs} +18 -8
  2. package/dist/authoring-34L18kA1.mjs.map +1 -0
  3. package/dist/canonicalize-D1F4vUnP.d.mts.map +1 -1
  4. package/dist/{codec-ids-CI144z7g.d.mts → codec-ids-BswpvvND.d.mts} +3 -3
  5. package/dist/codec-ids-BswpvvND.d.mts.map +1 -0
  6. package/dist/{codec-ids-B10ZZHzw.mjs → codec-ids-D3G8K0YM.mjs} +3 -2
  7. package/dist/codec-ids-D3G8K0YM.mjs.map +1 -0
  8. package/dist/codec-ids.d.mts +2 -2
  9. package/dist/codec-ids.mjs +2 -2
  10. package/dist/{codec-types-CTfT4U4O.d.mts → codec-types-CQ07nOz-.d.mts} +4 -5
  11. package/dist/codec-types-CQ07nOz-.d.mts.map +1 -0
  12. package/dist/codec-types.d.mts +1 -1
  13. package/dist/{codecs-D7W9O0Xx.d.mts → codecs-G4DTY0F_.d.mts} +35 -7
  14. package/dist/codecs-G4DTY0F_.d.mts.map +1 -0
  15. package/dist/{codecs-DRbb26Or.mjs → codecs-TCsGgW-Y.mjs} +77 -5
  16. package/dist/codecs-TCsGgW-Y.mjs.map +1 -0
  17. package/dist/codecs.d.mts +2 -3
  18. package/dist/codecs.d.mts.map +1 -1
  19. package/dist/codecs.mjs +2 -2
  20. package/dist/contract-free.d.mts +2 -3
  21. package/dist/contract-free.d.mts.map +1 -1
  22. package/dist/contract-free.mjs +3 -3
  23. package/dist/{control-policy-DtbkLLGv.mjs → control-policy-S7XBe07W.mjs} +44 -75
  24. package/dist/control-policy-S7XBe07W.mjs.map +1 -0
  25. package/dist/control.d.mts +0 -1
  26. package/dist/control.d.mts.map +1 -1
  27. package/dist/control.mjs +75 -25
  28. package/dist/control.mjs.map +1 -1
  29. package/dist/data-transform-BOWpliq8.mjs.map +1 -1
  30. package/dist/{data-transform-DPzwBMhW.d.mts → data-transform-CGvE8yGx.d.mts} +1 -2
  31. package/dist/data-transform-CGvE8yGx.d.mts.map +1 -0
  32. package/dist/data-transform.d.mts +1 -1
  33. package/dist/{ddl-GTTOvjx_.mjs → ddl-DHXKzQ99.mjs} +3 -3
  34. package/dist/{ddl-GTTOvjx_.mjs.map → ddl-DHXKzQ99.mjs.map} +1 -1
  35. package/dist/ddl.d.mts +1 -1
  36. package/dist/default-normalizer-B9ZUiyUE.mjs +264 -0
  37. package/dist/default-normalizer-B9ZUiyUE.mjs.map +1 -0
  38. package/dist/default-normalizer.d.mts +14 -2
  39. package/dist/default-normalizer.d.mts.map +1 -1
  40. package/dist/default-normalizer.mjs +2 -248
  41. package/dist/{descriptor-meta-CFQKLXl_.mjs → descriptor-meta-qubYaxQA.mjs} +9 -4
  42. package/dist/descriptor-meta-qubYaxQA.mjs.map +1 -0
  43. package/dist/{diff-database-schema-GncqMuSI.mjs → diff-database-schema-BRxq7l98.mjs} +92 -26
  44. package/dist/diff-database-schema-BRxq7l98.mjs.map +1 -0
  45. package/dist/diff-database-schema.d.mts +4 -13
  46. package/dist/diff-database-schema.d.mts.map +1 -1
  47. package/dist/diff-database-schema.mjs +1 -1
  48. package/dist/{errors-DO-w0EUS.mjs → errors-ByDS0xJL.mjs} +6 -6
  49. package/dist/errors-ByDS0xJL.mjs.map +1 -0
  50. package/dist/errors.d.mts +4 -4
  51. package/dist/errors.d.mts.map +1 -1
  52. package/dist/errors.mjs +1 -1
  53. package/dist/issue-planner.d.mts +1 -2
  54. package/dist/issue-planner.d.mts.map +1 -1
  55. package/dist/issue-planner.mjs +1 -1
  56. package/dist/migration.d.mts +3 -4
  57. package/dist/migration.d.mts.map +1 -1
  58. package/dist/migration.mjs +2 -2
  59. package/dist/native-type-normalizer.d.mts.map +1 -1
  60. package/dist/{nodes-Dv5eCSq9.d.mts → nodes-CQzM2hR0.d.mts} +1 -2
  61. package/dist/nodes-CQzM2hR0.d.mts.map +1 -0
  62. package/dist/{op-factory-call-C38NzvQD.mjs → op-factory-call-Ci3H6InB.mjs} +37 -21
  63. package/dist/op-factory-call-Ci3H6InB.mjs.map +1 -0
  64. package/dist/{op-factory-call-Bn9zDx4_.d.mts → op-factory-call-DT7kdjVP.d.mts} +10 -7
  65. package/dist/op-factory-call-DT7kdjVP.d.mts.map +1 -0
  66. package/dist/op-factory-call.d.mts +2 -2
  67. package/dist/op-factory-call.mjs +2 -2
  68. package/dist/pack.d.mts +122 -6
  69. package/dist/pack.d.mts.map +1 -1
  70. package/dist/pack.mjs +1 -1
  71. package/dist/{planner-kql24i_F.mjs → planner-BpL7uktH.mjs} +15 -15
  72. package/dist/planner-BpL7uktH.mjs.map +1 -0
  73. package/dist/planner-ddl-builders.d.mts +1 -2
  74. package/dist/planner-ddl-builders.d.mts.map +1 -1
  75. package/dist/planner-identity-values.d.mts +0 -1
  76. package/dist/planner-identity-values.d.mts.map +1 -1
  77. package/dist/{planner-produced-postgres-migration-BEY1T0Xm.d.mts → planner-produced-postgres-migration-8QP8eMph.d.mts} +3 -4
  78. package/dist/planner-produced-postgres-migration-8QP8eMph.d.mts.map +1 -0
  79. package/dist/{planner-produced-postgres-migration-D7y-PEHK.mjs → planner-produced-postgres-migration-D8exrrxA.mjs} +8 -5
  80. package/dist/planner-produced-postgres-migration-D8exrrxA.mjs.map +1 -0
  81. package/dist/planner-produced-postgres-migration.d.mts +1 -1
  82. package/dist/planner-produced-postgres-migration.mjs +1 -1
  83. package/dist/planner-schema-lookup.d.mts +0 -1
  84. package/dist/planner-schema-lookup.d.mts.map +1 -1
  85. package/dist/{planner-sql-checks-zqNfs2XZ.mjs → planner-sql-checks-BuB877wH.mjs} +2 -2
  86. package/dist/{planner-sql-checks-zqNfs2XZ.mjs.map → planner-sql-checks-BuB877wH.mjs.map} +1 -1
  87. package/dist/planner-sql-checks.d.mts +0 -1
  88. package/dist/planner-sql-checks.d.mts.map +1 -1
  89. package/dist/planner-sql-checks.mjs +1 -1
  90. package/dist/planner-target-details-HkP4RrRO.d.mts.map +1 -1
  91. package/dist/planner.d.mts +9 -4
  92. package/dist/planner.d.mts.map +1 -1
  93. package/dist/planner.mjs +2 -2
  94. package/dist/{postgres-contract-view-C9j5cqP0.d.mts → postgres-contract-view-CeU7FJjW.d.mts} +4 -3
  95. package/dist/postgres-contract-view-CeU7FJjW.d.mts.map +1 -0
  96. package/dist/{postgres-contract-view-DM8sN1Ot.mjs → postgres-contract-view-DebHMqGR.mjs} +4 -4
  97. package/dist/{postgres-contract-view-DM8sN1Ot.mjs.map → postgres-contract-view-DebHMqGR.mjs.map} +1 -1
  98. package/dist/{postgres-database-schema-node-CJv5bfbs.d.mts → postgres-database-schema-node-DMXsIwAb.d.mts} +12 -4
  99. package/dist/postgres-database-schema-node-DMXsIwAb.d.mts.map +1 -0
  100. package/dist/{postgres-migration-arMSWU9b.mjs → postgres-migration-Bn3fj1I4.mjs} +22 -6
  101. package/dist/postgres-migration-Bn3fj1I4.mjs.map +1 -0
  102. package/dist/{postgres-migration-DbckK1su.d.mts → postgres-migration-DLIvzHjX.d.mts} +31 -7
  103. package/dist/postgres-migration-DLIvzHjX.d.mts.map +1 -0
  104. package/dist/{postgres-rls-policy--CSddvMR.d.mts → postgres-rls-policy-CYXdqG9B.d.mts} +1 -2
  105. package/dist/postgres-rls-policy-CYXdqG9B.d.mts.map +1 -0
  106. package/dist/{postgres-role-schema-node-Cm-j0ZGM.mjs → postgres-role-schema-node-bg32e7I-.mjs} +3 -2
  107. package/dist/postgres-role-schema-node-bg32e7I-.mjs.map +1 -0
  108. package/dist/{postgres-schema-Cm4DtiGA.mjs → postgres-schema-CewLYHB7.mjs} +2 -2
  109. package/dist/{postgres-schema-Cm4DtiGA.mjs.map → postgres-schema-CewLYHB7.mjs.map} +1 -1
  110. package/dist/{postgres-schema-DKq6s1P3.d.mts → postgres-schema-FdKRwe7X.d.mts} +3 -4
  111. package/dist/postgres-schema-FdKRwe7X.d.mts.map +1 -0
  112. package/dist/render-ops.d.mts +0 -1
  113. package/dist/render-ops.d.mts.map +1 -1
  114. package/dist/{render-typescript-7yqILcwr.mjs → render-typescript-Ta7_6CGf.mjs} +20 -8
  115. package/dist/render-typescript-Ta7_6CGf.mjs.map +1 -0
  116. package/dist/render-typescript.d.mts +2 -1
  117. package/dist/render-typescript.d.mts.map +1 -1
  118. package/dist/render-typescript.mjs +1 -1
  119. package/dist/{resolve-ddl-schema-XyBaaenM.mjs → resolve-ddl-schema-D0wi_P9Q.mjs} +2 -2
  120. package/dist/{resolve-ddl-schema-XyBaaenM.mjs.map → resolve-ddl-schema-D0wi_P9Q.mjs.map} +1 -1
  121. package/dist/rls-canonicalize.d.mts +0 -1
  122. package/dist/rls-canonicalize.d.mts.map +1 -1
  123. package/dist/runtime.d.mts +1 -2
  124. package/dist/runtime.d.mts.map +1 -1
  125. package/dist/runtime.mjs +1 -1
  126. package/dist/schema-ir-annotations.d.mts +0 -1
  127. package/dist/schema-ir-annotations.d.mts.map +1 -1
  128. package/dist/schema-ir-annotations.mjs +1 -1
  129. package/dist/schema-node-kinds-DxvWh2Ic.d.mts +29 -0
  130. package/dist/schema-node-kinds-DxvWh2Ic.d.mts.map +1 -0
  131. package/dist/sql-utils.d.mts.map +1 -1
  132. package/dist/{table-source-BvFo7gVs.d.mts → table-source-D5c2fxAQ.d.mts} +1 -2
  133. package/dist/table-source-D5c2fxAQ.d.mts.map +1 -0
  134. package/dist/{types-BDKkx8MA.d.mts → types-D2MLzLBD.d.mts} +1 -2
  135. package/dist/types-D2MLzLBD.d.mts.map +1 -0
  136. package/dist/types.d.mts +6 -5
  137. package/dist/types.mjs +4 -4
  138. package/package.json +21 -21
  139. package/src/core/authoring.ts +18 -5
  140. package/src/core/codec-helpers.ts +53 -1
  141. package/src/core/codec-ids.ts +1 -0
  142. package/src/core/codecs.ts +56 -3
  143. package/src/core/default-normalizer.ts +22 -0
  144. package/src/core/descriptor-meta.ts +2 -0
  145. package/src/core/errors.ts +5 -5
  146. package/src/core/index-types.ts +15 -0
  147. package/src/core/migrations/contract-to-postgres-database-schema-node.ts +99 -19
  148. package/src/core/migrations/control-policy.ts +4 -3
  149. package/src/core/migrations/diff-database-schema.ts +6 -3
  150. package/src/core/migrations/issue-planner.ts +41 -80
  151. package/src/core/migrations/op-factory-call.ts +41 -22
  152. package/src/core/migrations/operations/data-transform.ts +2 -2
  153. package/src/core/migrations/planner-produced-postgres-migration.ts +9 -2
  154. package/src/core/migrations/planner-strategies.ts +6 -6
  155. package/src/core/migrations/planner.ts +13 -4
  156. package/src/core/migrations/postgres-migration.ts +62 -2
  157. package/src/core/migrations/render-typescript.ts +20 -8
  158. package/src/core/migrations/runner.ts +12 -11
  159. package/src/core/migrations/verify-postgres-namespaces.ts +0 -1
  160. package/src/core/psl-infer/infer-psl-contract.ts +130 -12
  161. package/src/core/schema-ir/postgres-policy-schema-node.ts +12 -2
  162. package/src/exports/codecs.ts +2 -0
  163. package/src/exports/control.ts +2 -0
  164. package/src/exports/default-normalizer.ts +1 -1
  165. package/src/exports/op-factory-call.ts +5 -0
  166. package/src/exports/types.ts +1 -0
  167. package/dist/authoring-BtsOWgI3.mjs.map +0 -1
  168. package/dist/codec-ids-B10ZZHzw.mjs.map +0 -1
  169. package/dist/codec-ids-CI144z7g.d.mts.map +0 -1
  170. package/dist/codec-types-CTfT4U4O.d.mts.map +0 -1
  171. package/dist/codecs-D7W9O0Xx.d.mts.map +0 -1
  172. package/dist/codecs-DRbb26Or.mjs.map +0 -1
  173. package/dist/control-policy-DtbkLLGv.mjs.map +0 -1
  174. package/dist/data-transform-DPzwBMhW.d.mts.map +0 -1
  175. package/dist/default-normalizer.mjs.map +0 -1
  176. package/dist/descriptor-meta-CFQKLXl_.mjs.map +0 -1
  177. package/dist/diff-database-schema-GncqMuSI.mjs.map +0 -1
  178. package/dist/errors-DO-w0EUS.mjs.map +0 -1
  179. package/dist/nodes-Dv5eCSq9.d.mts.map +0 -1
  180. package/dist/op-factory-call-Bn9zDx4_.d.mts.map +0 -1
  181. package/dist/op-factory-call-C38NzvQD.mjs.map +0 -1
  182. package/dist/planner-kql24i_F.mjs.map +0 -1
  183. package/dist/planner-produced-postgres-migration-BEY1T0Xm.d.mts.map +0 -1
  184. package/dist/planner-produced-postgres-migration-D7y-PEHK.mjs.map +0 -1
  185. package/dist/postgres-contract-view-C9j5cqP0.d.mts.map +0 -1
  186. package/dist/postgres-database-schema-node-CJv5bfbs.d.mts.map +0 -1
  187. package/dist/postgres-migration-DbckK1su.d.mts.map +0 -1
  188. package/dist/postgres-migration-arMSWU9b.mjs.map +0 -1
  189. package/dist/postgres-rls-policy--CSddvMR.d.mts.map +0 -1
  190. package/dist/postgres-role-schema-node-Cm-j0ZGM.mjs.map +0 -1
  191. package/dist/postgres-schema-DKq6s1P3.d.mts.map +0 -1
  192. package/dist/render-typescript-7yqILcwr.mjs.map +0 -1
  193. package/dist/table-source-BvFo7gVs.d.mts.map +0 -1
  194. package/dist/types-BDKkx8MA.d.mts.map +0 -1
@@ -25,6 +25,7 @@ import type {
25
25
  } from '@prisma-next/family-sql/control';
26
26
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
27
27
  import type { DiffableNode, SchemaDiffIssue } from '@prisma-next/framework-components/control';
28
+ import { issueOutcome, orderIssuesByDependencies } from '@prisma-next/framework-components/control';
28
29
  import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
29
30
  import type { SqlStorage, StorageTypeInstance } from '@prisma-next/sql-contract/types';
30
31
  import type { DdlTableConstraint } from '@prisma-next/sql-relational-core/ast';
@@ -136,6 +137,7 @@ export interface IssuePlannerValue {
136
137
  type CallCategory =
137
138
  | 'dep'
138
139
  | 'drop'
140
+ | 'dropType'
139
141
  | 'table'
140
142
  | 'rlsEnable'
141
143
  | 'rlsPolicy'
@@ -151,6 +153,13 @@ type CallCategory =
151
153
  * legacy walk-schema planner's emission order so `db init` and `db update`
152
154
  * produce byte-identical plans for the shared shape (deps → drops → tables
153
155
  * → columns → alters → PKs → uniques → indexes → FKs).
156
+ *
157
+ * `dropType` (DROP TYPE for a managed native enum) is its own bucket, ordered
158
+ * after the general drop bucket: a type can only be dropped once every table
159
+ * whose column uses it is gone. A column→enum edge would make this precise,
160
+ * but `coalesceSubtreeIssues` removes the column issue under a whole-table
161
+ * drop, so the edge never reaches the graph; the coarse bucket stands in until
162
+ * that edge lands (the coordinate work is a later slice).
154
163
  */
155
164
  function classifyCall(call: PostgresOpFactoryCall): CallCategory {
156
165
  switch (call.factoryName) {
@@ -159,8 +168,9 @@ function classifyCall(call: PostgresOpFactoryCall): CallCategory {
159
168
  case 'createNativeEnumType':
160
169
  case 'addNativeEnumValue':
161
170
  return 'dep';
162
- case 'dropTable':
163
171
  case 'dropNativeEnumType':
172
+ return 'dropType';
173
+ case 'dropTable':
164
174
  case 'dropColumn':
165
175
  case 'dropConstraint':
166
176
  case 'dropCheckConstraint':
@@ -369,57 +379,6 @@ export function columnTypeChanged(expected: SqlColumnIR, actual: SqlColumnIR): b
369
379
  );
370
380
  }
371
381
 
372
- // ----------------------------------------------------------------------------
373
- // Node-keyed issue ordering (re-keys ISSUE_KIND_ORDER on nodeKind + reason)
374
- // ----------------------------------------------------------------------------
375
-
376
- /**
377
- * Re-keys the legacy `ISSUE_KIND_ORDER` on `(nodeKind, reason)`, numbers
378
- * preserved so the dependency intent stays legible. Final emission order is
379
- * fixed downstream by `classifyCall` bucketing (dep → drop → table → column →
380
- * recipe → alter → primaryKey → unique → index → foreignKey), so this only
381
- * breaks ties within a bucket.
382
- */
383
- export function nodeIssueOrder(issue: SchemaDiffIssue): number {
384
- const node = issueNode(issue);
385
- if (node === undefined) return 99;
386
- switch (node.nodeKind) {
387
- case PostgresSchemaNodeKind.namespace:
388
- return 1;
389
- case PostgresSchemaNodeKind.nativeEnum:
390
- // Creates order right after namespace creates within the 'dep' bucket
391
- // (CREATE SCHEMA before CREATE TYPE); drops order after table drops
392
- // within the 'drop' bucket (DROP TYPE only after its dependents left).
393
- return issue.reason === 'not-expected' ? 17 : 2;
394
- case RelationalSchemaNodeKind.foreignKey:
395
- return issue.reason === 'not-expected' ? 10 : 60;
396
- case RelationalSchemaNodeKind.unique:
397
- return issue.reason === 'not-expected' ? 11 : 51;
398
- case RelationalSchemaNodeKind.primaryKey:
399
- return issue.reason === 'not-expected' ? 12 : 50;
400
- case RelationalSchemaNodeKind.index:
401
- return issue.reason === 'not-expected' ? 13 : 52;
402
- case RelationalSchemaNodeKind.columnDefault:
403
- if (issue.reason === 'not-expected') return 14;
404
- return issue.reason === 'not-found' ? 42 : 43;
405
- case RelationalSchemaNodeKind.column:
406
- if (issue.reason === 'not-expected') return 15;
407
- return issue.reason === 'not-found' ? 30 : 40;
408
- case PostgresSchemaNodeKind.table:
409
- return issue.reason === 'not-expected' ? 16 : 20;
410
- case RelationalSchemaNodeKind.check:
411
- if (issue.reason === 'not-found') return 53;
412
- return issue.reason === 'not-expected' ? 55 : 54;
413
- default:
414
- return 99;
415
- }
416
- }
417
-
418
- /** Deterministic tiebreak within an order bucket: the diff path already encodes schema → table → child. */
419
- export function nodeIssueKey(issue: SchemaDiffIssue): string {
420
- return issue.path.join(' ');
421
- }
422
-
423
382
  // ----------------------------------------------------------------------------
424
383
  // Subtree coalescing (the planner's responsibility per the differ's contract)
425
384
  // ----------------------------------------------------------------------------
@@ -437,7 +396,7 @@ export function coalesceSubtreeIssues<TNode extends DiffableNode = DiffableNode>
437
396
  issues: readonly SchemaDiffIssue<TNode>[],
438
397
  ): readonly SchemaDiffIssue<TNode>[] {
439
398
  const collapsingPaths = issues
440
- .filter((issue) => issue.reason === 'not-found' || issue.reason === 'not-expected')
399
+ .filter((issue) => issueOutcome(issue) !== 'not-equal')
441
400
  .map((issue) => issue.path);
442
401
  if (collapsingPaths.length === 0) return issues;
443
402
  return issues.filter(
@@ -601,14 +560,14 @@ function mapNativeEnumNodeIssue(
601
560
  );
602
561
  }
603
562
  const schemaName = emissionSchemaName(ctx, ddlSchemaName);
604
- if (issue.reason === 'not-found') {
563
+ if (issueOutcome(issue) === 'not-found') {
605
564
  const expected = blindCast<
606
565
  PostgresNativeEnumSchemaNode,
607
566
  'a not-found native-enum issue always carries the expected PostgresNativeEnumSchemaNode'
608
567
  >(issue.expected);
609
568
  return ok([new CreateNativeEnumTypeCall(schemaName, expected.typeName, expected.members)]);
610
569
  }
611
- if (issue.reason === 'not-expected') {
570
+ if (issueOutcome(issue) === 'not-expected') {
612
571
  const actual = blindCast<
613
572
  PostgresNativeEnumSchemaNode,
614
573
  'a not-expected native-enum issue always carries the actual PostgresNativeEnumSchemaNode'
@@ -654,14 +613,14 @@ function mapTableNodeIssue(
654
613
  ddlSchemaName: string,
655
614
  codecHooks: ReadonlyMap<string, CodecControlHooks>,
656
615
  ): Result<readonly PostgresOpFactoryCall[], SqlPlannerConflict> {
657
- if (issue.reason === 'not-found') {
616
+ if (issueOutcome(issue) === 'not-found') {
658
617
  const table = blindCast<
659
618
  PostgresTableSchemaNode,
660
619
  'a not-found table issue always carries the expected PostgresTableSchemaNode'
661
620
  >(issue.expected);
662
621
  return ok(buildCreateTableCallsFromNode(schemaName, ddlSchemaName, table, codecHooks));
663
622
  }
664
- if (issue.reason === 'not-expected') {
623
+ if (issueOutcome(issue) === 'not-expected') {
665
624
  const table = blindCast<
666
625
  PostgresTableSchemaNode,
667
626
  'a not-expected table issue always carries the actual PostgresTableSchemaNode'
@@ -703,7 +662,7 @@ function mapColumnNodeIssue(
703
662
  tableName: string,
704
663
  codecHooks: ReadonlyMap<string, CodecControlHooks>,
705
664
  ): Result<readonly PostgresOpFactoryCall[], SqlPlannerConflict> {
706
- if (issue.reason === 'not-found') {
665
+ if (issueOutcome(issue) === 'not-found') {
707
666
  const column = blindCast<
708
667
  SqlColumnIR,
709
668
  'a not-found column issue always carries the expected column node'
@@ -712,7 +671,7 @@ function mapColumnNodeIssue(
712
671
  new AddColumnCall(schemaName, tableName, renderColumnDdl(column.name, column, codecHooks)),
713
672
  ]);
714
673
  }
715
- if (issue.reason === 'not-expected') {
674
+ if (issueOutcome(issue) === 'not-expected') {
716
675
  const column = blindCast<
717
676
  SqlColumnIR,
718
677
  'a not-expected column issue always carries the actual column node'
@@ -755,7 +714,7 @@ function mapColumnDefaultNodeIssue(
755
714
  tableName: string,
756
715
  columnName: string,
757
716
  ): Result<readonly PostgresOpFactoryCall[], SqlPlannerConflict> {
758
- if (issue.reason === 'not-expected') {
717
+ if (issueOutcome(issue) === 'not-expected') {
759
718
  return ok([new DropDefaultCall(schemaName, tableName, columnName)]);
760
719
  }
761
720
  // not-found (SET DEFAULT, additive) or not-equal (SET DEFAULT, widening).
@@ -772,7 +731,7 @@ function mapColumnDefaultNodeIssue(
772
731
  tableName,
773
732
  columnName,
774
733
  defaultSql,
775
- issue.reason === 'not-equal' ? 'widening' : 'additive',
734
+ issueOutcome(issue) === 'not-equal' ? 'widening' : 'additive',
776
735
  ),
777
736
  ]);
778
737
  }
@@ -782,7 +741,7 @@ function mapPrimaryKeyNodeIssue(
782
741
  schemaName: string,
783
742
  tableName: string,
784
743
  ): Result<readonly PostgresOpFactoryCall[], SqlPlannerConflict> {
785
- if (issue.reason === 'not-found') {
744
+ if (issueOutcome(issue) === 'not-found') {
786
745
  const pk = blindCast<
787
746
  { readonly columns: readonly string[]; readonly name?: string },
788
747
  'a not-found primary-key issue always carries the expected PrimaryKey node'
@@ -790,7 +749,7 @@ function mapPrimaryKeyNodeIssue(
790
749
  const constraintName = pk.name ?? `${tableName}_pkey`;
791
750
  return ok([new AddPrimaryKeyCall(schemaName, tableName, constraintName, [...pk.columns])]);
792
751
  }
793
- if (issue.reason === 'not-expected') {
752
+ if (issueOutcome(issue) === 'not-expected') {
794
753
  const pk = blindCast<
795
754
  { readonly name?: string },
796
755
  'a not-expected primary-key issue always carries the actual PrimaryKey node'
@@ -807,14 +766,14 @@ function mapForeignKeyNodeIssue(
807
766
  schemaName: string,
808
767
  tableName: string,
809
768
  ): Result<readonly PostgresOpFactoryCall[], SqlPlannerConflict> {
810
- if (issue.reason === 'not-found') {
769
+ if (issueOutcome(issue) === 'not-found') {
811
770
  const fk = blindCast<
812
771
  SqlForeignKeyIR,
813
772
  'a not-found foreign-key issue always carries the expected foreign-key node'
814
773
  >(issue.expected);
815
774
  return ok([new AddForeignKeyCall(schemaName, tableName, fkSpecFromNode(fk, tableName))]);
816
775
  }
817
- if (issue.reason === 'not-expected') {
776
+ if (issueOutcome(issue) === 'not-expected') {
818
777
  const fk = blindCast<
819
778
  SqlForeignKeyIR,
820
779
  'a not-expected foreign-key issue always carries the actual foreign-key node'
@@ -830,7 +789,7 @@ function mapUniqueNodeIssue(
830
789
  schemaName: string,
831
790
  tableName: string,
832
791
  ): Result<readonly PostgresOpFactoryCall[], SqlPlannerConflict> {
833
- if (issue.reason === 'not-found') {
792
+ if (issueOutcome(issue) === 'not-found') {
834
793
  const unique = blindCast<
835
794
  SqlUniqueIR,
836
795
  'a not-found unique issue always carries the expected unique node'
@@ -838,7 +797,7 @@ function mapUniqueNodeIssue(
838
797
  const name = unique.name ?? `${tableName}_${unique.columns.join('_')}_key`;
839
798
  return ok([new AddUniqueCall(schemaName, tableName, name, [...unique.columns])]);
840
799
  }
841
- if (issue.reason === 'not-expected') {
800
+ if (issueOutcome(issue) === 'not-expected') {
842
801
  const unique = blindCast<
843
802
  SqlUniqueIR,
844
803
  'a not-expected unique issue always carries the actual unique node'
@@ -854,7 +813,7 @@ function mapIndexNodeIssue(
854
813
  schemaName: string,
855
814
  tableName: string,
856
815
  ): Result<readonly PostgresOpFactoryCall[], SqlPlannerConflict> {
857
- if (issue.reason === 'not-found') {
816
+ if (issueOutcome(issue) === 'not-found') {
858
817
  const index = blindCast<
859
818
  SqlIndexIR,
860
819
  'a not-found index issue always carries the expected index node'
@@ -865,7 +824,7 @@ function mapIndexNodeIssue(
865
824
  if (index.options !== undefined) extras.options = index.options;
866
825
  return ok([new CreateIndexCall(schemaName, tableName, indexName, [...index.columns], extras)]);
867
826
  }
868
- if (issue.reason === 'not-expected') {
827
+ if (issueOutcome(issue) === 'not-expected') {
869
828
  const index = blindCast<
870
829
  SqlIndexIR,
871
830
  'a not-expected index issue always carries the actual index node'
@@ -885,7 +844,7 @@ function mapCheckNodeIssue(
885
844
  // the default mapper handles directly; check_missing / check_mismatch are
886
845
  // consumed by `checkConstraintPlanCallStrategy` (drop+recreate), so reaching
887
846
  // here for them means the strategy did not run — a conflict.
888
- if (issue.reason === 'not-expected') {
847
+ if (issueOutcome(issue) === 'not-expected') {
889
848
  const check = blindCast<
890
849
  { readonly name: string },
891
850
  'a not-expected check issue always carries the actual check node'
@@ -902,8 +861,8 @@ function mapCheckNodeIssue(
902
861
 
903
862
  /**
904
863
  * Maps one node-typed diff issue to its migration call(s), dispatching on the
905
- * node's `nodeKind` + `issue.reason`, reading nodes and resolving column DDL
906
- * from `codecRef` via `column-ddl-rendering.ts`.
864
+ * node's `nodeKind` and the issue's presence-derived `issueOutcome`, reading
865
+ * nodes and resolving column DDL from `codecRef` via `column-ddl-rendering.ts`.
907
866
  */
908
867
  export function mapNodeIssueToCall(
909
868
  issue: SchemaDiffIssue,
@@ -919,7 +878,7 @@ export function mapNodeIssueToCall(
919
878
  );
920
879
  }
921
880
  if (node.nodeKind === PostgresSchemaNodeKind.namespace) {
922
- if (issue.reason !== 'not-found') {
881
+ if (issueOutcome(issue) !== 'not-found') {
923
882
  return notOk(
924
883
  nodeConflict('unsupportedOperation', `Unexpected namespace drift: ${issue.path.join('/')}`),
925
884
  );
@@ -1052,13 +1011,12 @@ export function planIssues(
1052
1011
  }
1053
1012
  }
1054
1013
 
1055
- const sorted = [...remaining].sort((a, b) => {
1056
- const kindDelta = nodeIssueOrder(a) - nodeIssueOrder(b);
1057
- if (kindDelta !== 0) return kindDelta;
1058
- const keyA = nodeIssueKey(a);
1059
- const keyB = nodeIssueKey(b);
1060
- return keyA < keyB ? -1 : keyA > keyB ? 1 : 0;
1061
- });
1014
+ // Dependency-graph order: a topological sort over the issues' `dependsOn`
1015
+ // cross-links and containment edges (the ordering law drives edge direction
1016
+ // by presence), path-tiebroken for determinism. `classifyCall` bucketing
1017
+ // downstream fixes the coarse cross-entity DDL sequence; this settles the
1018
+ // order within each bucket (notably the teardown order among drops).
1019
+ const sorted = orderIssuesByDependencies(remaining);
1062
1020
 
1063
1021
  const defaultCalls: PostgresOpFactoryCall[] = [];
1064
1022
  const conflicts: SqlPlannerConflict[] = [];
@@ -1106,6 +1064,9 @@ export function planIssues(
1106
1064
  const calls: PostgresOpFactoryCall[] = [
1107
1065
  ...byCategory('dep'),
1108
1066
  ...byCategory('drop'),
1067
+ // DROP TYPE after every table drop — a managed enum type can only go once
1068
+ // no live column still uses it.
1069
+ ...byCategory('dropType'),
1109
1070
  ...byCategory('table'),
1110
1071
  ...byCategory('column'),
1111
1072
  ...gatedRecipe,
@@ -9,7 +9,7 @@
9
9
  * - `toOp()` — converts the IR node to a runtime
10
10
  * `SqlMigrationPlanOperation` by delegating to the matching pure factory
11
11
  * under `operations/`. `DataTransformCall.toOp()` always throws
12
- * `PN-MIG-2001` because a planner-generated data transform is an
12
+ * `MIGRATION.UNFILLED_PLACEHOLDER` because a planner-generated data transform is an
13
13
  * unfilled authoring stub by construction.
14
14
  * - `renderTypeScript()` / `importRequirements()` — inherited from
15
15
  * `TsExpression`. Used by `renderCallsToTypeScript` to emit the call as
@@ -45,7 +45,7 @@ import {
45
45
  tableExistsAst,
46
46
  } from '../../contract-free/checks';
47
47
  import * as contractFreeDdl from '../../contract-free/ddl';
48
- import type { PostgresRlsPolicy } from '../postgres-rls-policy';
48
+ import type { PostgresRlsPolicy, PostgresRlsPolicyInput } from '../postgres-rls-policy';
49
49
  import {
50
50
  escapeLiteral,
51
51
  quoteIdentifier,
@@ -740,10 +740,6 @@ export class AddNotNullColumnDirectCall extends PostgresOpFactoryCallNode {
740
740
  renderTypeScript(): string {
741
741
  return `rawSql(${jsonToTsSource({ id: `column.${this.tableName}.${this.columnName}`, label: this.label, operationClass: 'additive' })})`;
742
742
  }
743
-
744
- override importRequirements(): readonly ImportRequirement[] {
745
- return [];
746
- }
747
743
  }
748
744
 
749
745
  /**
@@ -810,10 +806,6 @@ export class AddNotNullColumnWithTempDefaultCall extends PostgresOpFactoryCallNo
810
806
  renderTypeScript(): string {
811
807
  return `rawSql(${jsonToTsSource({ id: `column.${this.tableName}.${this.columnName}`, label: this.label, operationClass: 'additive' })})`;
812
808
  }
813
-
814
- override importRequirements(): readonly ImportRequirement[] {
815
- return [];
816
- }
817
809
  }
818
810
 
819
811
  // ============================================================================
@@ -1515,7 +1507,7 @@ export class AddNativeEnumValueCall extends PostgresOpFactoryCallNode {
1515
1507
  * A planner-generated data-transform stub. `checkSlot` and `runSlot` name
1516
1508
  * the unfilled authoring slots that the rendered `migration.ts` will expose
1517
1509
  * to the user via `placeholder("…")` calls. `toOp()` always throws
1518
- * `PN-MIG-2001`: the planner cannot lower a stubbed transform to a runtime
1510
+ * `MIGRATION.UNFILLED_PLACEHOLDER`: the planner cannot lower a stubbed transform to a runtime
1519
1511
  * op — the user must fill the rendered `migration.ts` and re-emit.
1520
1512
  */
1521
1513
  export class DataTransformCall extends PostgresOpFactoryCallNode {
@@ -1555,12 +1547,7 @@ export class DataTransformCall extends PostgresOpFactoryCallNode {
1555
1547
  override importRequirements(): readonly ImportRequirement[] {
1556
1548
  return [
1557
1549
  { moduleSpecifier: POSTGRES_MIGRATION_FACADE, symbol: 'placeholder' },
1558
- {
1559
- moduleSpecifier: './end-contract.json',
1560
- symbol: 'endContract',
1561
- kind: 'default',
1562
- attributes: { type: 'json' },
1563
- },
1550
+ // endContract is imported by the migration scaffold's contractImports; an op never renders outside it.
1564
1551
  ];
1565
1552
  }
1566
1553
  }
@@ -1592,7 +1579,23 @@ export class CreatePostgresRlsPolicyCall extends PostgresOpFactoryCallNode {
1592
1579
  }
1593
1580
 
1594
1581
  renderTypeScript(): string {
1595
- return `createRlsPolicy(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.policy)})`;
1582
+ const p = this.policy;
1583
+ const input: PostgresRlsPolicyInput = {
1584
+ name: p.name,
1585
+ prefix: p.prefix,
1586
+ tableName: p.tableName,
1587
+ namespaceId: p.namespaceId,
1588
+ operation: p.operation,
1589
+ roles: p.roles,
1590
+ ...ifDefined('using', p.using),
1591
+ ...ifDefined('withCheck', p.withCheck),
1592
+ permissive: p.permissive,
1593
+ };
1594
+ return `this.createRlsPolicy({ schema: ${jsonToTsSource(this.schemaName)}, table: ${jsonToTsSource(this.tableName)}, policy: ${jsonToTsSource(input)} })`;
1595
+ }
1596
+
1597
+ override importRequirements(): readonly ImportRequirement[] {
1598
+ return [];
1596
1599
  }
1597
1600
  }
1598
1601
 
@@ -1623,7 +1626,11 @@ export class DropPostgresRlsPolicyCall extends PostgresOpFactoryCallNode {
1623
1626
  }
1624
1627
 
1625
1628
  renderTypeScript(): string {
1626
- return `dropRlsPolicy(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.policyName)})`;
1629
+ return `this.dropRlsPolicy({ schema: ${jsonToTsSource(this.schemaName)}, table: ${jsonToTsSource(this.tableName)}, policy: ${jsonToTsSource(this.policyName)} })`;
1630
+ }
1631
+
1632
+ override importRequirements(): readonly ImportRequirement[] {
1633
+ return [];
1627
1634
  }
1628
1635
  }
1629
1636
 
@@ -1652,7 +1659,11 @@ export class EnableRowLevelSecurityCall extends PostgresOpFactoryCallNode {
1652
1659
  }
1653
1660
 
1654
1661
  renderTypeScript(): string {
1655
- return `enableRowLevelSecurity(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)})`;
1662
+ return `this.enableRowLevelSecurity({ schema: ${jsonToTsSource(this.schemaName)}, table: ${jsonToTsSource(this.tableName)} })`;
1663
+ }
1664
+
1665
+ override importRequirements(): readonly ImportRequirement[] {
1666
+ return [];
1656
1667
  }
1657
1668
  }
1658
1669
 
@@ -1681,7 +1692,11 @@ export class DisableRowLevelSecurityCall extends PostgresOpFactoryCallNode {
1681
1692
  }
1682
1693
 
1683
1694
  renderTypeScript(): string {
1684
- return `disableRowLevelSecurity(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)})`;
1695
+ return `this.disableRowLevelSecurity({ schema: ${jsonToTsSource(this.schemaName)}, table: ${jsonToTsSource(this.tableName)} })`;
1696
+ }
1697
+
1698
+ override importRequirements(): readonly ImportRequirement[] {
1699
+ return [];
1685
1700
  }
1686
1701
  }
1687
1702
 
@@ -1724,7 +1739,11 @@ export class RenamePostgresRlsPolicyCall extends PostgresOpFactoryCallNode {
1724
1739
  }
1725
1740
 
1726
1741
  renderTypeScript(): string {
1727
- return `renameRlsPolicy(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.oldPolicyName)}, ${jsonToTsSource(this.newPolicyName)})`;
1742
+ return `this.renameRlsPolicy({ schema: ${jsonToTsSource(this.schemaName)}, table: ${jsonToTsSource(this.tableName)}, from: ${jsonToTsSource(this.oldPolicyName)}, to: ${jsonToTsSource(this.newPolicyName)} })`;
1743
+ }
1744
+
1745
+ override importRequirements(): readonly ImportRequirement[] {
1746
+ return [];
1728
1747
  }
1729
1748
  }
1730
1749
 
@@ -5,7 +5,7 @@
5
5
  * supplies the control adapter from the migration's injected stack:
6
6
  *
7
7
  * ```ts
8
- * import endContract from './end-contract.json' with { type: 'json' };
8
+ * import endContract from '../../snapshots/93f07d1b…c9e1e5a2/contract.json' with { type: 'json' };
9
9
  *
10
10
  * class M extends Migration {
11
11
  * override get operations() {
@@ -21,7 +21,7 @@
21
21
  *
22
22
  * The factory accepts lazy closures (`() => SqlQueryPlan | Buildable`),
23
23
  * invokes each one, asserts that its `meta.storageHash` matches the
24
- * `contract` it was handed (→ `PN-MIG-2005` on mismatch), and lowers the
24
+ * `contract` it was handed (→ `MIGRATION.DATA_TRANSFORM_CONTRACT_MISMATCH` on mismatch), and lowers the
25
25
  * plan via the supplied control adapter to a serialized `{sql, params}`
26
26
  * payload.
27
27
  *
@@ -15,7 +15,7 @@
15
15
  * Placeholder-bearing plans: `renderTypeScript()` always succeeds and embeds
16
16
  * `() => placeholder("slot")` at each stub. `operations`, in contrast, is
17
17
  * _not safe to enumerate_ on a stub-bearing plan — `DataTransformCall.toOp()`
18
- * throws `PN-MIG-2001` because a planner-stubbed closure cannot be lowered
18
+ * throws `MIGRATION.UNFILLED_PLACEHOLDER` because a planner-stubbed closure cannot be lowered
19
19
  * to a runtime op. Callers that know a plan may carry stubs must render to
20
20
  * `migration.ts`, let the user fill the slots, and re-load the edited
21
21
  * migration before enumerating ops. The walk-schema planner does not emit
@@ -42,6 +42,7 @@ export class TypeScriptRenderablePostgresMigration
42
42
  readonly #calls: readonly OpFactoryCall[];
43
43
  readonly #meta: MigrationMeta;
44
44
  readonly #spaceId: string;
45
+ readonly #snapshotsImportPath: string;
45
46
  readonly #lowerer: ExecuteRequestLowerer | undefined;
46
47
  #operationsCache:
47
48
  | readonly (
@@ -54,12 +55,14 @@ export class TypeScriptRenderablePostgresMigration
54
55
  calls: readonly OpFactoryCall[],
55
56
  meta: MigrationMeta,
56
57
  spaceId: string,
58
+ snapshotsImportPath: string,
57
59
  lowerer?: ExecuteRequestLowerer,
58
60
  ) {
59
61
  super();
60
62
  this.#calls = calls;
61
63
  this.#meta = meta;
62
64
  this.#spaceId = spaceId;
65
+ this.#snapshotsImportPath = snapshotsImportPath;
63
66
  this.#lowerer = lowerer;
64
67
  }
65
68
 
@@ -85,6 +88,10 @@ export class TypeScriptRenderablePostgresMigration
85
88
  }
86
89
 
87
90
  renderTypeScript(): string {
88
- return renderCallsToTypeScript(this.#calls, { from: this.#meta.from, to: this.#meta.to });
91
+ return renderCallsToTypeScript(this.#calls, {
92
+ from: this.#meta.from,
93
+ to: this.#meta.to,
94
+ snapshotsImportPath: this.#snapshotsImportPath,
95
+ });
89
96
  }
90
97
  }
@@ -35,6 +35,7 @@ import type {
35
35
  import { resolveValueSetValues } from '@prisma-next/family-sql/control';
36
36
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
37
37
  import type { SchemaDiffIssue } from '@prisma-next/framework-components/control';
38
+ import { issueOutcome } from '@prisma-next/framework-components/control';
38
39
  import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
39
40
  import {
40
41
  type SqlStorage,
@@ -198,7 +199,7 @@ export const notNullBackfillCallStrategy: CallMigrationStrategy = (issues, ctx)
198
199
  const calls: PostgresOpFactoryCall[] = [];
199
200
 
200
201
  for (const issue of issues) {
201
- if (issue.reason !== 'not-found') continue;
202
+ if (issueOutcome(issue) !== 'not-found') continue;
202
203
  const node = issueNode(issue);
203
204
  if (node === undefined || node.nodeKind !== RelationalSchemaNodeKind.column) continue;
204
205
  const expected = blindCast<
@@ -263,7 +264,7 @@ export const typeChangeCallStrategy: CallMigrationStrategy = (issues, ctx) => {
263
264
  const calls: PostgresOpFactoryCall[] = [];
264
265
 
265
266
  for (const issue of issues) {
266
- if (issue.reason !== 'not-equal') continue;
267
+ if (issueOutcome(issue) !== 'not-equal') continue;
267
268
  const pair = columnNodePair(issue);
268
269
  if (pair === undefined) continue;
269
270
  const { expected, actual } = pair;
@@ -357,7 +358,7 @@ export const nullableTighteningCallStrategy: CallMigrationStrategy = (issues, ct
357
358
  const calls: PostgresOpFactoryCall[] = [];
358
359
 
359
360
  for (const issue of issues) {
360
- if (issue.reason !== 'not-equal') continue;
361
+ if (issueOutcome(issue) !== 'not-equal') continue;
361
362
  const pair = columnNodePair(issue);
362
363
  if (pair === undefined) continue;
363
364
  const { expected, actual } = pair;
@@ -590,7 +591,7 @@ export const notNullAddColumnCallStrategy: CallMigrationStrategy = (issues, ctx)
590
591
  const mutableStorageTypes = ctx.storageTypes as Record<string, StorageTypeInstance>;
591
592
 
592
593
  for (const issue of issues) {
593
- if (issue.reason !== 'not-found') continue;
594
+ if (issueOutcome(issue) !== 'not-found') continue;
594
595
  const node = issueNode(issue);
595
596
  if (node === undefined || node.nodeKind !== RelationalSchemaNodeKind.column) continue;
596
597
  const expected = blindCast<
@@ -685,8 +686,7 @@ function canUseSharedTemporaryDefaultStrategy(options: {
685
686
  }
686
687
 
687
688
  for (const foreignKey of table.foreignKeys) {
688
- if (foreignKey.constraint === false || !foreignKey.source.columns.includes(columnName))
689
- continue;
689
+ if (!foreignKey.source.columns.includes(columnName)) continue;
690
690
  if (!schemaLookup || !hasForeignKey(schemaLookup, foreignKey)) return false;
691
691
  }
692
692
 
@@ -21,6 +21,7 @@ import type {
21
21
  SchemaDiffIssue,
22
22
  SchemaOwnership,
23
23
  } from '@prisma-next/framework-components/control';
24
+ import { issueOutcome } from '@prisma-next/framework-components/control';
24
25
  import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
25
26
  import type { SqlStorage } from '@prisma-next/sql-contract/types';
26
27
  import type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';
@@ -140,6 +141,12 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
140
141
  * {@link SqlMigrationPlannerPlanOptions.ownership}.
141
142
  */
142
143
  readonly ownership?: SchemaOwnership;
144
+ /**
145
+ * POSIX-relative path from the migration package dir to
146
+ * `migrations/snapshots` — see
147
+ * {@link SqlMigrationPlannerPlanOptions.snapshotsImportPath}.
148
+ */
149
+ readonly snapshotsImportPath: string;
143
150
  }): PostgresPlanResult {
144
151
  return this.planSql(options as SqlMigrationPlannerPlanOptions);
145
152
  }
@@ -155,6 +162,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
155
162
  to: context.toHash,
156
163
  },
157
164
  spaceId,
165
+ context.snapshotsImportPath,
158
166
  this.#lowerer,
159
167
  );
160
168
  }
@@ -211,7 +219,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
211
219
  options.policy.allowedOperationClasses.includes('destructive');
212
220
  const coalesced = coalesceSubtreeIssues(relationalDiffIssues);
213
221
  const owned = retainUnownedExtras(coalesced, options.ownership, options.contract);
214
- const gated = strict ? owned : owned.filter((issue) => issue.reason !== 'not-expected');
222
+ const gated = strict ? owned : owned.filter((issue) => issueOutcome(issue) !== 'not-expected');
215
223
 
216
224
  // Namespace presence (`CREATE SCHEMA`) is a planner-only op-generation
217
225
  // concern stitched in here rather than inside the shared diff — verify
@@ -322,6 +330,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
322
330
  to: options.contract.storage.storageHash,
323
331
  },
324
332
  options.spaceId,
333
+ options.snapshotsImportPath,
325
334
  this.#lowerer,
326
335
  ),
327
336
  ...(warnings.length > 0 ? { warnings: Object.freeze(warnings) } : {}),
@@ -369,7 +378,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
369
378
  // 'not-equal' is unreachable for content-addressed policies: the wire name
370
379
  // encodes the body hash, so two policies sharing a local key (same name)
371
380
  // are always equal and isEqualTo never returns false.
372
- if (issue.reason === 'not-found') {
381
+ if (issueOutcome(issue) === 'not-found') {
373
382
  const expected = issue.expected;
374
383
  PostgresPolicySchemaNode.assert(expected);
375
384
  // expected.namespaceId is the DDL schema name (resolved during projection);
@@ -381,7 +390,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
381
390
  expected.namespaceId,
382
391
  ),
383
392
  });
384
- } else if (issue.reason === 'not-expected') {
393
+ } else if (issueOutcome(issue) === 'not-expected') {
385
394
  const actual = issue.actual;
386
395
  PostgresPolicySchemaNode.assert(actual);
387
396
  extra.push({
@@ -505,7 +514,7 @@ function retainUnownedExtras(
505
514
  ): readonly SchemaDiffIssue<SqlSchemaDiffNode>[] {
506
515
  if (ownership === undefined) return issues;
507
516
  return issues.filter((issue) => {
508
- if (issue.reason !== 'not-expected') return true;
517
+ if (issueOutcome(issue) !== 'not-expected') return true;
509
518
  const node = issueNode(issue);
510
519
  if (node === undefined) return true;
511
520
  const coordinate = postgresNodeStorageCoordinate(node);