Skip to content

Commit 946f282

Browse files
dmealingclaude
andcommitted
docs(plan): split metamodel batch into parallel metadata-codegen + migrate-ts workstreams
The unified implementation plan from commit a8bb997 was written for a single session to pick up. The shape of the actual work splits cleanly along package boundaries — metadata + codegen in one stream, migrate-ts in the other — so two sessions on two machines can run in parallel after the metadata foundation lands per FR. - Rename existing plan to metamodel-batch-metadata-codegen-plan.md and re-scope to metadata + codegen only. Adds a Coordination-with-migrate-ts-session section spelling out the metadata-lands-first contract, per-FR push beacons, and shared-resource boundaries. - New sibling: metamodel-batch-migrate-ts-plan.md. Scoped to the three migrate-ts slices (FR-016 resolution rewrite, FR-015 pg_proc introspect + skeleton emit, FR-014 TPH DDL + drift). FR-013 has no migrate-ts work — noted explicitly. Per-task preflight checks gate on the sister-session's metadata commit being on main. Each plan now reads as a self-contained execution doc for its session. Sister-plan pointers at the top of both. ADR-0015 (TS-only schema migration) keeps the migrate-ts work TS-only — no per-port fan-out on that workstream. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent a8bb997 commit 946f282

2 files changed

Lines changed: 272 additions & 40 deletions

File tree

docs/superpowers/plans/2026-05-31-metamodel-batch-fr013-016-implementation-plan.md renamed to docs/superpowers/plans/2026-05-31-metamodel-batch-metadata-codegen-plan.md

Lines changed: 56 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
1-
# Metamodel Batch (FR-013/014/015/016 + ADR-0018) — Implementation Plan
1+
# Metamodel Batch (FR-013/014/015/016 + ADR-0018) — Metadata + Codegen Workstream
22

