@prisma-next/cli 0.14.0-dev.5 → 0.14.0-dev.51

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 (112) hide show
  1. package/dist/cli.mjs +14 -14
  2. package/dist/cli.mjs.map +1 -1
  3. package/dist/{client-EnAjSmZg.mjs → client-DL6KE_c0.mjs} +88 -99
  4. package/dist/client-DL6KE_c0.mjs.map +1 -0
  5. package/dist/{command-helpers-6cNJZ863.mjs → command-helpers-DMG0RsR1.mjs} +3 -3
  6. package/dist/command-helpers-DMG0RsR1.mjs.map +1 -0
  7. package/dist/commands/contract-emit.mjs +1 -1
  8. package/dist/commands/contract-infer.mjs +1 -1
  9. package/dist/commands/db-init.mjs +3 -3
  10. package/dist/commands/db-schema.mjs +3 -3
  11. package/dist/commands/db-sign.mjs +4 -4
  12. package/dist/commands/db-update.mjs +4 -4
  13. package/dist/commands/db-verify.mjs +1 -1
  14. package/dist/commands/migrate.d.mts +1 -1
  15. package/dist/commands/migrate.d.mts.map +1 -1
  16. package/dist/commands/migrate.mjs +35 -35
  17. package/dist/commands/migrate.mjs.map +1 -1
  18. package/dist/commands/migration-check.d.mts +1 -1
  19. package/dist/commands/migration-check.mjs +1 -1
  20. package/dist/commands/migration-graph.d.mts +1 -1
  21. package/dist/commands/migration-graph.mjs +7 -7
  22. package/dist/commands/migration-graph.mjs.map +1 -1
  23. package/dist/commands/migration-list.d.mts +6 -6
  24. package/dist/commands/migration-list.d.mts.map +1 -1
  25. package/dist/commands/migration-list.mjs +1 -1
  26. package/dist/commands/migration-log.d.mts +1 -1
  27. package/dist/commands/migration-log.mjs +1 -1
  28. package/dist/commands/migration-new.mjs +3 -3
  29. package/dist/commands/migration-plan.d.mts.map +1 -1
  30. package/dist/commands/migration-plan.mjs +1 -1
  31. package/dist/commands/migration-show.mjs +3 -3
  32. package/dist/commands/migration-status.d.mts +1 -1
  33. package/dist/commands/migration-status.mjs +1 -1
  34. package/dist/commands/ref.d.mts +1 -1
  35. package/dist/commands/ref.mjs +2 -2
  36. package/dist/commands/telemetry/index.mjs +1 -1
  37. package/dist/{contract-at-errors-DdxWRjMc.mjs → contract-at-errors-bF4CGDl0.mjs} +2 -2
  38. package/dist/{contract-at-errors-DdxWRjMc.mjs.map → contract-at-errors-bF4CGDl0.mjs.map} +1 -1
  39. package/dist/{contract-emit-fz6pDR2n.mjs → contract-emit-BM7oxYIG.mjs} +7 -5
  40. package/dist/{contract-emit-fz6pDR2n.mjs.map → contract-emit-BM7oxYIG.mjs.map} +1 -1
  41. package/dist/{contract-emit-D4xDDFkO.mjs → contract-emit-DnCG2JaQ.mjs} +3 -3
  42. package/dist/{contract-emit-D4xDDFkO.mjs.map → contract-emit-DnCG2JaQ.mjs.map} +1 -1
  43. package/dist/{contract-infer-Ars4yjBv.mjs → contract-infer-Dq9XNQCx.mjs} +3 -3
  44. package/dist/{contract-infer-Ars4yjBv.mjs.map → contract-infer-Dq9XNQCx.mjs.map} +1 -1
  45. package/dist/{contract-space-aggregate-loader-Bu2fjSWV.mjs → contract-space-aggregate-loader-DAa8u6bi.mjs} +2 -2
  46. package/dist/{contract-space-aggregate-loader-Bu2fjSWV.mjs.map → contract-space-aggregate-loader-DAa8u6bi.mjs.map} +1 -1
  47. package/dist/{db-verify-CZxRV_er.mjs → db-verify-BpfJvC8O.mjs} +56 -60
  48. package/dist/db-verify-BpfJvC8O.mjs.map +1 -0
  49. package/dist/exports/control-api.d.mts +3 -3
  50. package/dist/exports/control-api.mjs +2 -2
  51. package/dist/exports/index.mjs +2 -2
  52. package/dist/extension-pack-inputs-1ySHqxKG.mjs.map +1 -1
  53. package/dist/{format-C3XdaDuA.mjs → format-DrUSkdqR.mjs} +2 -2
  54. package/dist/{format-C3XdaDuA.mjs.map → format-DrUSkdqR.mjs.map} +1 -1
  55. package/dist/{framework-components-5RljWT7Q.mjs → framework-components-BdmVlRSS.mjs} +2 -2
  56. package/dist/{framework-components-5RljWT7Q.mjs.map → framework-components-BdmVlRSS.mjs.map} +1 -1
  57. package/dist/{init-C79Yb97u.mjs → init-C0YyVoGj.mjs} +3 -3
  58. package/dist/{init-C79Yb97u.mjs.map → init-C0YyVoGj.mjs.map} +1 -1
  59. package/dist/{inspect-live-schema-DR8Eyt29.mjs → inspect-live-schema-Bs_0M6z8.mjs} +3 -3
  60. package/dist/{inspect-live-schema-DR8Eyt29.mjs.map → inspect-live-schema-Bs_0M6z8.mjs.map} +1 -1
  61. package/dist/{migration-check-CTcJfCC4.mjs → migration-check-DYxrNfK-.mjs} +9 -9
  62. package/dist/migration-check-DYxrNfK-.mjs.map +1 -0
  63. package/dist/{migration-command-scaffold-6OT_Q-3Y.mjs → migration-command-scaffold-DB5DQLvF.mjs} +3 -3
  64. package/dist/{migration-command-scaffold-6OT_Q-3Y.mjs.map → migration-command-scaffold-DB5DQLvF.mjs.map} +1 -1
  65. package/dist/{migration-list-DVHVmMrf.mjs → migration-list-BX5w9HAn.mjs} +14 -14
  66. package/dist/migration-list-BX5w9HAn.mjs.map +1 -0
  67. package/dist/{migration-log-CQpc9JoL.mjs → migration-log-CB1KzVOG.mjs} +3 -3
  68. package/dist/{migration-log-CQpc9JoL.mjs.map → migration-log-CB1KzVOG.mjs.map} +1 -1
  69. package/dist/{migration-path-target-zK6RG6JY.mjs → migration-path-target-CEFRK2Q7.mjs} +2 -2
  70. package/dist/{migration-path-target-zK6RG6JY.mjs.map → migration-path-target-CEFRK2Q7.mjs.map} +1 -1
  71. package/dist/{migration-plan-o2aqjnfd.mjs → migration-plan-DMKl_HnV.mjs} +29 -28
  72. package/dist/migration-plan-DMKl_HnV.mjs.map +1 -0
  73. package/dist/{migration-status-2aXGtYMW.mjs → migration-status-BeNAmEju.mjs} +12 -12
  74. package/dist/migration-status-BeNAmEju.mjs.map +1 -0
  75. package/dist/{telemetry-BckOSUWG.mjs → telemetry-_19qtfXp.mjs} +2 -2
  76. package/dist/{telemetry-BckOSUWG.mjs.map → telemetry-_19qtfXp.mjs.map} +1 -1
  77. package/dist/{types-qETCCNbe.d.mts → types-DFoqNIcQ.d.mts} +26 -20
  78. package/dist/types-DFoqNIcQ.d.mts.map +1 -0
  79. package/dist/{verify-DLqhL2LU.mjs → verify-Djo-Jep_.mjs} +57 -159
  80. package/dist/verify-Djo-Jep_.mjs.map +1 -0
  81. package/package.json +21 -21
  82. package/src/commands/db-verify.ts +31 -19
  83. package/src/commands/lsp.ts +3 -1
  84. package/src/commands/migrate.ts +46 -43
  85. package/src/commands/migration-check.ts +5 -5
  86. package/src/commands/migration-graph.ts +4 -4
  87. package/src/commands/migration-list.ts +12 -12
  88. package/src/commands/migration-plan.ts +14 -3
  89. package/src/commands/migration-status.ts +9 -9
  90. package/src/control-api/client.ts +5 -3
  91. package/src/control-api/operations/contract-emit.ts +7 -6
  92. package/src/control-api/operations/db-init.ts +2 -2
  93. package/src/control-api/operations/db-run.ts +18 -17
  94. package/src/control-api/operations/db-verify.ts +52 -44
  95. package/src/control-api/operations/migrate.ts +72 -72
  96. package/src/control-api/operations/run-migration.ts +6 -5
  97. package/src/control-api/types.ts +8 -8
  98. package/src/utils/combine-verify-results.ts +94 -0
  99. package/src/utils/extension-pack-inputs.ts +1 -1
  100. package/src/utils/formatters/errors.ts +12 -4
  101. package/src/utils/formatters/verify.ts +88 -227
  102. package/src/utils/plan-resolution.ts +18 -18
  103. package/dist/client-EnAjSmZg.mjs.map +0 -1
  104. package/dist/command-helpers-6cNJZ863.mjs.map +0 -1
  105. package/dist/db-verify-CZxRV_er.mjs.map +0 -1
  106. package/dist/migration-check-CTcJfCC4.mjs.map +0 -1
  107. package/dist/migration-list-DVHVmMrf.mjs.map +0 -1
  108. package/dist/migration-plan-o2aqjnfd.mjs.map +0 -1
  109. package/dist/migration-status-2aXGtYMW.mjs.map +0 -1
  110. package/dist/types-qETCCNbe.d.mts.map +0 -1
  111. package/dist/verify-DLqhL2LU.mjs.map +0 -1
  112. package/src/utils/combine-schema-results.ts +0 -84
