Skip to content

feat(cli): hand over every generator and drop the docs command - #439

Merged
dmealing merged 9 commits into
mainfrom
fm/mo-1-1-0-eject-ts
Oct 14, 2026
Merged

dmealing merged 9 commits into
mainfrom
fm/mo-1-1-0-eject-ts

Conversation

@dmealing

@dmealing dmealing commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

What Changed

  • Removes the meta docs command and its docs-drift verify path. Docs now come from generator entries the config declares (docsFile, apiDocsFile, agentDocsFile, requirementsFile, docs-site). meta gen emits only what the config lists.
  • meta eject copies the full template closure of a TypeScript reference generator into the repo (server/typescript/packages/cli/src/lib/template-eject.ts), so the ejected copy owns the code that writes its output. Adds docs-site ejection and eject-contract and owned-copy tests.
  • Adds the codegen-ts engine surface (engine.ts, engine-surface.ts, docs-run.ts, tph.ts, reference/requirements.ts, reference/agent-docs.ts), moves test-generators sources under src/generators/, and updates the React/TanStack reference generators and the agent skill and context templates to the new generator model.

Sequencing: the C# and Python half of ADR-0034 Amendment 9 (the sibling change) lands before rc.3 and the 1.1.0 cut, and nothing is released from main in between. The shipped agent context in this PR describes 1.1.0 as it will ship, all five ports included; the api-docs note in fixtures/generator-registry-conformance/registry.json describes the current tree, and the sibling change updates it.

Validation runs: 1 no-mistakes run (review fixes folded into its rounds).

Net hand-written line change (vs main, both commits): code +3,169 / −3,098 (net +71; 221 added lines are moved lines), tests +2,590 / −3,704 (net −1,114), docs +943 / −510 (net +433). Not hand-written, counted separately: owned template copies written by scripts/sync-owned-template-copies.ts +16,022 / −177, and regenerated fixtures and snapshots +510 / −430.

Validation

Risk Assessment

⚠️ Medium: A large refactor that removes a CLI command and changes how docs, eject and the runner work, but the code paths hang together and are gated by byte-identical and compile contracts; the one new finding is a test that bypasses the runner and carries a stale comment, and the C#/Python wording issue the user already ruled on still stands.

Testing

Built the CLI and drove a disposable project end-to-end: eject of entity, docs and agent-docs generators, meta gen, and verify --codegen. Verify caught the ejected-generator behavior edit and the agent/schema.md drift, and passed the git-ignored-docs fresh-clone case with exit 0. Ran the TypeScript, C#, and Java registry and eject suites, all green. Python plan step and live skill evaluation were not run.

  • Live validation: ✅ go - 5 of 8 scenarios driven live against the product
Scenario Result Live Evidence
Eject TS generators into a project and gen from the local copies (entity, docs, agent-docs) ✅ pass live Disposable project /tmp/nm-eject-proj; meta eject + meta gen wrote src/generated/, docs/generated/
Edit an ejected entity generator's behavior -> verify --codegen fails and names the new file ✅ pass live /tmp/nm-v7.log: exit 1, '+ src/generated/Marker.ts (regen would emit it; not committed)'; restore -> exit 0
Change a column without regen -> verify --codegen fails naming agent/schema.md (review-2) ✅ pass live /tmp/nm-v2.log: exit 1, '~ docs/generated/agent/schema.md'
Fresh clone with git-ignored docs dir absent on disk -> verify passes (review-1 round 7) ✅ pass live verify exit 0, message names docs/generated as not compared
Clean project verify --codegen exits 0 ✅ pass live clean_exit=0 after commit
Registry and eject contract suites for C# and Java over shared registry.json ⏸️ untested no Unit and conformance suites ran (dotnet 32 passed; surefire 27 tests, 0 failures), but the product CLI was not driven live for this scenario
Python registry and eject contract suites ⏸️ untested no No server/python/.venv in the worktree; building one was not attempted. Run: cd server/python && .venv/bin/python -m pytest -q tests/conformance -k "eject or generator_registry"
New skill revisions exercised by application-building agent tests ⏸️ untested no Requires LLM-driven agent runs that carry metered or quota spend; not approved in this session. Needs Doug's explicit approval with a cost estimate before running.

Pipeline

Updates from git push no-mistakes

