Skip to content

Commit d869089

Browse files
wmaddenclaude
andcommitted
TML-2500(M1): slice spec + plan amendment for disjointness reconciliation
Record Decision D-recon (fix disjointness namespace-awareness, remove namespaceOwnershipCollision) and the walking-skeleton obligation (FK deferred to M2/M3). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Will Madden <madden@prisma.io>
1 parent 1b48f34 commit d869089

2 files changed

Lines changed: 64 additions & 0 deletions

File tree

projects/cross-contract-refs/slices/M1/plan.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,32 @@ ACs owned by M1: **AC6** (collision + cycle rejection), **AC8** (round-trip prop
5252
## Open items (deferred from M1)
5353

5454
- **Transitive auto-loading of unlisted contract spaces.** M1.2 builds the dependency graph + cycle detection over the descriptor set the app lists in `extensionPacks`, with edges from each pack's declared `extensionPacks`, and errors on a declared-but-absent dependency. Auto-discovering and loading a pack the app did **not** list (via an extension's bundled `extensionPacks`) is deferred — it requires a `spaceId → descriptor` resolution mechanism not present today. Additive when needed.
55+
56+
## Amendment dispatches (2026-06-06) — reconciliation + walking skeleton
57+
58+
Added after merging `main` (PR#746 walking skeleton + #719). See `spec.md` § Amendment / Decision D-recon.
59+
60+
### Dispatch M1.5 — Reconcile cross-space ownership checks
61+
62+
- **Outcome:** make `main`'s `disjointness` check **namespace-aware** and **remove our duplicate `namespaceOwnershipCollision`** entirely. After this, one correct cross-space ownership check exists.
63+
- **Fix `disjointness`** (`packages/1-framework/3-tooling/migration/src/aggregate/check-integrity.ts` ~`contractViolations`, lines ~230–246): key `elementClaimedBy` on the full `(namespaceId, entityKind, entityName)` coordinate (all already yielded by `elementCoordinates`), not bare `entityName`. The `disjointness` violation's `element` becomes the qualified coordinate string (`claimedBy` shape unchanged). Update consumers' display strings (`contract-space-aggregate-loader.ts`, cli `integrity-violation-to-check-failure.ts`) and the assertion in `loader.test.ts` (~:366, `'user'` → qualified form).
64+
- **Remove `namespaceOwnershipCollision`:** the IntegrityViolation kind (`integrity-violation.ts:67–73` + JSDoc); `findNamespaceOwnershipCollisions` + `NamespaceCollision*` types (`control-stack.ts:382–459`) + their `exports/control.ts` re-exports; the call + helper in `check-integrity.ts` (line 118 + `namespaceOwnershipCollisionViolations` 261–286 + import); the cli case `PN-MIG-CHECK-017` (`integrity-violation-to-check-failure.ts:138–145`); the `migration-check.ts:384` filter entry; and the tests (`control-stack.test.ts:730–784`, `check-integrity.test.ts:78–118`).
65+
- **Migrate test coverage to `disjointness`:** add tests proving (a) same name in **different** namespaces across spaces → **no** collision (the `auth.users` vs `public.users` case — the false-positive we're fixing); (b) same `(namespace, kind, name)` across two spaces → collision naming both contributors; through the real load path.
66+
- **Validation gate (expanded — closes the babysit gaps):** the touched packages' tests + `pnpm typecheck` + `pnpm lint:deps` + `pnpm lint:casts` AND **full `pnpm lint`** (`biome check --error-on-warnings`) AND **`pnpm fixtures:check`** AND the **`examples/supabase` skeleton test** — the last three were the gaps CI caught during babysit. Rebuild dependent `dist` before downstream tests.
67+
- **dispatch-INVEST:** one logical change (make the check correct, drop the duplicate); Small-ish (mostly deletions + one keying fix + test migration).
68+
69+
### Dispatch M1.6 — Walking-skeleton verification + FK-deferral record
70+
71+
> **Resolution (2026-06-06): no code dispatch needed.** M1.5's review confirmed the skeleton requires **no M1 change** (the cross-contract FK genuinely needs M2 authoring + M3 planner/verifier), and `examples/supabase/test/skeleton.integration.test.ts`'s JSDoc already frames "later constituents extend in place." So M1.6 is satisfied by: (a) the **PR #745 description** recording that the `Profile.userId → auth.User.id` FK + cascade DoD is deferred to M2/M3, and (b) **CI** verifying the skeleton stays green against M1's reconciled checks (it couldn't be run locally — `examples/supabase` deps aren't installed in this worktree subtree). No implementer dispatch was spawned.
72+
73+
- **Outcome:** confirm the `examples/supabase` skeleton integration test is green against M1 (post-M1.5), and **record that the cross-contract FK + cascade-delete DoD is deferred to M2 (authoring) + M3 (planner/verifier)** — not achievable in M1 (no authoring surface). No change to the skeleton's app contract / migration artefacts.
74+
- **Where the record lands:** the skeleton test's JSDoc (already frames "later constituents extend in place" — make the M1-vs-M2/M3 boundary explicit) and the PR #745 walking-skeleton note.
75+
- **Validation gate:** `examples/supabase` test green; `pnpm fixtures:check` clean (the skeleton emits `migrations/supabase/*`).
76+
- **dispatch-INVEST:** verification + docs; no production code change.
77+
78+
## Revised slice DoD (supersedes the above for the amended scope)
79+
80+
- AC6 delivered via the **namespace-aware `disjointness`** (cycle + reverse-ref + collision); `namespaceOwnershipCollision` removed; AC8/AC10 green; AC9 regression.
81+
- `examples/supabase` skeleton green against M1; FK-step deferral to M2/M3 recorded.
82+
- Full `pnpm lint` + `pnpm fixtures:check` + skeleton test added to the slice's standing gate (babysit lesson).
83+
- Reviewer SATISFIED; CI green on PR #745.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# M1 — Foundation (slice spec)
2+
3+
Slice of project [cross-contract-refs](../../spec.md) (TML-2500), PR #745. Delivers the IR carrier + aggregate-load checks; **no authoring surface** (that's M2), **no planner/verifier e2e** (M3).
4+
5+
Owns project ACs: **AC6** (collision + cycle rejection at aggregate load), **AC8** (round-trip), **AC10** (`lint:deps` + cast ratchet), and **AC9** regression.
6+
7+
## Delivered (dispatches M1.1–M1.4)
8+
9+
- **FK reference carrier**`ForeignKeyReference` carries an optional `spaceId`; its presence discriminates a cross-space target from a local one (absent = local). Local FKs serialize byte-identically (NFR2). (M1.1, simplified in M1.4 — `origin` discriminator dropped per operator decision.)
10+
- **Cross-space dependency graph + cycle rejection**`buildExtensionLoadOrder` (framework-components), edges from each pack's declared `extensionPacks`, errors on an unlisted declared dependency, rejects cycles (FR12/FR13). The computed order is applied when assembling the control stack (review fix A-001).
11+
- **Reverse-reference rejection**`assertNoCrossSpaceFkReverseReferences` (SQL family) rejects an extension FK pointing against the dependency arrows (FR14).
12+
- **Namespace-ownership collision detection***(superseded — see amendment below)*.
13+
14+
## Amendment (2026-06-06): reconcile with main's `disjointness`; update the merged walking skeleton
15+
16+
Merging latest `main` brought in two relevant landings (#719 Mongo marker/ledger — orthogonal; and PR#746 — the `@prisma-next/supabase` extension + the `examples/supabase` **walking skeleton**) and surfaced a redundancy + latent bug in how cross-space primitive ownership is checked.
17+
18+
### Decision D-recon — fix `disjointness`, remove our `namespaceOwnershipCollision`
19+
20+
`main` already has a `disjointness` integrity check (`PN-MIG-CHECK-014`, in `packages/1-framework/3-tooling/migration/src/aggregate/check-integrity.ts`) that detects "a storage element claimed by multiple contract spaces" using the canonical `elementCoordinates(storage)` walker — **but it keys on bare `entityName`**, discarding namespace + kind. That is namespace-blind: it would **falsely flag `auth.users` (supabase space) and `public.users` (app space)** — same name, different namespaces — as a collision. That multi-namespace same-name scenario is exactly what this project enables, so our feature turns a latent `disjointness` bug into a live one.
21+
22+
Our M1.3 `namespaceOwnershipCollision` (`PN-MIG-CHECK-017`) is the namespace-aware version (keys on `namespace:kind:name`), but it **duplicates the concern** with a bespoke walk in a different package — the very "hand-walk an untyped structure instead of the typed/canonical path" pattern that #719 spent a PR eliminating elsewhere.
23+
24+
**Resolution:** make `disjointness` namespace-aware (key on the full `(namespaceId, entityKind, entityName)` coordinate that `elementCoordinates` already yields) and **remove our `namespaceOwnershipCollision` entirely** — after the fix the two are exact duplicates, and `disjointness` is the incumbent broadly consumed by `db-init` / `db-run` / `db-verify` / `migration-check`. AC6's collision half is then delivered by the fixed `disjointness`, not a separate new check. This is preferred over removing `disjointness` (which would mean re-threading the refusal chain through many consumers).
25+
26+
### Walking-skeleton obligation (revised for M1's level)
27+
28+
`examples/supabase` (landed on `main`) composes the app space (`public.Profile`) + the `@prisma-next/supabase` extension space (`auth.*`, `storage.*`, external) via `extensionPacks`, and its integration test runs `db init` / `db verify` — which now execute our aggregate-integrity checks. The project's full walking-skeleton DoD (add `Profile.userId → auth.User.id` cross-contract FK + cascade test, planner emits qualified `REFERENCES "auth"."users"`) **requires M2 (authoring surface) + M3 (planner/verifier) and is NOT achievable in M1** (no way to author the cross-space FK yet). M1's obligation is narrower: **the skeleton stays green against M1's checks** (including the reconciled `disjointness`), and the deferral of the FK step to M2/M3 is recorded.
29+
30+
## Slice DoD (updated)
31+
32+
- AC6 (cycle + reverse-ref + namespace-aware collision via fixed `disjointness`), AC8, AC10 green; AC9 regression.
33+
- `disjointness` is namespace-aware; no `namespaceOwnershipCollision` remains; the duplicate is gone.
34+
- `examples/supabase` skeleton test green against M1; FK-step deferral to M2/M3 documented.
35+
- Reviewer SATISFIED; CI green; PR #745 updated.

0 commit comments

Comments
 (0)