@@ -20,7 +20,7 @@ import { executeRun } from './db-run';
20
20
  * {@link import('@prisma-next/migration-tools/aggregate').loadContractSpaceAggregate}
21
21
  * from the supplied descriptor set + on-disk on-disk artefacts.
22
22
  * 2. The aggregate planner runs with `callerPolicy.ignoreGraphFor`
23
- * locked to the app member — synth strategy for the app, graph-walk
23
+ * locked to the app space — synth strategy for the app, graph-walk
24
24
  * for every extension.
25
25
  * 3. The runner's `execute` applies the per-space plans
26
26
  * inside one outer transaction.
@@ -53,7 +53,7 @@ export interface ExecuteDbInitOptions<TFamilyId extends string, TTargetId extend
53
53
  /**
54
54
  * Declared extension descriptors. Defaults to an empty list, which
55
55
  * routes through the same loader → planner → runner pipeline with no
56
- * extension members in the aggregate.
56
+ * extension spaces in the aggregate.
57
57
  */
58
58
  readonly extensionPacks?: ReadonlyArray<ControlExtensionDescriptor<TFamilyId, TTargetId>>;
59
59
  /** Optional progress callback for observing operation progress */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Backs `db init` / `db update`. Strategy: introspect → planMigration; synth-for-app + graph-walk-extensions; plan-mode + orphan-marker preflight.
2
+ * Backs `db init` / `db update`. Strategy: introspect → planMigration; planFromDiff-for-app + resolveRecordedPath-extensions; plan-mode + orphan-marker preflight.
3
3
  */
4
4
 
5
5
  import type { Contract } from '@prisma-next/contract/types';