✅ **Intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 2 issues (1 warning, 1 info)
  • ⚠️ docs/features/migrations/docs-are-generators.md:76 - The TypeScript migration section says the 1.1 entries "write the same pages to the same paths", but it leaves out the first-run refusal every upgrading project will hit. 1.0 meta docs wrote pages with a raw writeFile and never recorded them in .metaobjects/.gen-state/.hashes.json (old docs.ts:770/982). In 1.1 those pages go through decideAndWrite. An existing file with no hash record whose content differs from fresh output is no-snapshot-refused (overwrite-policy.ts:541-558). This change itself alters the bytes of two page sets: every metamodel page's header (REGEN_COMMAND in metadata/src/metamodel-docs/render.ts:27, visible in all 19 fixtures/metamodel-docs and site-reference files) and every docs-site page's footer (the date stamp is now ""). Concrete sequence: a 1.0 project that committed docs/generated (with --site) and docs/metamodel follows this section and runs meta gen. Every site page and every metamodel page is refused with a per-file warning. meta verify --codegen then reports each as ~ drift, because there is no recorded hash and the check fails closed. Nothing in the guide tells the agent how to get out of this. Fix: add an explicit step to the section, "delete the 1.0 docs output directories before the first meta gen", or run meta gen --generators <the docs names> --baseline=fresh. The --generators narrowing keeps the fresh baseline off code files. Also soften the "same pages" sentence at line 76.
  • ⚠️ server/typescript/packages/codegen-ts/src/runner.ts:756 - A 1.0 owned docs or api-docs copy, moved into generators as the guide says (docs-are-generators.md:142 and :169-171, 'the owned copy, in generators, like any other owned copy'), breaks the new rule that documentation is never written into the code's output directory by default. The 1.0 references (base reference/docs.ts:80-94 and reference/api-docs.ts:61-75) set no target, carry docsSurface, and read ctx.docsDoor, which no longer exists. The runner used to skip a docsSurface generator with a warning. It now runs it on the default target: meta gen writes README.md and the model pages into the code outDir (for example src/generated), and the api pages under src/generated/docs/api, with no cross-links. It does this silently. meta eject docs without --force keeps the old copy and only prints a DIFFERS note. Fix at the earliest shared boundary: in runGen, before the generator loop, refuse any declared generator that still carries the obsolete docsSurface property, and name meta eject <name> --force as the remedy. That covers meta gen, verify --codegen and programmatic callers alike, just as the legacy docs block is refused. Also change the guide's 'A generator you own' paragraph to say a 1.0 copy must be re-ejected, not moved.
  • ⚠️ server/typescript/packages/cli/src/lib/catalog-listing.ts:226 - meta gen --list --probe runs each generator through runGen without the run inputs that meta gen now supplies to the new documentation generators. probeOne's runGen call (catalog-listing.ts:226-235) receives neither sourceRoots nor agentSchema, and the probe option assembled in gen.ts:510 carries only metadata, scope and sourceFiles. Result 1: the new docs-site row reports a probe failure on every project. docsSiteFile() throws 'the run named no metadata source directories (ctx.sourceRoots)' (docs-site-file.ts:53-59), and the docs-target retry does not cure it. Result 2: the agent-docs count always leaves out agent/schema.md, because ctx.docs.agentSchema is undefined. Both understate what meta gen would emit. Fix: thread sourceRoots: sourceRootsOf(genCollection) and the same buildAgentSchemaInput thunk through CatalogListingOpts.probe into probeOne, as gen.ts:244-251 does.
  • ⚠️ agent-context/templates/always-on.md.mustache:62 - The shipped agent context now describes the C# and Python halves of Amendment 9 as done, but this tree does not implement them. The skill text says there is "no separate docs command or docs config block on any port", that codegen/Generators.cs is "scaffolded by dotnet meta init", and that --generators (TypeScript, C#, Python) only narrows a declared list. The same claims appear in agent-context/servers/csharp.meta.json and python.meta.json (docsCommand dotnet meta gen / metaobjects gen, 'Declare ApiDocsGenerator in codegen/Generators.cs'), skills/metaobjects-codegen/references/csharp.md ("There is no dotnet meta docs command (removed in 1.1.0)"), references/owning-generators.md, audit/references/csharp.md, and AGENTS.md ('documentation a set of generators in every port … the docs commands are removed'). In this tree, C# still dispatches "docs" => RunDocs (server/csharp/MetaObjects.Cli/Program.cs:75) and no Generators.cs exists anywhere under server/csharp. Python still has the docs subcommand (server/python/src/metaobjects/cli.py:2773), which reads docs.generators. This change's own registry.json note for api-docs says 'C# and Python still run it from the port's docs command … and their gen commands skip it'. A C# or Python adopter who follows the shipped agent context gets told to use a file and behaviour that do not exist. Decide the sequencing: either the C# and Python Amendment 9 changes must land before 1.1.0 is cut (and this PR depends on them), or the C#/Python wording here should describe the current state until they land.
  • ℹ️ server/typescript/packages/test-generators/src/index.ts:9 - Stale pointer. The comment says the copies are byte-gated against readReferenceTemplate() by test/owned-copies-current.test.ts. This change deleted that test from test-generators. The gate is now server/typescript/packages/cli/test/owned-copies-current.test.ts, and it compares against the eject closure (generator plus templates/), not readReferenceTemplate(). Update the comment to name the new location and the new comparison.

