|
| 1 | +# Neutral metadata documentation — implementation plan |
| 2 | + |
| 3 | +> **For agentic workers:** REQUIRED SUB-SKILL: superpowers:subagent-driven-development. Steps use checkbox (`- [ ]`) tracking. |
| 4 | +
|
| 5 | +**Goal:** Generate neutral metadata documentation (entity + `template.output` |
| 6 | +pages) from a single shared TS engine, with one canonical doc-template source, |
| 7 | +runnable standalone via `meta docs`. No language assumptions, no `--target`. |
| 8 | + |
| 9 | +**Architecture:** Tier-2 per ADR-0020 — one shared engine (TS), language-neutral |
| 10 | +output. Canonical Mustache templates live at root `templates/`; the codegen-ts |
| 11 | +package ships a byte-identity-gated copy. Entity page is neutralized (drop |
| 12 | +Zod/generated SDK sections; document constraint metadata). New neutral template |
| 13 | +page documents the render contract. New `meta docs` CLI command emits both from |
| 14 | +metadata alone. |
| 15 | + |
| 16 | +**Tech Stack:** TypeScript, bun, the existing `render()`/`templateGenerator()` |
| 17 | +engine, Mustache. |
| 18 | + |
| 19 | +**Scope guard:** TS only (single engine). Do NOT port the docs builder to |
| 20 | +C#/Python/Java/Kotlin. Do NOT touch native code generators. SDK/API docs are out |
| 21 | +of scope (Tier 1, future). |
| 22 | + |
| 23 | +--- |
| 24 | + |
| 25 | +## Reference files (read before starting) |
| 26 | + |
| 27 | +- `server/typescript/packages/codegen-ts/src/generators/docs-data.ts` — `EntityDocData` shape. |
| 28 | +- `server/typescript/packages/codegen-ts/src/generators/docs-data-builder.ts` — builder; `constraintsCell()` (~L159), `validation` (~L370), `generated` (~L392-443). |
| 29 | +- `server/typescript/packages/codegen-ts/src/generators/docs-file.ts` — per-entity emission; iterates `ctx.loadedRoot.objects().filter(ctx.matches)`. |
| 30 | +- `server/typescript/packages/codegen-ts/src/generators/render-helper-file.ts` — reference for walking `template.output` nodes (`@kind`/`@payloadRef`/`@format`/`@textRef`/`@subjectRef`/`@htmlBodyRef`/`@textBodyRef`/`@maxChars`/`@requiredTags`). |
| 31 | +- `server/typescript/packages/codegen-ts/src/render-engine/framework-provider.ts` — how templates resolve (the package `templates/` dir; `CANONICAL_TEMPLATE_REL`). |
| 32 | +- `server/typescript/packages/codegen-ts/templates/docs/entity-page.md.mustache` — current template. |
| 33 | +- `server/typescript/packages/codegen-ts/test/golden/docs-file-conformance.test.ts` — current golden. |
| 34 | +- `server/typescript/packages/cli/src/commands/migrate.ts` — model for a standalone metadata-loading command (mirror for `docs.ts`). |
| 35 | + |
| 36 | +--- |
| 37 | + |
| 38 | +## Task 1: Canonical `templates/` source + byte-identity gate (pure relocation) |
| 39 | + |
| 40 | +**Intent:** Establish root `templates/` as the single source of truth for doc |
| 41 | +templates; the codegen-ts package keeps a bundled copy (needed in the npm |
| 42 | +tarball) that is byte-identity-gated against root. No output change — golden must |
| 43 | +still pass. |
| 44 | + |
| 45 | +**Files:** |
| 46 | +- Create: `templates/docs/entity-page.md.mustache` (root canonical — copy of the current package template, byte-for-byte) |
| 47 | +- Create: `scripts/sync-doc-templates.sh` (copies `templates/` → consuming packages' bundled `templates/`; idempotent) |
| 48 | +- Keep: `server/typescript/packages/codegen-ts/templates/docs/entity-page.md.mustache` (bundled copy, now generated/synced) |
| 49 | +- Test: `server/typescript/packages/codegen-ts/test/templates-canonical.test.ts` |
| 50 | + |
| 51 | +- [ ] **Step 1: Write the failing byte-identity test.** Reads the repo-root `templates/docs/entity-page.md.mustache` and the package copy; asserts they are byte-identical (`readFileSync` both, `toEqual`). Resolve repo root by walking up from the test file to the dir containing `templates/` + `server/`. |
| 52 | +- [ ] **Step 2: Run → FAIL** (root file doesn't exist yet). `cd server/typescript && bun test packages/codegen-ts/test/templates-canonical.test.ts`. |
| 53 | +- [ ] **Step 3: Create root canonical** by copying the existing package template verbatim into `templates/docs/entity-page.md.mustache`. Add `scripts/sync-doc-templates.sh` that copies root → `server/typescript/packages/codegen-ts/templates/docs/` (so the package copy is reproducible; future ports add their dest here). |
| 54 | +- [ ] **Step 4: Run → PASS** (byte-identical). Also run the existing docs golden to confirm NO output change: `bun test packages/codegen-ts/test/golden/docs-file-conformance.test.ts`. |
| 55 | +- [ ] **Step 5: Commit.** `feat(docs): root templates/ canonical doc-template source + byte-identity gate` |
| 56 | + |
| 57 | +--- |
| 58 | + |
| 59 | +## Task 2: Neutralize the entity page |
| 60 | + |
| 61 | +**Intent:** Remove the language-specific sections. Drop `generated` (TS |
| 62 | +filenames) and the Zod `validation` pointer. Replace with a neutral |
| 63 | +**Constraints** table built from field metadata that renders for ALL entities |
| 64 | +(including value objects with no storage). Keep Storage/Identity/Relationships/ |
| 65 | +Used by. |
| 66 | + |
| 67 | +**Files:** |
| 68 | +- Modify: `server/typescript/packages/codegen-ts/src/generators/docs-data.ts` (remove `GeneratedFileDoc` + `generated`; remove Zod `validation`; add `constraints` table shape: `{ hasConstraints, header, rows: [{ field, required, type, limits, rules }] }`) |
| 69 | +- Modify: `server/typescript/packages/codegen-ts/src/generators/docs-data-builder.ts` (delete the `generated.push(...)` block and the `validation` Zod object; build `constraints` from every field — reuse the existing `constraintsCell` logic for limits/required/enum/validators; works without storage) |
| 70 | +- Modify: `templates/docs/entity-page.md.mustache` (root) — remove `## Generated code`; replace `## Validation` with a `## Constraints` Markdown table (columns: Field | Required | Type | Limits | Rules); make `## Used by` bullets link `./<Template>.md`. Then run `scripts/sync-doc-templates.sh`. |
| 71 | +- Modify: golden `docs-file-conformance.test.ts` to the neutralized output. |
| 72 | +- Test: add a value-object fixture (no storage) with declared constraints (required, maxLength, enum, a `validator.*`) proving constraints render without a Storage section. |
| 73 | +- Test: `server/typescript/packages/codegen-ts/test/docs-neutrality.test.ts` — generated entity docs contain none of: `Zod`, a generated `.ts`/`.cs`/`.kt`/`.py` filename, TS type literals. |
| 74 | + |
| 75 | +- [ ] **Step 1: Write the neutrality test** asserting the rendered entity doc (from an existing fixture) contains no `Zod`, no `.ts` filename, no `## Generated code`. Run → FAIL. |
| 76 | +- [ ] **Step 2: Write the value-object-constraints test** (a VO fixture with required/maxLength/enum/validator) asserting a `## Constraints` table with those rules and NO `## Storage`. Run → FAIL. |
| 77 | +- [ ] **Step 3: Update `docs-data.ts`** — drop `generated`/`GeneratedFileDoc`, drop Zod `validation`, add `constraints` shape. |
| 78 | +- [ ] **Step 4: Update `docs-data-builder.ts`** — remove generated + Zod-validation builders; build the `constraints` table for all fields. |
| 79 | +- [ ] **Step 5: Update + sync `entity-page.md.mustache`** (root → run sync script). |
| 80 | +- [ ] **Step 6: Regenerate/update the golden** to the neutral output; eyeball it for neutrality. |
| 81 | +- [ ] **Step 7: Run → PASS** all of: neutrality test, VO-constraints test, golden, byte-identity gate. `bun test packages/codegen-ts`. |
| 82 | +- [ ] **Step 8: Commit.** `feat(docs): neutralize entity page — constraints table, drop Zod/generated SDK sections` |
| 83 | + |
| 84 | +--- |
| 85 | + |
| 86 | +## Task 3: New neutral `template.output` page |
| 87 | + |
| 88 | +**Intent:** One Markdown page per `template.output`, render-contract-shaped, |
| 89 | +distinct from the entity page, fully neutral (no generated-helper signatures, no |
| 90 | +language types). |
| 91 | + |
| 92 | +**Files:** |
| 93 | +- Create: `server/typescript/packages/codegen-ts/src/generators/template-doc-data.ts` — `TemplateDocData`: `{ generatedMarker, name, kind (document|email), description?, format, isEmail, parts?: [{ label, ref, format, escaped }], payload: { name, link }, referencedFields: string[], requiredTags?: string[], maxChars?: number, sourceRefs: string[], capability: string }`. |
| 94 | +- Create: `server/typescript/packages/codegen-ts/src/generators/template-doc-builder.ts` — `buildTemplateDocData(template, opts)` walking a `template.output` node (mirror `render-helper-file.ts` attr reads). `capability` is a fixed neutral string per kind (document → "returns the rendered string"; email → "returns subject + html body + optional text body"). |
| 95 | +- Create: `templates/docs/template-page.md.mustache` (root) + sync — sections: title + kind/description; `## Output` (format; for email the parts table + multipart note + escaping); `## Input` (payload link + referenced fields/requiredTags); `## Render contract` (maxChars-fails note, requiredTags, neutral drift guarantee); `## Source` (template refs); `## Capability` (the neutral capability sentence). |
| 96 | +- Modify: `docs-file.ts` — after entity pages, iterate `loadedRoot` `template.output` nodes → emit `<Template>.md` via `templateGenerator({ ref: "docs/template-page.md", ... })`. (Or a sibling `template-docs-file.ts`; keep one emission entry from the generator list.) |
| 97 | +- Test: `template-doc-conformance.test.ts` golden — a `document` template (WelcomePage) and an `email` template (WelcomeEmail, 3 parts); assert the sections, the payload link to the entity page, and the entity page's Used-by link resolving to this page. |
| 98 | + |
| 99 | +- [ ] **Step 1: Write the template-page golden test** for a document + an email template (fixtures), asserting the neutral sections, payload→entity link, and entity↔template cross-links resolve. Run → FAIL. |
| 100 | +- [ ] **Step 2: Implement `template-doc-data.ts`** (the shape). |
| 101 | +- [ ] **Step 3: Implement `template-doc-builder.ts`** (walk node → data; neutral capability string). |
| 102 | +- [ ] **Step 4: Author `templates/docs/template-page.md.mustache`** (root) + run sync. |
| 103 | +- [ ] **Step 5: Wire emission** in `docs-file.ts` to also emit template pages. |
| 104 | +- [ ] **Step 6: Run → PASS** the golden + full `bun test packages/codegen-ts`. |
| 105 | +- [ ] **Step 7: Commit.** `feat(docs): neutral template.output documentation page (render contract)` |
| 106 | + |
| 107 | +--- |
| 108 | + |
| 109 | +## Task 4: `meta docs` standalone command |
| 110 | + |
| 111 | +**Intent:** A standalone CLI command that emits neutral metadata docs (entity + |
| 112 | +template pages) from metadata ALONE — no codegen config, no Node toolchain for |
| 113 | +the adopter (ships in the compiled binary, like `migrate-ts`). |
| 114 | + |
| 115 | +**Files:** |
| 116 | +- Create: `server/typescript/packages/cli/src/commands/docs.ts` — `meta docs <metadata> --out <dir>`: load the metadata root (mirror `migrate.ts` loading), build a minimal render context (project provider + framework templates), emit entity pages (neutral) + template pages into `--out`. No dependency on a full `gen` config. |
| 117 | +- Modify: the CLI command registry (where `gen`/`migrate`/`verify` register) to add `docs`. |
| 118 | +- Ensure the standalone `bun build --compile` binary includes the command (verify it imports cleanly — the framework-provider lazy-resolves templates; the binary has no on-disk `templates/`, so the command must surface a clear error if templates can't resolve, OR embed them — match how `gen` handles templates in the binary). |
| 119 | +- Test: `server/typescript/packages/cli/test/docs-command.test.ts` — run `docs` against a fixture metadata file → asserts `<Entity>.md` + `<Template>.md` written to `--out`, neutral content, exits 0; appears in `--help`. |
| 120 | + |
| 121 | +- [ ] **Step 1: Write the command test** (emits both page types from a metadata fixture to a temp `--out`; no gen config). Run → FAIL. |
| 122 | +- [ ] **Step 2: Implement `docs.ts`** mirroring `migrate.ts`'s metadata loading; reuse the docs + template-doc builders + `render()`. |
| 123 | +- [ ] **Step 3: Register** the command; add `--help` text. |
| 124 | +- [ ] **Step 4: Run → PASS** the command test. Verify `meta docs --help` lists it. |
| 125 | +- [ ] **Step 5: Binary check** — build/locate the standalone binary path used by other CLI tests; confirm `docs` is reachable (or document the template-resolution constraint if the binary can't see on-disk templates, and handle gracefully). |
| 126 | +- [ ] **Step 6: Commit.** `feat(cli): meta docs — standalone neutral metadata-docs command` |
| 127 | + |
| 128 | +--- |
| 129 | + |
| 130 | +## Task 5: Closeout |
| 131 | + |
| 132 | +- [ ] **Step 1:** Full TS suites green: `cd server/typescript && bun test` (metadata, render, codegen-ts, cli). Record counts. |
| 133 | +- [ ] **Step 2:** Hygiene — `git diff $(git merge-base origin/main HEAD)..HEAD` shows no private names, no absolute home paths, no committed node_modules/bunfig.toml. The new `templates/` + fixtures use generic names. |
| 134 | +- [ ] **Step 3:** Whole-branch code-review + code-simplifier (the pre-merge gate); fix findings. |
| 135 | +- [ ] **Step 4:** Forward-merge to main (FF/merge onto current origin/main tip, never rewrite), push, remove worktree, delete branch. Update memory. |
| 136 | + |
| 137 | +--- |
| 138 | + |
| 139 | +## Self-review notes |
| 140 | + |
| 141 | +- **Spec coverage:** shared canonical (T1) ✓; neutral entity page incl. constraints table + drop Zod/generated (T2) ✓; neutral template page (T3) ✓; `meta docs` standalone (T4) ✓; byte-identity gate (T1) ✓; neutrality assertion (T2) ✓. |
| 142 | +- **Type consistency:** `constraints` shape introduced in T2 `docs-data.ts` is consumed by the T2 template edit; `TemplateDocData` in T3 is consumed by the T3 template + the T4 command. |
| 143 | +- **Out-of-scope guard restated:** no per-port port of the builder; no native-codegen changes; no SDK docs. |
0 commit comments