@@ -97,10 +97,11 @@ export interface ExecuteRunOptions<TFamilyId extends string, TTargetId extends s
97
97
  * integrity violation short-circuits with a structured error.
98
98
  * 2. **Read DB state**: marker rows (`familyInstance.readAllMarkers`)
99
99
  * + introspected schema (`familyInstance.introspect`).
100
- * 3. **Plan**: {@link planMigration} chooses graph-walk vs synth per
101
- * member according to `callerPolicy.ignoreGraphFor`. The app member
102
- * is forced through synth (today's daily-driver behaviour); every
103
- * extension member walks its on-disk graph.
100
+ * 3. **Plan**: {@link planMigration} chooses `resolveRecordedPath` vs
101
+ * `planFromDiff` per space according to `callerPolicy.ignoreGraphFor`.
102
+ * The app space is forced through `planFromDiff` (today's daily-driver
103
+ * behaviour); every extension space walks its on-disk graph via
104
+ * `resolveRecordedPath`.
104
105
  * 4. **Apply** (when `mode === 'apply'`): every per-space `MigrationPlan`
105
106
  * feeds into the runner's `execute` — one outer
106
107
  * transaction across every space; failure on any space rolls back
@@ -168,9 +169,9 @@ export async function executeRun<TFamilyId extends string, TTargetId extends str
168
169
  });
169
170
  onProgress?.({ action, kind: 'spanEnd', spanId: SPAN_IDS.introspect, outcome: 'ok' });
170
171
 