🔧 Fix applied.
3 warnings still open:

  • ⚠️ agent-context/templates/always-on.md.mustache:62 - The shipped agent context now describes the C# and Python halves of Amendment 9 as done, but this tree does not implement them. The skill text says there is "no separate docs command or docs config block on any port", that codegen/Generators.cs is "scaffolded by dotnet meta init", and that --generators (TypeScript, C#, Python) only narrows a declared list. The same claims appear in agent-context/servers/csharp.meta.json and python.meta.json (docsCommand dotnet meta gen / metaobjects gen, 'Declare ApiDocsGenerator in codegen/Generators.cs'), skills/metaobjects-codegen/references/csharp.md ("There is no dotnet meta docs command (removed in 1.1.0)"), references/owning-generators.md, audit/references/csharp.md, and AGENTS.md ('documentation a set of generators in every port … the docs commands are removed'). In this tree, C# still dispatches "docs" => RunDocs (server/csharp/MetaObjects.Cli/Program.cs:75) and no Generators.cs exists anywhere under server/csharp. Python still has the docs subcommand (server/python/src/metaobjects/cli.py:2773), which reads docs.generators. This change's own registry.json note for api-docs says 'C# and Python still run it from the port's docs command … and their gen commands skip it'. A C# or Python adopter who follows the shipped agent context gets told to use a file and behaviour that do not exist. Decide the sequencing: either the C# and Python Amendment 9 changes must land before 1.1.0 is cut (and this PR depends on them), or the C#/Python wording here should describe the current state until they land.
  • ⚠️ server/typescript/packages/cli/src/lib/codegen-drift.ts:299 - meta verify --codegen now checks documentation pages, so a project that generates docs but does not commit them fails the gate. When generators lists a docs generator and git ignores the whole docs output directory, computeCodegenDrift returns a hard error (exit 2): 'the repository git-ignores every one of them, so there is nothing to compare'. The test gen-docs-generators.test.ts:305-311 asserts this exit 2. In 1.0 the same refusal existed only behind the opt-in meta verify --docs, and verify --codegen never looked at documentation. Concrete sequence: a 1.0 project runs meta docs locally with docs/generated/ in .gitignore, and its CI runs meta verify --codegen. It follows docs-are-generators.md and moves its docs into generators with targets.docs. Its CI then exits 2 on every run. The guide's 'Things that are different' and 'Scripts and CI' sections do not mention this. The error text offers only two remedies: commit the pages, or stop generating docs. No option lets a project keep generating docs locally without committing them and still pass verify --codegen. This is a product decision. Options: (a) treat a docs directory that is ignored as a whole as 'not checked' and print the count line already used for partly ignored directories, without failing; (b) keep the refusal and document it in the migration section, with the exact way out; (c) add a narrowing option. The remedy changes gate behaviour, so it needs your decision.
  • ⚠️ docs/features/own-your-codegen.md:639 - The TypeScript row of the per-port 'own your codegen' table still says "docs, api-docs, requirements and agent-docs are run by meta docs, which runs a copy listed in docs: { generators: [...] }". This change removes meta docs and the docs block for TypeScript. Section 224-260 of the same file and docs-are-generators.md now say these generators are listed in generators and run by meta gen. An agent or adopter who reads this table is sent to a command that exits with the removal message, and to a config block that meta gen refuses (legacyDocsConfigMessage). Fix: replace the sentence with the 1.1 wiring. The docs generators, metamodel-docs and docs-site included, are entries in generators that write to the docs target and are run by meta gen. Keep the existing link to #the-docs-generators-and-shared-model-typescript. The C# rows at 1435-1462 describe dotnet meta docs, which still exists in this tree. Leave them as they are, per the review-4 decision.

🔧 Fix applied.
2 warnings still open:

  • ⚠️ agent-context/templates/always-on.md.mustache:62 - The shipped agent context now describes the C# and Python halves of Amendment 9 as done, but this tree does not implement them. The skill text says there is "no separate docs command or docs config block on any port", that codegen/Generators.cs is "scaffolded by dotnet meta init", and that --generators (TypeScript, C#, Python) only narrows a declared list. The same claims appear in agent-context/servers/csharp.meta.json and python.meta.json (docsCommand dotnet meta gen / metaobjects gen, 'Declare ApiDocsGenerator in codegen/Generators.cs'), skills/metaobjects-codegen/references/csharp.md ("There is no dotnet meta docs command (removed in 1.1.0)"), references/owning-generators.md, audit/references/csharp.md, and AGENTS.md ('documentation a set of generators in every port … the docs commands are removed'). In this tree, C# still dispatches "docs" => RunDocs (server/csharp/MetaObjects.Cli/Program.cs:75) and no Generators.cs exists anywhere under server/csharp. Python still has the docs subcommand (server/python/src/metaobjects/cli.py:2773), which reads docs.generators. This change's own registry.json note for api-docs says 'C# and Python still run it from the port's docs command … and their gen commands skip it'. A C# or Python adopter who follows the shipped agent context gets told to use a file and behaviour that do not exist. Decide the sequencing: either the C# and Python Amendment 9 changes must land before 1.1.0 is cut (and this PR depends on them), or the C#/Python wording here should describe the current state until they land.
  • ⚠️ agent-context/skills/metaobjects-codegen/references/typescript-docs.md:68 - The TypeScript docs skill tells an agent to read ./docs/api/AGENT-API.md before it calls generated code. In 1.1.0 that path does not exist for the configuration the same file recommends. The file's own example (line 36) sets targets: { docs: { outDir: "./docs/generated" } }, and apiDocsFile() writes under api/ in that target (DEFAULT_API_DIR = "api", target "docs"). The page therefore lands at ./docs/generated/api/AGENT-API.md, or more generally at <targets.docs.outDir>/api/AGENT-API.md. An agent that follows the skill looks in the wrong place, finds nothing, and either guesses the generated API or regenerates without need. The always-on template avoids this: it names the page relative to the docs target ({{apiSubDir}}/AGENT-API.md under targets.docs.outDir). The wrong path predates this change, but this change rewrote the file around the docs target, so the file now contradicts itself. The user named the skill revisions as a priority. Fix: say <targets.docs.outDir>/api/AGENT-API.md (e.g. ./docs/generated/api/AGENT-API.md). Same defect at line 10 of the same file (the Contents bullet). After the edit, regenerate the byte-identical fixture copies: fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/typescript-docs.md and fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/references/typescript-docs.md.