3-
> **For agentic workers:** REQUIRED SUB-SKILLS: `superpowers:brainstorming` (before each FR's implementation, brainstorm with the user if scope is unclear), `superpowers:test-driven-development` (every loader / codegen / migrate change is TDD-first against the conformance corpus), `superpowers:subagent-driven-development` (the per-port fan-outs in FR-013/015/016 are parallel-friendly subagent work once TS lands).
3+
> **For agentic workers:** REQUIRED SUB-SKILLS: `superpowers:brainstorming` (before each FR's implementation, brainstorm with the user if scope is unclear), `superpowers:test-driven-development` (every loader / codegen change is TDD-first against the conformance corpus), `superpowers:subagent-driven-development` (the per-port fan-outs are parallel-friendly subagent work once TS lands).
44
>
55
> **Gate:** the cross-port conformance corpus is the contract. A change is done when (a) its TS reference is green against the conformance fixtures it adds, AND (b) every other port — Java / Kotlin / C# / Python — runs the same fixtures byte-identically. No exceptions.
66
7-
**Status:** Plan (ready for execution on a fresh Claude session).
7+
**Status:** Plan (in execution).
88
**Created:** 2026-05-31.
9-
**Scope:** four new metamodel design FRs + the ADR that frames one of them. Closes the metamodel gaps surfaced by adopter pressure ahead of 1.0:
9+
**Sister plan:** [migrate-ts workstream](2026-05-31-metamodel-batch-migrate-ts-plan.md) — runs in parallel on a separate session/machine. **Coordination contract is "metadata-lands-first per FR";** see [Coordination with migrate-ts session](#coordination-with-migrate-ts-session).
1010

11+
**Scope of this plan:** **metadata + codegen** across the four FRs. The four `migrate-ts` slices (FR-014 TPH DDL, FR-015 `pg_proc` introspect, FR-016 physical-name resolution rewrite, FR-013 optional `verify` warn) are **out of scope here** — the sister plan owns them.
12+
13+
Source designs:
1114
- [ADR-0018 — Per-kind physical-name attributes within source paradigms](../../../spec/decisions/ADR-0018-per-kind-physical-name-attrs.md)
1215
- [FR-013 — Field-level `@readOnly`](../specs/2026-05-28-fr-013-field-read-only-design.md)
1316
- [FR-014 — TPH discriminator on `object.entity`](../specs/2026-05-28-fr-014-tph-discriminator-design.md)
1417
- [FR-015 — `@parameterRef` on `source.rdb` (callable inputs)](../specs/2026-05-28-fr-015-source-parameter-ref-design.md)
1518
- [FR-016 — `source.rdb` `name` + per-kind aliases (realizes ADR-0018)](../specs/2026-05-28-fr-016-source-rdb-name-and-kind-aliases-design.md)
1619

17-
The four FRs are **design-complete** at the metamodel / loader / ORM-emission layer. This plan adds the missing pieces:
20+
This plan adds the three pieces missing from the per-FR design docs:
1821
1. **Execution order + dependency map** so two of these FRs don't crash into each other.
19-
2. **Generator-stack gap list per FR.** The FRs' per-port codegen tables describe the entity/ORM layer; this plan enumerates the additional generator surfaces — routes, hooks, forms, grids, filter allowlists, Spring controllers/DTOs/repos — each FR must touch and which currently aren't specified in the FRs themselves.
22+
2. **Generator-stack gap list per FR.** The FRs' per-port codegen tables describe the entity/ORM layer; this plan enumerates the additional generator surfaces — routes, hooks, forms, grids, filter allowlists, Spring controllers/DTOs/repos — each FR must touch and which aren't specified in the FRs themselves.
2023
3. **A directive for FR-014 specifically:** write FR-017 as the first execution step of TPH work, before implementation. The TPH polymorphic-codegen design is its own beast and deserves a focused spec.
2124

2225
---
@@ -35,45 +38,57 @@ The four FRs are **design-complete** at the metamodel / loader / ORM-emission la
3538
▼ ▼ ▼
3639
┌─────────┐ ┌─────────┐ ┌─────────────────┐
3740
│ FR-013 │ │ FR-015 │ │ FR-014 + 017 │
38-
@readOnly│ │parameter│ │ TPH discriminator│
41+
│@readOnly│ │parameter│ │ TPH discriminator│
3942
│ │ │ Ref │ │ + polymorphic │
4043
│ │ │ │ │ generator-stack │
4144
└─────────┘ └─────────┘ └─────────────────┘
4245
```
4346

4447
**Order:** FR-016 → (FR-013 ∥ FR-015 ∥ FR-014/017).
4548

46-
**Why FR-016 first.** FR-015's example metadata writes `@proc: "fn_x"` (not `@table: "fn_x"`), and FR-014 doesn't depend on alias spelling at all, but the canonical-serializer rewrite from ADR-0018 changes the corpus's wire format. Landing FR-016 before adding new conformance fixtures for the other FRs means new fixtures use canonical per-kind spellings from the start — no churn.
49+
**Why FR-016 first.** FR-015's example metadata uses `@proc: "fn_x"` (not `@table: "fn_x"`), and the canonical-serializer rewrite from ADR-0018 changes the corpus's wire format. Landing FR-016 before adding new conformance fixtures for the other FRs means new fixtures use canonical per-kind spellings from the start — no churn.
4750

4851
**Why FR-013/015 are independent.** Different layers (FR-013 is field-attr; FR-015 is source-attr) with no shared loader code path.
4952

5053
**Why FR-014 needs FR-017 written first.** The metamodel piece of TPH (`@discriminator` + `@discriminatorValue` on `object.entity`) is well-specified in FR-014. The generator-stack design (polymorphic REST routes, per-subtype forms, discriminated-union hooks, per-subtype filter allowlists, Spring `@Inheritance` controllers / DTOs / repos / payload generators) is **not** specified. Implementing TPH without that design produces inconsistent per-port output. Write FR-017 first, then implement.
5154

5255
---
5356

54-
## Recommended workflow for the next session
57+
## Coordination with migrate-ts session
58+
59+
The sibling [migrate-ts plan](2026-05-31-metamodel-batch-migrate-ts-plan.md) runs in parallel. Coordination rules:
60+
61+
1. **Metadata-lands-first per FR.** This session pushes the metadata (constants, schemas, loader validation, canonical-serializer, conformance fixtures) for FR-N to `main` BEFORE the migrate-ts session starts FR-N migrate-ts work. The migrate-ts session reads the loaded model — without the new attrs registered, the migrate-ts work has nothing to consume.
62+
2. **Per-FR push beacon.** When the metadata commit for an FR lands on `main`, leave a single-line comment in the sister plan's "FR-N preflight" row marking it green (or just message the user; the user is the human courier between the two sessions).
63+
3. **Disjoint package directories.** This session touches `packages/metadata`, `packages/codegen-ts*`, `packages/runtime-ts`, `server/java/codegen-spring`, `server/kotlin/codegen-kotlin`, C# / Python codegen. The migrate-ts session touches `packages/migrate-ts` only. No code-file overlap.
64+
4. **Shared resources** that need coordination:
65+
- `fixtures/conformance/` — this session owns metamodel fixtures (round-trip + loader validation).
66+
- `fixtures/persistence-conformance/` — both sessions add fixtures: this session adds query scenarios for new attrs (e.g., TPH polymorphic queries); the migrate-ts session adds migration scenarios for new DDL emission.
67+
- Conformance corpus regeneration after FR-016 (canonical rewrite of `@table``@view`/`@proc`/etc. for non-table kinds) is a **this session task** since it's the canonical-serializer output that changes; the migrate-ts session pulls and continues.
68+
69+
---
5570

56-
When a fresh Claude session picks this up:
71+
## Recommended workflow for this session
5772

58-
1. **Pull latest.** `git fetch && git status` — ensure on `main` and clean. The five spec docs + ADR-0018 + this plan are already on `origin/main`.
59-
2. **Read the four spec docs + ADR-0018** in this order: ADR-0018, FR-016, FR-013, FR-015, FR-014. (You're reading high-context first, then easier-to-harder.)
60-
3. **Pick a task.** Start with FR-016 (smallest, unblocks everything). When that lands, fan out FR-013 + FR-015 in parallel, and start FR-014 by writing FR-017.
61-
4. **Per-FR rhythm:** brainstorm (only if the FR's design has gaps you discover) → write TDD-style task plan as a new file under `docs/superpowers/plans/` named `2026-MM-DD-fr-NNN-implementation.md` → execute red/green/refactor → fan out to ports via subagents → conformance corpus stays green throughout.
62-
5. **Push cadence:** each FR is its own PR-sized chunk. Land TS reference + corpus fixtures first, then per-port fan-out commits, then close.
73+
1. **Pull latest** every time you start. The migrate-ts session pushes to `main` too.
74+
2. **Read the four spec docs + ADR-0018** in this order: ADR-0018, FR-016, FR-013, FR-015, FR-014.
75+
3. **Pick a task.** Start with FR-016 (smallest, unblocks everything). When FR-016 metadata lands, signal the migrate-ts session, then fan out FR-013 + FR-015 in parallel here, and start FR-014 by writing FR-017.
76+
4. **Per-FR rhythm:** brainstorm (only if the FR's design has gaps you discover) → write TDD-style task list with TodoWrite → execute red/green/refactor → fan out to ports via subagents → conformance corpus stays green throughout.
77+
5. **Push cadence:** each FR is its own PR-sized chunk. Land TS reference + metadata fixtures first (signal migrate-ts session), then codegen, then per-port fan-out commits.
6378

6479
---
6580

6681
## Task: FR-016 (lands first)
6782

68-
### Metamodel / loader work (specified in FR-016)
83+
### Metadata / loader work
6984
- 5 new `AttrSchema` entries on `sourceRdbAttrs` (`@table`, `@view`, `@materializedView`, `@proc`, `@function`), 1 new `SOURCE_ATTR_NAME` schema entry.
7085
- Constants in `server/typescript/packages/metadata/src/persistence/source/source-constants.ts` per FR-016 §Decision Part 2.
7186
- `PHYSICAL_NAME_ATTR_BY_KIND` map.
7287
- Four-step physical-name resolution in the loader (`server/typescript/packages/metadata/src/loader/`).
7388
- Loader errors `ERR_PHYSICAL_NAME_KIND_MISMATCH`, `ERR_PHYSICAL_NAME_MULTIPLE`, `WARN_LEGACY_PHYSICAL_NAME_ALIAS`.
7489
- Canonical-serializer rewrite: input `@kind: "storedProc"` + `@table` → output `@proc`.
7590

76-
### Conformance fixtures (specified in FR-016)
91+
### Conformance fixtures
7792
9 fixtures under `fixtures/conformance/` — 6 positive + 3 error. See FR-016 §Conformance fixtures.
7893

7994
### Generator-stack sweep (NOT in FR-016 — open work)
@@ -83,24 +98,26 @@ Every generator that today reads `source.@table` to determine the physical name
8398
- `server/typescript/packages/codegen-ts/src/generators/queriesFile.ts` — Kysely / Drizzle query helpers.
8499
- `server/typescript/packages/codegen-ts/src/generators/routesFile.ts` — Fastify route paths derive from physical name in some cases.
85100
- `server/typescript/packages/runtime-ts/src/drizzle-fastify/*.ts` — runtime query helpers.
86-
- `server/typescript/packages/migrate-ts/src/expected-schema.ts` — already in scope per FR-016 §migrate-ts impact.
101+
102+
> The corresponding `migrate-ts/src/expected-schema.ts` rewrite is **on the sister plan**, not here.
87103
88104
**Sweep recipe:** `grep -rn 'SOURCE_ATTR_TABLE\|"@table"\|source\.table\b' server/typescript/packages/ --include='*.ts'` → migrate each call site to the helper.
89105

90106
### Per-port fan-out
91107
Java / Kotlin / C# / Python: register the same 5 attr aliases; mirror canonical-serializer output; sweep their generators for `@table`-hardcoding equivalent to the TS sweep. Per FR-016, ~half day per port.
92108

93-
### Done when
109+
### Done when (this session)
94110
- TS reference green against the 9 new fixtures.
95111
- `WARN_LEGACY_PHYSICAL_NAME_ALIAS` fires on legacy `@table` for non-table kind.
96112
- Every existing rdb-source conformance fixture canonical-output uses the per-kind attr key matching `@kind`.
97113
- All four other ports run the 9 fixtures byte-identically.
114+
- **Signal the migrate-ts session that FR-016 metadata is on `main` so they can start their slice.**
98115

99116
---
100117

101118
## Task: FR-013 (fan out after FR-016)
102119

103-
### Metamodel / loader work (specified in FR-013)
120+
### Metadata / loader work (specified in FR-013)
104121
- `FIELD_ATTR_READ_ONLY = "readOnly"` constant + schema entry on `commonFieldAttrs` in `server/typescript/packages/metadata/src/core/field/`.
105122
- Cross-attr validation: `ERR_READONLY_ASSIGNED_PRIMARY`, `ERR_READONLY_DOWNGRADE`.
106123
- `WARN_READONLY_VALUE_OBJECT` for `@readOnly` on `object.value` field children.
@@ -110,7 +127,7 @@ Java / Kotlin / C# / Python: register the same 5 attr aliases; mirror canonical-
110127
6 fixtures — 4 positive + 2 error. See FR-013 §Conformance fixtures.
111128

112129
### Per-port ORM emission (specified in FR-013)
113-
TS Drizzle, Java JPA, Kotlin Exposed, C# EF Core, Python SQLAlchemy. See FR-013 §Per-port codegen mapping for the per-port emission rules.
130+
TS Drizzle, Java JPA, Kotlin Exposed, C# EF Core, Python SQLAlchemy. See FR-013 §Per-port codegen mapping.
114131

115132
### Generator-stack gaps (NOT in FR-013 — open work)
116133
The FR's per-port table covers the entity/ORM emission. These generators also need updating:
@@ -123,10 +140,12 @@ The FR's per-port table covers the entity/ORM emission. These generators also ne
123140
- **`server/java/codegen-spring/`** — filter-allowlist generator no change (filter is a read concern).
124141
- **`fixtures/api-contract-conformance/`** — add a scenario: POST with a readOnly field in the body returns 400. Cross-port byte-equivalent.
125142

143+
> FR-013 has **no migrate-ts work** (the FR explicitly states "no DDL emission"). The optional `WARN_READONLY_NO_GENERATOR` in `migrate-ts verify` is listed on the sister plan as "defer/skip."
144+
126145
### Per-port fan-out
127146
After TS reference + ORM-emission per port, run the conformance corpus on each port. ~1 day per port per FR-013's estimate, parallel.
128147

129-
### Done when
148+
### Done when (this session)
130149
- TS reference green against 6 fixtures.
131150
- API-contract conformance scenario (POST with readOnly field) green across all 5 ports.
132151
- Generated forms in `client/web/packages/react` skip / disable readOnly fields (snapshot test).
@@ -135,7 +154,7 @@ After TS reference + ORM-emission per port, run the conformance corpus on each p
135154

136155
## Task: FR-015 (fan out after FR-016)
137156

138-
### Metamodel / loader work (specified in FR-015)
157+
### Metadata / loader work (specified in FR-015)
139158
- `SOURCE_ATTR_PARAMETER_REF = "parameterRef"` constant + schema entry on `sourceRdbAttrs`.
140159
- Loader errors: `ERR_PARAMETER_REF_REQUIRED_FOR_CALLABLE`, `ERR_PARAMETER_REF_UNRESOLVED`, `ERR_PARAMETER_REF_NOT_VALUE_OBJECT`, `ERR_PARAMETER_REF_ON_NON_CALLABLE_KIND`, `ERR_PARAMETER_REF_PASSTHROUGH_TYPE_MISMATCH`.
141160
- Resolution: bare name OR FQN; reuses standard reference-resolution.
@@ -146,10 +165,7 @@ After TS reference + ORM-emission per port, run the conformance corpus on each p
146165
### Per-port repo emission (specified in FR-015)
147166
TS Drizzle wrapper, Java Spring `@Query`, Kotlin Exposed transaction, C# EF Core `FromSqlInterpolated`, Python SQLAlchemy `text(...)`. See FR-015 §Per-port codegen mapping.
148167

149-
### `migrate-ts` work (specified in FR-015)
150-
- `pg_proc` introspection in `migrate-ts/src/expected-schema.ts`.
151-
- Signature validation against live DB.
152-
- `CREATE OR REPLACE FUNCTION` skeleton emit for new procs (body = `RAISE NOTICE 'unimplemented'`).
168+
> The `pg_proc` introspection + signature drift + `CREATE OR REPLACE FUNCTION` skeleton emit are on the sister plan, not here.
153169
154170
### Generator-stack gaps (NOT in FR-015 — open work)
155171
The FR specifies how the **calling code** emits. It does NOT specify whether stored procs are exposed as REST endpoints. **Decision required before implementation:**
@@ -171,9 +187,8 @@ Procs typically don't accept arbitrary filter/sort. **Default:** no filter / sor
171187
### Per-port fan-out
172188
After TS reference + REST decision, port the attr-registration + repo-emission per port. ~1 day per port per FR-015's estimate, parallel.
173189

174-
### Done when
190+
### Done when (this session)
175191
- TS reference green against 10 fixtures.
176-
- `pg_proc` signature drift test green (in `server/typescript/packages/migrate-ts/test/`).
177192
- `@exposeAsRoute` decision documented in the implementation-plan PR.
178193
- All 5 ports' generated repo helpers callable in the persistence-conformance corpus.
179194

@@ -202,20 +217,20 @@ FR-014 covers the metamodel + ORM-layer emission. FR-017 covers everything else
202217
- **C# generator stack.** EF `HasDiscriminator` in entity gen; per-subtype DTO + controller.
203218
- **Python generator stack.** SQLAlchemy polymorphic config + per-subtype Pydantic + per-subtype FastAPI route.
204219
- **API-contract conformance.** Polymorphic CRUD scenarios — cross-port byte-equivalent.
205-
- **Persistence-conformance.** TPH query + filter + dispatch — cross-port.
220+
221+
> FR-014's migrate-ts piece (TPH DDL emit + drift detection) is on the sister plan. **This session and the migrate-ts session both depend on the FR-014 metadata commit landing first.** Once it does, they execute in parallel — disjoint package directories.
206222
207223
### Step 2: Implementation order
208224

209225
Per FR-014:
210-
1. TS reference: metamodel + 9 conformance fixtures + `migrate-ts` TPH emit and diff (~1 week per FR-014 estimate).
226+
1. TS reference: metamodel + 9 conformance fixtures (this session).
211227
2. TS reference: FR-017 generator-stack work (~1+ week — the polymorphic generator surface is large).
212228
3. Per-port fan-out: TS metamodel + generator-stack per port (Java largest because of Spring controller / DTO / repo / payload generator interplay).
213229

214-
### Done when
230+
### Done when (this session)
215231
- FR-017 spec doc landed.
216232
- FR-014's 9 metamodel fixtures green.
217233
- FR-017's API-contract conformance scenarios green across 5 ports.
218-
- Persistence-conformance TPH scenarios green across 5 ports (`fixtures/persistence-conformance/`).
219234
- An adopter project (the C# CareMetx P3 CRM is the driver) can model their workflow-task / authorization / payment polymorphism in metadata and codegen produces working CRUD.
220235

221236
---
@@ -242,14 +257,15 @@ Before every commit: scan the diff for absolute home paths and other-project nam
242257

243258
---
244259

245-
## Done criteria for the whole batch
260+
## Done criteria for this workstream
261+
262+
- All 4 FRs' metadata layers implemented across all 5 ports.
263+
- All 4 FRs' codegen layers implemented (TS reference + per-port fan-out).
264+
- FR-017 spec written + implemented.
265+
- Cross-port conformance corpus (metamodel + render + api-contract) green.
266+
- Each FR's status updated from *Design**Implemented* (the migrate-ts portion may still be in flight on the sister plan; mark the codegen-side as done independently).
246267

247-
- All 5 docs implemented across all 5 ports.
248-
- Cross-port conformance corpus (metamodel + render + persistence + api-contract) green.
249-
- ADR-0018 status promoted from *Proposed**Accepted*.
250-
- ADR-0007 status updated (the per-kind-aliases section now realized).
251-
- Each FR's status updated from *Design**Implemented*.
252-
- An adopter project (CareMetx P3 CRM) declares a polymorphic entity in metadata and codegen produces working CRUD + REST + form + grid.
268+
The whole batch is done when this plan AND the sister migrate-ts plan are both green.
253269

254270
---
255271

0 commit comments

Comments
 (0)