171
- // 3. Plan via aggregate planner. App is forced through synth (today's
172
- // `db init` / `db update` daily-driver behaviour); extensions walk
173
- // their on-disk migration graphs.
172
+ // 3. Plan via aggregate planner. App is forced through planFromDiff
173
+ // (today's `db init` / `db update` daily-driver behaviour); extensions
174
+ // walk their on-disk migration graphs via resolveRecordedPath.
174
175
  onProgress?.({
175
176
  action,
176
177
  kind: 'spanStart',
@@ -200,7 +201,7 @@ export async function executeRun<TFamilyId extends string, TTargetId extends str
200
201
  const appResolution = orderedResolutions.find((r) => r.spaceId === aggregate.app.spaceId);
201
202
  if (!appResolution) {
202
203
  throw new Error(
203
- 'Aggregate planner returned no plan for the app member — the planner is supposed to always emit one.',
204
+ 'Aggregate planner returned no plan for the app space — the planner is supposed to always emit one.',
204
205
  );
205
206
  }
206
207
  const appPlan = appResolution.entry.plan;
@@ -228,9 +229,9 @@ export async function executeRun<TFamilyId extends string, TTargetId extends str
228
229
  // 5. Run mode: hand off to the shared `runMigration` primitive.
229
230
  // The runner-driving tail is identical for `db init` / `db update` /
230
231
  // `migrate` — only how each caller produces `perSpacePlans`
231
- // differs (synth + graph-walk via planMigration here; graph-walk
232
- // only for migrate). Each caller produces perSpacePlans differently;
233
- // this helper handles the shared run tail.
232
+ // differs (planFromDiff + resolveRecordedPath via planMigration here;
233
+ // resolveRecordedPath only for migrate). Each caller produces
234
+ // perSpacePlans differently; this helper handles the shared run tail.
234
235
  const applied = await runMigration({
235
236
  aggregate,
236
237
  perSpacePlans: planResult.value.perSpace,
@@ -280,14 +281,14 @@ function aggregatePlannerWarnings(
280
281
 
281
282
  /**
282
283
  * Compare the live `_prisma_marker` rows against the aggregate's
283
- * declared members. Any marker row whose `space` is not a member of
284
+ * declared contract spaces. Any marker row whose `space` is not a space of
284
285
  * the aggregate is an "orphan" — typically a marker left behind by
285
286
  * an extension that was removed from `extensionPacks` without first
286
287
  * cleaning up its on-disk migrations / database tables.
287
288
  *
288
289
  * Returns a {@link CliStructuredError} envelope (code `5002`,
289
290
  * `kind: 'orphanMarker'`) for the first orphan it finds, or `null`
290
- * when every marker row maps to a declared member. Mirrors the M2
291
+ * when every marker row maps to a declared contract space. Mirrors the M2
291
292
  * `runContractSpaceVerifierMarkerCheck` envelope so downstream
292
293
  * tooling (integration tests, JSON consumers) keeps asserting on the
293
294
  * same shape.
@@ -296,13 +297,13 @@ function detectOrphanMarkers(
296
297
  aggregate: ContractSpaceAggregate,
297
298
  markerRows: ReadonlyMap<string, unknown>,
298
299
  ): CliStructuredError | null {
299
- const memberSpaceIds = new Set<string>([
300
+ const aggregateSpaceIds = new Set<string>([
300
301
  aggregate.app.spaceId,
301
302
  ...aggregate.extensions.map((m) => m.spaceId),
302
303
  ]);
303
304
  const orphans: string[] = [];
304
305
  for (const [spaceId, row] of markerRows) {
305
- if (row !== null && row !== undefined && !memberSpaceIds.has(spaceId)) {
306
+ if (row !== null && row !== undefined && !aggregateSpaceIds.has(spaceId)) {
306
307
  orphans.push(spaceId);
307
308
  }
308
309
  }
@@ -328,7 +329,7 @@ function detectOrphanMarkers(
328
329
  }
329
330
 
330
331
  function mapPlannerError(error: PlannerError): DbInitResult | DbUpdateResult {
331
- if (error.kind === 'appSynthFailure') {
332
+ if (error.kind === 'planFromDiffFailed') {
332
333
  const failure: DbInitFailure | DbUpdateFailure = {
333
334
  code: 'PLANNING_FAILED',
334
335
  summary: 'Migration planning failed due to conflicts',
@@ -4,16 +4,19 @@ import type {
4
4
  ControlDriverInstance,
5
5
  ControlExtensionDescriptor,
6
6
  ControlFamilyInstance,
7
+ SchemaDiffIssue,
7
8
  VerifyDatabaseSchemaResult,
8
9
  } from '@prisma-next/framework-components/control';
10
+ import { hasSchemaSubjectClassifier } from '@prisma-next/framework-components/control';
9
11
  import {
10
- type ContractSpaceMember,
12
+ type AggregateContractSpace,
11
13
  collectAggregateNamespaces,
12
14
  requireHeadRef,
13
15
  type VerifierOutput,
14
16
  verifyMigration,
15
17
  } from '@prisma-next/migration-tools/aggregate';
16
- import { blindCast, castAs } from '@prisma-next/utils/casts';
18
+ import { castAs } from '@prisma-next/utils/casts';
19
+ import { ifDefined } from '@prisma-next/utils/defined';
17
20
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
18
21
  import { CliStructuredError } from '../../utils/cli-errors';
19
22
  import {
@@ -37,9 +40,9 @@ const SPAN_IDS = {
37
40
  *
38
41
  * Loader → verifier pipeline. The loader (sole descriptor-import
39
42
  * boundary) builds a {@link import('@prisma-next/migration-tools/aggregate').ContractSpaceAggregate};
40
- * the aggregate verifier bundles `markerCheck` + per-space pre-projected
41
- * `schemaCheck`. `mode: 'strict' | 'lenient'` maps directly to the user
42
- * facing `--strict` flag.
43
+ * the aggregate verifier bundles `markerCheck` + per-space `schemaCheck`
44
+ * (each contract space verified against the full schema; extras stripped to its own view).
45
+ * `mode: 'strict' | 'lenient'` maps directly to the user facing `--strict` flag.
43
46
  */
44
47
  export interface ExecuteDbVerifyOptions<TFamilyId extends string, TTargetId extends string> {
45
48
  readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;
@@ -63,13 +66,19 @@ export interface ExecuteDbVerifyOptions<TFamilyId extends string, TTargetId exte
63
66
  * emitted, so downstream tooling and integration tests assert on the
64
67
  * same shape).
65
68
  *
66
- * On success, the per-space schema results are returned for the CLI to
69
+ * On success, the per-space verify results are returned for the CLI to
67
70
  * render. When `skipSchema` is true (`--marker-only`), the schema map
68
71
  * is empty.
69
72
  */
70
73
  export interface ExecuteDbVerifySuccess {
71
74
  readonly schemaResults: ReadonlyMap<string, VerifyDatabaseSchemaResult>;
72
- readonly memberOrder: readonly string[];
75
+ /**
76
+ * Live element names no contract space declares, deduplicated and reported
77
+ * once for the whole database (never per space). Strict mode fails on a
78
+ * non-empty list; lenient mode surfaces it informationally.
79
+ */
80
+ readonly unclaimed: readonly string[];
81
+ readonly spaceOrder: readonly string[];
73
82
  readonly appSpaceId: string;
74
83
  }
75
84
 
@@ -85,11 +94,11 @@ export type ExecuteDbVerifyResult = Result<ExecuteDbVerifySuccess, CliStructured
85
94
  * structured CLI error.
86
95
  * 2. **Read DB state**: marker rows + (when `skipSchema` is `false`)
87
96
  * schema introspection.
88
- * 3. **Verify**: {@link verifyMigration} returns per-space
89
- * `markerCheck` + per-space pre-projected `schemaCheck` (closes F23).
90
- * Marker mismatches map to `CliStructuredError` (code `5002`) so
91
- * callers (CLI command) can render and exit. Schema results are
92
- * returned to the caller verbatim.
97
+ * 3. **Verify**: {@link verifyMigration} returns per-space `markerCheck` +
98
+ * per-space `schemaCheck` (each contract space verified against the full schema,
99
+ * then scoped to its own contract space). Marker mismatches map to
100
+ * `CliStructuredError` (code `5002`) so callers (CLI command) can render
101
+ * and exit. Verify results are returned to the caller verbatim.
93
102
  */
94
103
  export async function executeDbVerify<TFamilyId extends string, TTargetId extends string>(
95
104
  options: ExecuteDbVerifyOptions<TFamilyId, TTargetId>,
@@ -109,13 +118,26 @@ export async function executeDbVerify<TFamilyId extends string, TTargetId extend
109
118
  contract: collectAggregateNamespaces(aggregate),
110
119
  });
111
120
 
121
+ // The subject-granularity + entity-kind classifiers are an injected
122
+ // capability — not every family provides one — detected the same way the
123
+ // other optional per-family capabilities are (`hasSchemaView`, …), never
124
+ // assumed.
125
+ const classifySubjectGranularity = hasSchemaSubjectClassifier(familyInstance)
126
+ ? (issue: SchemaDiffIssue) => familyInstance.classifySubjectGranularity(issue)
127
+ : undefined;
128
+ const classifyEntityKind = hasSchemaSubjectClassifier(familyInstance)
129
+ ? (issue: SchemaDiffIssue) => familyInstance.classifyEntityKind(issue)
130
+ : undefined;
131
+
112
132
  emitVerifySpan(onProgress, 'spanStart');
113
133
  const verifyResult = verifyMigration({
114
134
  aggregate,
115
135
  markersBySpaceId,
116
136
  schemaIntrospection,
117
137
  mode: options.mode,
118
- verifySchemaForMember: createPerMemberVerifier(options),
138
+ verifySchemaForSpace: createPerSpaceVerifier(options),
139
+ ...ifDefined('classifySubjectGranularity', classifySubjectGranularity),
140
+ ...ifDefined('classifyEntityKind', classifyEntityKind),
119
141
  });
120
142
  return finaliseVerifyResult({ verifyResult, aggregate, skipMarker, onProgress });
121
143
  }
@@ -166,30 +188,27 @@ async function runIntrospection<TFamilyId extends string, TTargetId extends stri
166
188
  }
167
189
 
168
190
  /**
169
- * Build the per-member schema callback handed to the aggregate verifier.
191
+ * Build the per-space schema callback handed to the aggregate verifier.
170
192
  * When `skipSchema` is true the callback short-circuits with a synthetic
171
193
  * `ok` result so the verifier still runs the (cheap) schemaCheck loop
172
194
  * without invoking the family's verification path.
173
195
  */
174
- export function createPerMemberVerifier<TFamilyId extends string, TTargetId extends string>(
196
+ export function createPerSpaceVerifier<TFamilyId extends string, TTargetId extends string>(
175
197
  options: ExecuteDbVerifyOptions<TFamilyId, TTargetId>,
176
198
  ): (
177
- projectedSchema: unknown,
178
- member: ContractSpaceMember,
199
+ schema: unknown,
200
+ space: AggregateContractSpace,
179
201
  verifyMode: 'strict' | 'lenient',
180
202
  ) => VerifyDatabaseSchemaResult {
181
203
  const { skipSchema, familyInstance, frameworkComponents } = options;
182
- return (projectedSchema, member, verifyMode) => {
183
- if (skipSchema) return buildSkippedSchemaResult(member);
204
+ return (schema, space, verifyMode) => {
205
+ if (skipSchema) return buildSkippedSchemaResult(space);
184
206
  return familyInstance.verifySchema({
185
- contract: member.contract(),
186
- // The family's `TSchemaIR` is opaque to migration-tools; the
187
- // aggregate verifier passes through whatever we hand it. The
188
- // family expects its own IR shape on the way back.
189
- schema: blindCast<
190
- never,
191
- 'family TSchemaIR is opaque to migration-tools; projectedSchema is passed straight through'
192
- >(projectedSchema),
207
+ contract: space.contract(),
208
+ // `familyInstance` is `ControlFamilyInstance<_, unknown>`, so `verifySchema`
209
+ // takes its `TSchemaIR` as `unknown` the introspected schema passes
210
+ // straight through; the family narrows to its own IR node internally.
211
+ schema,
193
212
  strict: verifyMode === 'strict',
194
213
  frameworkComponents,
195
214
  });
@@ -223,7 +242,7 @@ function emitVerifySpan(
223
242
  * by the CLI's `--schema-only` mode.
224
243
  */
225
244
  function finaliseVerifyResult(args: {
226
- verifyResult: VerifierOutput<VerifyDatabaseSchemaResult>;
245
+ verifyResult: VerifierOutput;
227
246
  aggregate: {
228
247
  readonly app: { readonly spaceId: string };
229
248
  readonly extensions: ReadonlyArray<{ readonly spaceId: string }>;
@@ -253,14 +272,15 @@ function finaliseVerifyResult(args: {
253
272
  emitVerifySpan(onProgress, 'spanEndOk');
254
273
  return ok({
255
274
  schemaResults: verifyResult.value.schemaCheck.perSpace,
256
- memberOrder: [aggregate.app.spaceId, ...aggregate.extensions.map((e) => e.spaceId)],
275
+ unclaimed: verifyResult.value.schemaCheck.unclaimed,
276
+ spaceOrder: [aggregate.app.spaceId, ...aggregate.extensions.map((e) => e.spaceId)],
257
277
  appSpaceId: aggregate.app.spaceId,
258
278
  });
259
279
  }
260
280
 
261
- function buildSkippedSchemaResult(member: ContractSpaceMember): VerifyDatabaseSchemaResult {
262
- const contract = member.contract();
263
- const headRef = requireHeadRef(member);
281
+ function buildSkippedSchemaResult(space: AggregateContractSpace): VerifyDatabaseSchemaResult {
282
+ const contract = space.contract();
283
+ const headRef = requireHeadRef(space);
264
284
  const profileHash = castAs<{ profileHash?: string }>(contract).profileHash;
265
285
  return {
266
286
  ok: true,
@@ -272,18 +292,6 @@ function buildSkippedSchemaResult(member: ContractSpaceMember): VerifyDatabaseSc
272
292
  target: { expected: contract.target },
273
293
  schema: {
274
294
  issues: [],
275
- root: {
276
- status: 'pass',
277
- kind: 'skipped',
278
- name: member.spaceId,
279
- contractPath: '',
280
- code: 'SKIPPED',
281
- message: 'Schema verification skipped',
282
- expected: undefined,
283
- actual: undefined,
284
- children: [],
285
- },
286
- counts: { pass: 0, warn: 0, fail: 0, totalNodes: 0 },
287
295
  },
288
296
  timings: { total: 0 },
289
297
  };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Backs the `migrate` command. Strategy: graph-walk-all-members, replay-only (no introspect/synth/planner).
2
+ * Backs the `migrate` command. Resolves the recorded path for every space, replay-only (no introspect/diff/planner).
3
3
  */
4
4
 
5
5
  import type { Contract } from '@prisma-next/contract/types';
@@ -11,13 +11,13 @@ import type {
11
11
  TargetMigrationsCapability,
12
12
  } from '@prisma-next/framework-components/control';
13
13
  import {
14
- buildSynthMigrationEdge,
14
+ type AggregateContractSpace,
15
+ buildFabricatedMigrationEdge,
15
16
  type ContractMarkerRecordLike,
16
17
  type ContractSpaceAggregate,
17
- type ContractSpaceMember,
18
- graphWalkStrategy,
19
18
  type PerSpacePlan,
20
19
  requireHeadRef,
20
+ resolveRecordedPath,
21
21
  } from '@prisma-next/migration-tools/aggregate';
22
22
  import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
23
23
  import { errorNoInvariantPath } from '@prisma-next/migration-tools/errors';
@@ -62,8 +62,8 @@ export interface ExecuteMigrateOptions<TFamilyId extends string, TTargetId exten
62
62
  readonly extensionPacks: ReadonlyArray<ControlExtensionDescriptor<TFamilyId, TTargetId>>;
63
63
  readonly targetId: TTargetId;
64
64
  /**
65
- * Optional app-space ref override. When provided, the app member's
66
- * graph-walk targets this hash instead of `member.headRef.hash`.
65
+ * Optional app-space ref override. When provided, the app space's
66
+ * graph-walk targets this hash instead of `space.headRef.hash`.
67
67
  * Extensions are unaffected — they always walk to their own head.
68
68
  */
69
69
  readonly refHash?: string;
@@ -72,7 +72,7 @@ export interface ExecuteMigrateOptions<TFamilyId extends string, TTargetId exten
72
72
  * Threaded into the graph-walk's `required` calculation so the
73
73
  * planner picks an invariant-bearing path and surfaces the
74
74
  * required/satisfied set on the success envelope. When `refHash`
75
- * is absent the file's `member.headRef.invariants` are used.
75
+ * is absent the file's `space.headRef.invariants` are used.
76
76
  */
77
77
  readonly refInvariants?: readonly string[];
78
78
  /**
@@ -89,16 +89,16 @@ export interface ExecuteMigrateOptions<TFamilyId extends string, TTargetId exten
89
89
  /**
90
90
  * Apply pending migrations across every contract space (app +
91
91
  * extensions). Replay-only: graph-walk against the on-disk graph for
92
- * every member; no synth, no introspection.
92
+ * every contract space; no synth, no introspection.
93
93
  *
94
94
  * Pipeline:
95
95
  *
96
96
  * 1. Load aggregate from disk (loader hydrates extension graphs;
97
97
  * caller provides app-space packages).
98
98
  * 2. Read live marker rows per space (`familyInstance.readAllMarkers`).
99
- * 3. Per member: `graphWalkStrategy` plots the path from the live
100
- * marker to `member.headRef.hash` (or `refHash` for the app
101
- * member when provided). Empty-graph members fail loudly — a
99
+ * 3. Per space: `resolveRecordedPath` plots the path from the live
100
+ * marker to `space.headRef.hash` (or `refHash` for the app
101
+ * space when provided). Empty-graph spaces fail loudly — a
102
102
  * "never planned" space is a user-error condition for replay.
103
103
  * 4. Hand off to {@link runMigration} (the runner-driving tail
104
104
  * shared with `db init` / `db update`). Marker advancement is
@@ -140,41 +140,41 @@ export async function executeMigrate<TFamilyId extends string, TTargetId extends
140
140
 
141
141
  const markerRows = await familyInstance.readAllMarkers({ driver });
142
142
 
143
- // Plan every member via graph-walk. App member targets `refHash`
143
+ // Plan every space via graph-walk. App space targets `refHash`
144
144
  // when provided, otherwise its own head; extensions always walk
145
145
  // to their own head ref.
146
- const allMembers: ReadonlyArray<ContractSpaceMember> = [aggregate.app, ...aggregate.extensions];
146
+ const allSpaces: ReadonlyArray<AggregateContractSpace> = [aggregate.app, ...aggregate.extensions];
147
147
  const perSpacePlans = new Map<string, PerSpacePlan>();
148
- // Already-at-head empty-graph members (typically extensions whose
148
+ // Already-at-head empty-graph spaces (typically extensions whose
149
149
  // head ref is the empty sentinel, or whose live marker already
150
150
  // matches the target). Kept out of the runner schedule so we don't
151
151
  // write spurious markers for greenfield extensions, but merged back
152
- // into the success envelope so every loaded member is represented.
152
+ // into the success envelope so every loaded space is represented.
153
153
  const atHeadResolutions = new Map<string, PerSpacePlan>();
154
- for (const member of allMembers) {
155
- const isAppMember = member.spaceId === aggregate.app.spaceId;
156
- // The aggregate passed the integrity gate, so every member's head ref
154
+ for (const space of allSpaces) {
155
+ const isAppSpace = space.spaceId === aggregate.app.spaceId;
156
+ // The aggregate passed the integrity gate, so every space's head ref
157
157
  // is resolved (the app's is synthesised from the live contract).
158
- const headRef = requireHeadRef(member);
159
- const memberTargetHash = isAppMember && refHash !== undefined ? refHash : headRef.hash;
160
- const memberRefInvariants = isAppMember && refHash !== undefined ? refInvariants : undefined;
161
- const liveMarker = markerRows.get(member.spaceId) ?? null;
158
+ const headRef = requireHeadRef(space);
159
+ const spaceTargetHash = isAppSpace && refHash !== undefined ? refHash : headRef.hash;
160
+ const spaceRefInvariants = isAppSpace && refHash !== undefined ? refInvariants : undefined;
161
+ const liveMarker = markerRows.get(space.spaceId) ?? null;
162
162
 
163
- const outcome = planMemberPath({
164
- member,
163
+ const outcome = planSpacePath({
164
+ space,
165
165
  aggregate,
166
- targetHash: memberTargetHash,
167
- refInvariants: memberRefInvariants,
166
+ targetHash: spaceTargetHash,
167
+ refInvariants: spaceRefInvariants,
168
168
  liveMarker,
169
- ...(isAppMember ? { refName } : {}),
169
+ ...(isAppSpace ? { refName } : {}),
170
170
  });
171
171
 
172
172
  if (outcome.kind === 'at-head') {
173
- // Empty-graph member whose live marker already matches the target.
173
+ // Empty-graph space whose live marker already matches the target.
174
174
  // Kept out of the runner schedule so we don't write spurious markers
175
175
  // for greenfield extensions, but merged back into the success envelope
176
- // so every loaded member is represented.
177
- atHeadResolutions.set(member.spaceId, outcome.plan);
176
+ // so every loaded space is represented.
177
+ atHeadResolutions.set(space.spaceId, outcome.plan);
178
178
  continue;
179
179
  }
180
180
  if (outcome.kind === 'never-planned') {
@@ -197,9 +197,9 @@ export async function executeMigrate<TFamilyId extends string, TTargetId extends
197
197
  // is never a graph node, producing an empty `structuralPath` and
198
198
  // a less actionable diagnostic.
199
199
  const structural = findPathWithDecision(
200
- outcome.targetMember.graph(),
200
+ outcome.targetSpace.graph(),
201
201
  outcome.liveHash,
202
- memberTargetHash,
202
+ spaceTargetHash,
203
203
  { required: new Set<string>() },
204
204
  );
205
205
  const structuralPath =
@@ -220,14 +220,14 @@ export async function executeMigrate<TFamilyId extends string, TTargetId extends
220
220
  });
221
221
  }
222
222
 
223
- perSpacePlans.set(member.spaceId, outcome.plan);
223
+ perSpacePlans.set(space.spaceId, outcome.plan);
224
224
  }
225
225
 
226
226
  const canonicalOrder = [...aggregate.extensions.map((m) => m.spaceId), aggregate.app.spaceId];
227
227
  const applyOrder = canonicalOrder.filter((spaceId) => perSpacePlans.has(spaceId));
228
228
 
229
229
  // Short-circuit: nothing pending across any space (no runner-bound
230
- // plans). Surfaces every loaded member — including at-head empty-
230
+ // plans). Surfaces every loaded space — including at-head empty-
231
231
  // graph extensions — in `perSpace[]` so the result reflects the
232
232
  // full aggregate, not just the spaces the runner would have touched.
233
233
  const totalPlannedOps = sumPlannedOps(applyOrder, perSpacePlans);
@@ -285,7 +285,7 @@ export async function executeMigrate<TFamilyId extends string, TTargetId extends
285
285
  }
286
286
 
287
287
  // Merge at-head zero-op resolutions back into the canonical order
288
- // so the success envelope surfaces every loaded member, not just
288
+ // so the success envelope surfaces every loaded space, not just
289
289
  // those the runner executed.
290
290
  const orderedAll = canonicalOrder
291
291
  .filter((spaceId) => perSpacePlans.has(spaceId) || atHeadResolutions.has(spaceId))
@@ -321,16 +321,16 @@ export async function executeMigrate<TFamilyId extends string, TTargetId extends
321
321
  }
322
322
 
323
323
  /**
324
- * Outcome variants for one member's path computation.
324
+ * Outcome variants for one space's path computation.
325
325
  *
326
326
  * Callers switch on `kind` and map to their own error representation:
327
327
  * `executeMigrate` throws / returns `notOk`; `executeMigrateShowCommand`
328
328
  * returns a CLI structured error. The shared discriminant guarantees both
329
- * paths feed `graphWalkStrategy` the same inputs.
329
+ * paths feed `resolveRecordedPath` the same inputs.
330
330
  *
331
331
  * @internal Exported for `executeMigrateShowCommand` to call.
332
332
  */
333
- export type MemberPathOutcome =
333
+ export type SpacePathOutcome =
334
334
  | { readonly kind: 'ok'; readonly plan: PerSpacePlan }
335
335
  | { readonly kind: 'at-head'; readonly plan: PerSpacePlan }
336
336
  | { readonly kind: 'never-planned'; readonly spaceId: string; readonly targetHash: string }
@@ -343,16 +343,16 @@ export type MemberPathOutcome =
343
343
  | {
344
344
  readonly kind: 'unsatisfiable';
345
345
  readonly spaceId: string;
346
- readonly isAppMember: boolean;
346
+ readonly isAppSpace: boolean;
347
347
  readonly missing: readonly string[];
348
348
  readonly targetInvariants: readonly string[];
349
- readonly targetMember: ContractSpaceMember;
349
+ readonly targetSpace: AggregateContractSpace;
350
350
  readonly liveHash: string;
351
351
  readonly refName: string | undefined;
352
352
  };
353
353
 
354
354
  /**
355
- * Compute the graph-walk path for one contract-space member.
355
+ * Compute the graph-walk path for one contract space.
356
356
  *
357
357
  * Encapsulates the invariant-correct input assembly that both
358
358
  * `executeMigrate` and `executeMigrateShowCommand` must use:
@@ -361,71 +361,71 @@ export type MemberPathOutcome =
361
361
  * - `targetInvariants` uses the caller-supplied `refInvariants` when a
362
362
  * `--to` ref was resolved (not always the file head ref's invariants).
363
363
  *
364
- * Both callers map the returned `MemberPathOutcome` to their own error
364
+ * Both callers map the returned `SpacePathOutcome` to their own error
365
365
  * representation; the path-compute logic is shared and identical.
366
366
  *
367
367
  * @internal Exported for `executeMigrateShowCommand`.
368
368
  */
369
- export function planMemberPath({
370
- member,
369
+ export function planSpacePath({
370
+ space,
371
371
  aggregate,
372
372
  targetHash,
373
373
  refInvariants,
374
374
  liveMarker,
375
375
  refName,
376
376
  }: {
377
- readonly member: ContractSpaceMember;
377
+ readonly space: AggregateContractSpace;
378
378
  readonly aggregate: Pick<ContractSpaceAggregate, 'targetId' | 'app'>;
379
379
  readonly targetHash: string;
380
380
  readonly refInvariants: readonly string[] | undefined;
381
381
  readonly liveMarker: ContractMarkerRecordLike | null;
382
382
  readonly refName?: string;
383
- }): MemberPathOutcome {
384
- const isAppMember = member.spaceId === aggregate.app.spaceId;
385
- const headRef = requireHeadRef(member);
383
+ }): SpacePathOutcome {
384
+ const isAppSpace = space.spaceId === aggregate.app.spaceId;
385
+ const headRef = requireHeadRef(space);
386
386
 
387
- if (member.graph().nodes.size === 0) {
387
+ if (space.graph().nodes.size === 0) {
388
388
  const liveHash = liveMarker?.storageHash;
389
389
  if (targetHash === liveHash || (liveHash === undefined && targetHash === EMPTY_CONTRACT_HASH)) {
390
390
  return {
391
391
  kind: 'at-head',
392
392
  plan: buildAtHeadResolution({
393
393
  aggregateTargetId: aggregate.targetId,
394
- member,
394
+ space,
395
395
  targetHash,
396
396
  liveMarker,
397
397
  }),
398
398
  };
399
399
  }
400
- return { kind: 'never-planned', spaceId: member.spaceId, targetHash };
400
+ return { kind: 'never-planned', spaceId: space.spaceId, targetHash };
401
401
  }
402
402
 
403
403
  const targetInvariants =
404
- isAppMember && refInvariants !== undefined ? refInvariants : headRef.invariants;
405
- const targetMember: ContractSpaceMember =
404
+ isAppSpace && refInvariants !== undefined ? refInvariants : headRef.invariants;
405
+ const targetSpace: AggregateContractSpace =
406
406
  targetHash === headRef.hash && targetInvariants === headRef.invariants
407
- ? member
408
- : { ...member, headRef: { hash: targetHash, invariants: targetInvariants } };
407
+ ? space
408
+ : { ...space, headRef: { hash: targetHash, invariants: targetInvariants } };
409
409
 
410
- const walked = graphWalkStrategy({
410
+ const walked = resolveRecordedPath({
411
411
  aggregateTargetId: aggregate.targetId,
412
- member: targetMember,
412
+ space: targetSpace,
413
413
  currentMarker: liveMarker,
414
- ...(isAppMember && refName !== undefined ? { refName } : {}),
414
+ ...(isAppSpace && refName !== undefined ? { refName } : {}),
415
415
  });
416
416
 
417
417
  if (walked.kind === 'unreachable') {
418
- return { kind: 'unreachable', spaceId: member.spaceId, liveMarker, targetHash };
418
+ return { kind: 'unreachable', spaceId: space.spaceId, liveMarker, targetHash };
419
419
  }
420
420
  if (walked.kind === 'unsatisfiable') {
421
421
  const liveHash = liveMarker?.storageHash ?? EMPTY_CONTRACT_HASH;
422
422
  return {
423
423
  kind: 'unsatisfiable',
424
- spaceId: member.spaceId,
425
- isAppMember,
424
+ spaceId: space.spaceId,
425
+ isAppSpace,
426
426
  missing: walked.missing,
427
427
  targetInvariants,
428
- targetMember,
428
+ targetSpace,
429
429
  liveHash,
430
430
  refName,
431
431
  };
@@ -435,32 +435,32 @@ export function planMemberPath({
435
435
 
436
436
  /**
437
437
  * Build a zero-op {@link PerSpacePlan} for an empty-graph
438
- * member whose live marker already matches the target. Lets the apply
439
- * pipeline thread the member through `perSpacePlans` -> `applyOrder`
438
+ * space whose live marker already matches the target. Lets the apply
439
+ * pipeline thread the space through `perSpacePlans` -> `applyOrder`
440
440
  * -> the success envelope's `perSpace[]` block so the result reflects
441
441
  * every loaded space, even when there is nothing to execute.
442
442
  */
443
443
  function buildAtHeadResolution(args: {
444
444
  readonly aggregateTargetId: string;
445
- readonly member: ContractSpaceMember;
445
+ readonly space: AggregateContractSpace;
446
446
  readonly targetHash: string;
447
447
  readonly liveMarker: ContractMarkerRecordLike | null;
448
448
  }): PerSpacePlan {
449
- const { aggregateTargetId, member, targetHash, liveMarker } = args;
449
+ const { aggregateTargetId, space, targetHash, liveMarker } = args;
450
450
  return {
451
451
  plan: {
452
452
  targetId: aggregateTargetId,
453
- spaceId: member.spaceId,
453
+ spaceId: space.spaceId,
454
454
  origin: liveMarker === null ? null : { storageHash: liveMarker.storageHash },
455
455
  destination: { storageHash: targetHash },
456
456
  operations: [],
457
457
  providedInvariants: [],
458
458
  },
459
459
  displayOps: [],
460
- destinationContract: member.contract(),
461
- strategy: 'graph-walk',
460
+ destinationContract: space.contract(),
461
+ strategy: 'declared-state',
462
462
  migrationEdges: [
463
- buildSynthMigrationEdge({
463
+ buildFabricatedMigrationEdge({
464
464
  currentMarkerStorageHash: liveMarker?.storageHash,
465
465
  destinationStorageHash: targetHash,
466
466
  operationCount: 0,
@@ -494,7 +494,7 @@ interface BuildSuccessArgs {
494
494
  }
495
495
 
496
496
  function buildSuccess(args: BuildSuccessArgs): MigrateSuccess {
497
- // The marker hash surfaced at the top level is the **app member's**
497
+ // The marker hash surfaced at the top level is the **app space's**
498
498
  // post-migrate marker (the top-level `markerHash` field).
499
499
  // Per-space markers live on `perSpace[].marker.storageHash`.
500
500
  const appResolution = args.orderedResolutions.find(
@@ -586,7 +586,7 @@ export function buildPathNotFoundFailure(
586
586
  const fromHash = marker?.storageHash ?? '<empty>';
587
587
  // The app-case phrasing names the user-visible condition (a
588
588
  // contract has been emitted that no on-disk migration reaches) so
589
- // the error reads naturally for the app member. Extension spaces
589
+ // the error reads naturally for the app space. Extension spaces
590
590
  // see the same condition expressed against the offending space.
591
591
  const summary =
592
592
  spaceId === 'app'