🔧 Fix applied.
3 warnings still open:

  • ⚠️ agent-context/templates/always-on.md.mustache:62 - The shipped agent context now describes the C# and Python halves of Amendment 9 as done, but this tree does not implement them. The skill text says there is "no separate docs command or docs config block on any port", that codegen/Generators.cs is "scaffolded by dotnet meta init", and that --generators (TypeScript, C#, Python) only narrows a declared list. The same claims appear in agent-context/servers/csharp.meta.json and python.meta.json (docsCommand dotnet meta gen / metaobjects gen, 'Declare ApiDocsGenerator in codegen/Generators.cs'), skills/metaobjects-codegen/references/csharp.md ("There is no dotnet meta docs command (removed in 1.1.0)"), references/owning-generators.md, audit/references/csharp.md, and AGENTS.md ('documentation a set of generators in every port … the docs commands are removed'). In this tree, C# still dispatches "docs" => RunDocs (server/csharp/MetaObjects.Cli/Program.cs:75) and no Generators.cs exists anywhere under server/csharp. Python still has the docs subcommand (server/python/src/metaobjects/cli.py:2773), which reads docs.generators. This change's own registry.json note for api-docs says 'C# and Python still run it from the port's docs command … and their gen commands skip it'. A C# or Python adopter who follows the shipped agent context gets told to use a file and behaviour that do not exist. Decide the sequencing: either the C# and Python Amendment 9 changes must land before 1.1.0 is cut (and this PR depends on them), or the C#/Python wording here should describe the current state until they land.
  • ⚠️ docs/features/own-your-codegen.md:361 - This change empties eject.pending in fixtures/generator-registry-conformance/registry.json (it is now []). The CHANGELOG 1.1.0 entry records that closure. The section 'What meta eject hands over, and what stays core (TypeScript)' was not updated and still says: "It is not yet true of all of them: several owned copies still call emit code in the package, and fixtures/generator-registry-conformance/registry.json lists each one under eject.pending until it is closed. What follows is the part that is already handed over." For TypeScript this is now false. A reader, or an agent fed llms-full.txt, will conclude that the Amendment 8 hand-over is still incomplete and will look for a pending list that is empty. Fix: replace the sentence with the current state. Every ejectable TypeScript generator hands over its template-module closure into codegen/templates/, and only the engine (ENGINE_MODULES, imported from @metaobjectsdev/codegen-ts/engine) stays packaged. Then regenerate docs/llms/llms-full.txt if it embeds this page.
  • ⚠️ agent-context/skills/metaobjects-codegen/references/typescript.md:129 - This change rewrote this paragraph of the shipped TypeScript codegen skill. It now says "One entry cannot be ejected and is package-only in meta gen --list: the template primitive". That count is wrong. docs-site is also not ejectable: it is absent from REFERENCE_GENERATOR_NAMES in reference-templates.ts, so ejectable("docs-site") is false (generator-registry.ts:347). Its registry note says the builder stays packaged and meta eject docs-site copies only the site's templates and assets. docs/features/own-your-codegen.md and docs/features/cli.md both say two entries (template and docs-site) are package-only. A second wrong sentence sits in the same paragraph: "The engine primitives come from the package main entry, @metaobjectsdev/codegen-ts". An ejected copy now imports the engine from @metaobjectsdev/codegen-ts/engine (template-eject.ts ENGINE_ENTRY; every reference under src/reference imports that entry). Fix: name both package-only entries, and say that owned copies import the engine from @metaobjectsdev/codegen-ts/engine. Then regenerate the byte-identical fixture copies with the sdk regen script: fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/typescript.md and fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/references/typescript.md.

🔧 Fix applied.
2 warnings still open:

  • ⚠️ agent-context/templates/always-on.md.mustache:62 - The shipped agent context now describes the C# and Python halves of Amendment 9 as done, but this tree does not implement them. The skill text says there is "no separate docs command or docs config block on any port", that codegen/Generators.cs is "scaffolded by dotnet meta init", and that --generators (TypeScript, C#, Python) only narrows a declared list. The same claims appear in agent-context/servers/csharp.meta.json and python.meta.json (docsCommand dotnet meta gen / metaobjects gen, 'Declare ApiDocsGenerator in codegen/Generators.cs'), skills/metaobjects-codegen/references/csharp.md ("There is no dotnet meta docs command (removed in 1.1.0)"), references/owning-generators.md, audit/references/csharp.md, and AGENTS.md ('documentation a set of generators in every port … the docs commands are removed'). In this tree, C# still dispatches "docs" => RunDocs (server/csharp/MetaObjects.Cli/Program.cs:75) and no Generators.cs exists anywhere under server/csharp. Python still has the docs subcommand (server/python/src/metaobjects/cli.py:2773), which reads docs.generators. This change's own registry.json note for api-docs says 'C# and Python still run it from the port's docs command … and their gen commands skip it'. A C# or Python adopter who follows the shipped agent context gets told to use a file and behaviour that do not exist. Decide the sequencing: either the C# and Python Amendment 9 changes must land before 1.1.0 is cut (and this PR depends on them), or the C#/Python wording here should describe the current state until they land.
  • ⚠️ AGENTS.md:9 - Round 4's fix for review-8 corrected the TypeScript package-only count in own-your-codegen.md, the CHANGELOG and the skill copies, but missed two sibling sites. (1) AGENTS.md:9 still says "meta gen --list marks the ones that do not package-only (in TypeScript: template, which is exempt for good, and requirements and agent-docs, which are pending ...)". This change made requirements and agent-docs ejectable: both are in REFERENCE_GENERATOR_NAMES, and registry.json eject.pending is []. The only package-only TypeScript entries are now template and docs-site, as eject-templates-contract.test.ts:82 asserts. AGENTS.md is loaded into every agent session, so this false statement reaches every future session. (2) server/typescript/packages/codegen-ts/src/generators/index.ts:17, rewritten in this change, says "today that is template alone", and the next sentence says the docsSiteFile builder stays packaged, so the comment contradicts itself. Fix both to name template (the Mustache primitive) and docs-site (its builder stays packaged; meta eject docs-site copies the theme), and to drop the 'pending' wording. The AGENTS.md edit is a fix to memory-file content that is part of this change. Separately, CHANGELOG.md:218 says the contract test ejects "each of the 24 generators", but the composed catalog has 25 ejectable names (21 core, 1 react, 3 tanstack). Check that number while making these edits.

🔧 Fix applied.
4 issues (3 warnings, 1 info) still open:

  • ⚠️ agent-context/templates/always-on.md.mustache:62 - The shipped agent context now describes the C# and Python halves of Amendment 9 as done, but this tree does not implement them. The skill text says there is "no separate docs command or docs config block on any port", that codegen/Generators.cs is "scaffolded by dotnet meta init", and that --generators (TypeScript, C#, Python) only narrows a declared list. The same claims appear in agent-context/servers/csharp.meta.json and python.meta.json (docsCommand dotnet meta gen / metaobjects gen, 'Declare ApiDocsGenerator in codegen/Generators.cs'), skills/metaobjects-codegen/references/csharp.md ("There is no dotnet meta docs command (removed in 1.1.0)"), references/owning-generators.md, audit/references/csharp.md, and AGENTS.md ('documentation a set of generators in every port … the docs commands are removed'). In this tree, C# still dispatches "docs" => RunDocs (server/csharp/MetaObjects.Cli/Program.cs:75) and no Generators.cs exists anywhere under server/csharp. Python still has the docs subcommand (server/python/src/metaobjects/cli.py:2773), which reads docs.generators. This change's own registry.json note for api-docs says 'C# and Python still run it from the port's docs command … and their gen commands skip it'. A C# or Python adopter who follows the shipped agent context gets told to use a file and behaviour that do not exist. Decide the sequencing: either the C# and Python Amendment 9 changes must land before 1.1.0 is cut (and this PR depends on them), or the C#/Python wording here should describe the current state until they land.
  • ⚠️ server/typescript/packages/codegen-ts/src/generators/docs-file.ts:118 - Two places this change added say the model pages follow meta gen's scope. CHANGELOG.md:936 says: "The model pages follow meta gen's scope and entity selection, as every per-object generator does". server/typescript/packages/cli/README.md:358 says the model pages (docsFile()) follow the scope. The code does not do this. docsFile() builds its pages from ctx.loadedRoot.objects().filter(ctx.matches) (docs-file.ts:118-120). runner.ts:804 sets matches: (e) => generator.filter?.(e) ?? true, which reads only the generator's own filter and never reads scope or the meta gen <Entity> selection. Only ctx.entities and ctx.select carry the scope. Concrete case: a project whose config has scope: { include: ["app::core::**"] } and declares docsFile(). meta gen writes a model page and an index entry for every object in the collection, including objects outside app::core. The CHANGELOG and README tell the user these pages are scoped, and no error appears. meta verify --codegen regenerates the same way, so no drift is reported either. This needs a product decision. Either correct both texts to say the model pages document the whole loaded model, like the other docs generators, or make docsFile() honour the scope (filter by ctx.select and the entity selection, or walk ctx.entities). The second option changes the pages written for scoped projects.
  • ⚠️ server/typescript/packages/cli/test/gen-docs-generators.test.ts:283 - This change deleted cli/test/integration/verify-docs-drift.test.ts. That test ran the drift gate end to end against examples/showcase with a real migrate-ts expected-schema snapshot. It was the only test that checked agent/schema.md that way, and it showed the gate failing in three distinct ways. The replacement case at gen-docs-generators.test.ts:283-296 uses a synthetic one-entity fixture. It wires only docsFile() and never wires agentDocsFile(), so it never reads a migrate-ts schema. examples/showcase/metaobjects.config.ts declares no docs generator, so showcase-drift.test.ts does not cover this either. Observable failure that no test now catches: a project wires agentDocsFile() and runs meta gen, then changes a column in its metadata without regenerating. If meta verify --codegen then fails to report agent/schema.md as drifted, the suite stays green. Independent oracle: agent/schema.md is generated from the expected schema, so a changed column must change its bytes, and the gate must exit non-zero and name that file. Fix: add an integration case that wires agentDocsFile() on a project with a persisted entity, runs meta gen, changes a column, and asserts that meta verify --codegen fails and names agent/schema.md. A related loss: the deleted integration/eject-docs-tier.test.ts ran the built CLI to check that an edited, ejected docs generator is reported as drift. The replacement at gen-docs-generators.test.ts:325-345 runs in-process and checks only that an unedited ejected copy writes the same output. verify-codegen-ejected-generator.test.ts still covers the generic edit-is-drift mechanism.
  • ℹ️ server/typescript/packages/codegen-ts/src/generator-registry.ts:290 - This change added the options string "subDir?, modelBaseUrl?, filter?, target?" to the api-docs catalog entry. It omits modelSurface?, which ApiDocsFileOpts declares (api-docs-file.ts:65). Under Amendment 9, modelSurface is the explicit override for the new default, which comes from ctx.docs.modelPages (api-docs-file.ts:81). meta gen --list prints this string. An agent that reads the catalog to discover apiDocsFile()'s options is told about four of the five and does not learn of this override. Fix: add modelSurface? to the options string.

🔧 Fix applied.
2 warnings still open:

  • ⚠️ agent-context/templates/always-on.md.mustache:62 - The shipped agent context now describes the C# and Python halves of Amendment 9 as done, but this tree does not implement them. The skill text says there is "no separate docs command or docs config block on any port", that codegen/Generators.cs is "scaffolded by dotnet meta init", and that --generators (TypeScript, C#, Python) only narrows a declared list. The same claims appear in agent-context/servers/csharp.meta.json and python.meta.json (docsCommand dotnet meta gen / metaobjects gen, 'Declare ApiDocsGenerator in codegen/Generators.cs'), skills/metaobjects-codegen/references/csharp.md ("There is no dotnet meta docs command (removed in 1.1.0)"), references/owning-generators.md, audit/references/csharp.md, and AGENTS.md ('documentation a set of generators in every port … the docs commands are removed'). In this tree, C# still dispatches "docs" => RunDocs (server/csharp/MetaObjects.Cli/Program.cs:75) and no Generators.cs exists anywhere under server/csharp. Python still has the docs subcommand (server/python/src/metaobjects/cli.py:2773), which reads docs.generators. This change's own registry.json note for api-docs says 'C# and Python still run it from the port's docs command … and their gen commands skip it'. A C# or Python adopter who follows the shipped agent context gets told to use a file and behaviour that do not exist. Decide the sequencing: either the C# and Python Amendment 9 changes must land before 1.1.0 is cut (and this PR depends on them), or the C#/Python wording here should describe the current state until they land.
  • ⚠️ server/typescript/packages/cli/src/lib/codegen-drift.ts:295 - verify --codegen fails on a fresh clone of a project that git-ignores its whole docs directory. Prior fix round 5d154e8 ("Skip git-ignored docs dirs in verify") introduced this code. It asks git about ignored pages with gitIgnored(committed, …), and gitIgnored runs git -C <committed> check-ignore (git-ignore.ts:69). committedOutDirs (codegen-drift.ts:131) returns every declared outDir, including ones that do not exist on disk. Failing sequence: a project lists docsFile() with targets.docs.outDir: "./docs/generated" and has docs/ in .gitignore, so it commits no pages. A CI runner clones the repository, so docs/generated is absent. listFiles(committed) returns [], the fresh run emits pages, and all.size > 0 calls gitIgnored. git -C <missing dir> fails with "cannot change to" (exit 128), so the lookup returns unavailable and ignored stays empty. The run then skips the docsIgnoredDirs branch, reports every page as + … (regen would emit it; not committed), and exits 1. The same project passes on the developer's machine, where meta gen created the directory. The code comment at lines 287-292 and the docsIgnoredDirs doc both promise the opposite: a verdict that "is a property of the repository and not of the machine", and "a project that generates its documentation without committing it passes". The existing test (gen-docs-generators.test.ts:321, "a docs directory the repository ignores whole") runs meta gen first, so the directory exists and the test cannot catch this. Fix: run the lookup from the project root, which always exists, and pass root-relative paths (relative(root, join(committed, rel))). Then map the results back to the dir-relative keys that ignored.has(toPosix(rel)) checks. Add a case that deletes docs/generated after meta gen and expects verify --codegen to exit 0 and name the ignored directory.

🔧 Fix applied.
2 issues (1 warning, 1 info) still open:

  • ⚠️ agent-context/templates/always-on.md.mustache:62 - The shipped agent context now describes the C# and Python halves of Amendment 9 as done, but this tree does not implement them. The skill text says there is "no separate docs command or docs config block on any port", that codegen/Generators.cs is "scaffolded by dotnet meta init", and that --generators (TypeScript, C#, Python) only narrows a declared list. The same claims appear in agent-context/servers/csharp.meta.json and python.meta.json (docsCommand dotnet meta gen / metaobjects gen, 'Declare ApiDocsGenerator in codegen/Generators.cs'), skills/metaobjects-codegen/references/csharp.md ("There is no dotnet meta docs command (removed in 1.1.0)"), references/owning-generators.md, audit/references/csharp.md, and AGENTS.md ('documentation a set of generators in every port … the docs commands are removed'). In this tree, C# still dispatches "docs" => RunDocs (server/csharp/MetaObjects.Cli/Program.cs:75) and no Generators.cs exists anywhere under server/csharp. Python still has the docs subcommand (server/python/src/metaobjects/cli.py:2773), which reads docs.generators. This change's own registry.json note for api-docs says 'C# and Python still run it from the port's docs command … and their gen commands skip it'. A C# or Python adopter who follows the shipped agent context gets told to use a file and behaviour that do not exist. Decide the sequencing: either the C# and Python Amendment 9 changes must land before 1.1.0 is cut (and this PR depends on them), or the C#/Python wording here should describe the current state until they land.
  • ℹ️ server/typescript/packages/codegen-ts/test/agent-ui-page-parity.test.ts:358 - This branch changed the parity test. It no longer runs agentDocsFile() through runGen: the generator was taken out of the generators array and is now called directly with a context the test builds by hand (includeUiTier: runEmitsUiTier(suite) and a makeRenderContext). The comment added at lines 358-361 justifies this with "the runner skips a docs generator, so it is generated the way that door does … meta docs … cli/src/commands/docs.ts". After this change none of that is true. The runner runs docs generators (ADR-0034 Amendment 9), and cli/src/commands/docs.ts is now a two-line stub that exits 2. The comment tells the next reader the opposite of how the system works. The test also checks the agent page against a context it built itself, not against the one runGen builds (ctx.config.includeUiTier, renderContext.apiPrefix). Its parity claim ("what meta gen would have written") therefore no longer covers the agent page. gen-docs-generators.test.ts:214 partly covers apiPrefix through meta gen. Fix: put agentDocsFile() back in suite, add targets: { docs: { outDir } } (or pass target), read agent/ui.md from the runner's output, and delete the hand-built context and the stale comment.
✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 5 of 8 scenarios driven live against the product
Scenario Result Live Evidence
Eject TS generators into a project and gen from the local copies (entity, docs, agent-docs) ✅ pass live Disposable project /tmp/nm-eject-proj; meta eject + meta gen wrote src/generated/, docs/generated/
Edit an ejected entity generator's behavior -> verify --codegen fails and names the new file ✅ pass live /tmp/nm-v7.log: exit 1, '+ src/generated/Marker.ts (regen would emit it; not committed)'; restore -> exit 0
Change a column without regen -> verify --codegen fails naming agent/schema.md (review-2) ✅ pass live /tmp/nm-v2.log: exit 1, '~ docs/generated/agent/schema.md'
Fresh clone with git-ignored docs dir absent on disk -> verify passes (review-1 round 7) ✅ pass live verify exit 0, message names docs/generated as not compared
Clean project verify --codegen exits 0 ✅ pass live clean_exit=0 after commit
Registry and eject contract suites for C# and Java over shared registry.json ⏸️ untested no Unit and conformance suites ran (dotnet 32 passed; surefire 27 tests, 0 failures), but the product CLI was not driven live for this scenario
Python registry and eject contract suites ⏸️ untested no No server/python/.venv in the worktree; building one was not attempted. Run: cd server/python && .venv/bin/python -m pytest -q tests/conformance -k "eject or generator_registry"
New skill revisions exercised by application-building agent tests ⏸️ untested no Requires LLM-driven agent runs that carry metered or quota spend; not approved in this session. Needs Doug's explicit approval with a cost estimate before running.
  • scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains
  • bun run build in server/typescript/packages/cli
  • meta eject --list and meta gen --list against built CLI: 25 TS generators available, own 0
  • Disposable project /tmp/nm-eject-proj: meta init, meta eject entity docs agent-docs, meta gen, meta verify --codegen
  • Adversarial: behavior edit to ejected entity generator (adds Marker.ts) -> verify exit 1 naming src/generated/Marker.ts; restored -> exit 0
  • Adversarial: changed @maxLength with no regen -> verify exit 1 naming docs/generated/agent/schema.md
  • Fresh-clone case: docs/ git-ignored, docs dir deleted -> verify exit 0 with not-compared message
  • cd server/typescript/packages/sdk && bun test (333 pass)
  • cd server/typescript/packages/docs-site && bun test (56 pass)
  • cd server/typescript/packages/cli && bun test (1702 tests, 0 fail)
  • cd server/typescript/packages/codegen-ts && bun test (2645 pass, 0 fail)
  • cd server/csharp && dotnet test MetaObjects.Codegen.Tests --filter "FullyQualifiedName~GeneratorRegistry|FullyQualifiedName~EjectContract" (32 pass)
  • cd server/java && mvn -o -pl codegen-spring,codegen-kotlin,maven-plugin -am test -Dtest='GeneratorRegistryConformanceTest,EjectContractTest' (surefire: 27 tests, 0 failures)
  • Not run: plan step 6 Python pytest (no server/python/.venv present)
  • Not run: skill revisions via application-building agent tests (LLM spend, needs approval)
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…equirements and agent-docs ejectable

`meta eject <name>` used to copy a thin generator that decided what to emit and called the
package to write it. It now also copies the transitive closure of the template modules the
generator calls into `codegen/templates/`, the way it already copies the HTTP adapter, and the
owned generator imports them from there. The engine (generator base, model walks, naming and
type-mapping tables, projection and report lowering) stays in the package; `ENGINE_MODULES`
lists it and the shared eject manifest's engine surface carries the same list.

`requirements` and `agent-docs` get reference templates, are listed in `docs.generators`,
and `meta docs` runs an owned copy in place of the packaged one. `meta gen` skips the packaged
factory of either, as it does for `docsFile()`.

Every one of the 24 ejectable generators is ejected alone into an empty project by a test
derived from the catalog: nothing it holds imports a packaged emit module, it typechecks as an
adopter's project does, and it writes what the packaged generator writes over six models. The
TypeScript entries are gone from the manifest's pending list.

The repository's own owned copies (the test-generators package and the examples) carry their
template modules, written by scripts/sync-owned-template-copies.ts and held by a test.
The release checklist's assess block now fails the deploy.
… docs` is removed

ADR-0034 Amendment 9, the TypeScript half. Every page of documentation is an entry in
`generators`, `meta gen` runs the list, `meta verify --codegen` checks it, and nothing is
written that the list does not declare.

- `meta docs` and `meta verify --docs` print the amendment's removal line and exit 2. A
  config with a `docs` block fails `meta gen` and `meta verify --codegen` before writing
  anything; `meta gen` with no metaobjects.config.ts fails with a pointer to the migration
  section. `meta init` writes no docs block. A config that declares no generator writes
  nothing, tool state included.
- Docs factories write to the `docs` target and are refused by name when it is missing.
  New: `metamodelDocsFile()` (`metamodel-docs`, ejectable) and `docsSiteFile()`
  (`docs-site`); `meta eject docs-site` copies the site theme. The site prints no date, so
  its pages are checkable. The runner builds the model/api cross-link facts per target
  (`ctx.docs`) from `emitsModelDocs` / `emitsApiDocs`, and the agent schema on request.
- `meta gen --generators <names>` narrows a run to declared entries and refuses others; a
  narrowed run reads run-level facts from the whole list. `meta gen` now passes the routes
  `expose` facts to the api reference, which it never did.
- `meta verify --codegen` does not check documentation the repository git-ignores, says how
  many pages it skipped, and refuses a docs directory ignored whole (carried from
  `verify --docs`).
- Owned generators and templates import the engine from the new
  `@metaobjectsdev/codegen-ts/engine` entry; zodEnumExpr, the primary-key and TPH walks
  moved into engine/template modules with forwarders where they were exported.
- Removed from the package root: DocsConfig, ResolvedDocsConfig, DocsSurface,
  resolveDocsConfig. `apiDocsFile()` writes under `api/` by default.
- Docs: the TypeScript section of docs-are-generators.md, CHANGELOG breaking notice and
  entries, ADR-0034 Amendment 9 TypeScript cells corrected, cli.md, ports/typescript.md,
  own-your-codegen.md, and the shipped agent context for all five ports. AGENTS.md no
  longer says 1.1.0 is released.
@dmealing
dmealing merged commit c4a685c into main Oct 14, 2026
1 check passed
@dmealing
dmealing deleted the fm/mo-1-1-0-eject-ts branch October 14, 2026 19:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant