Skip to content

feat(cli): one generator list and one gen command for C# and Python - #440

Merged
dmealing merged 9 commits into
mainfrom
fm/mo-1-1-0-onelist-cs-py
Oct 14, 2026
Merged

dmealing merged 9 commits into
mainfrom
fm/mo-1-1-0-onelist-cs-py

Conversation

@dmealing

@dmealing dmealing commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

What Changed

  • Every generator, the api reference included, is now an entry in the project's native generator list. C# scaffolds it as codegen/Generators.cs, and codegen/Program.cs is a two-line entry that calls CodegenCli.Run(args, Generators.All). dotnet meta gen and verify --codegen run that list and nothing else. --generators only narrows the declared list and refuses names it does not declare. Python reads the same model through metaobjects gen, verify, and a top-level apiDocs block in metaobjects.config.yaml.
  • Removed the separate docs path: dotnet meta docs and metaobjects docs (and the docs marker and Python codegen/docs_run.py) are gone. Running docs now exits with a message that names gen and the migration section. A 1.0-shape codegen/Program.cs that does not run Generators.All, or a 1.0 --project/--model-base-url docs flag, makes gen fail with a clear error and write nothing.
  • Added the migration section in docs/features/migrations/docs-are-generators.md, with matching updates to the CLI, codegen, ports, and recipe docs, the agent-context skills, and their conformance fixtures. Recorded the 1.1.0 release notes in CHANGELOG.md.
Validation

Risk Assessment

⚠️ Medium: Round 5's fix is correct: in both gen and verify --codegen, IncludeNames and the requires advisory now come from the declared list, and the dead branch is gone. The regression test fails before the fix and passes after it. The branch is still a broad, intentionally breaking CLI change across two ports, so its residual risk sits above cosmetic.

Testing

Full regression passes: Python suite 3141 passed, C# Cli 291 passed, Codegen 841 passed with 1 skipped. Live Python and C# scenarios for generator-list behavior, narrowing identity, refusals, fresh-checkout verify, and drift detection all pass against the built tools in disposable projects under /tmp. Overall result is green.

  • Live validation: ✅ go - 11 of 11 scenarios driven live against the product
Scenario Result Live Evidence
Python: 1.0 docs-block config refused with migration line and nothing written ✅ pass live /tmp/mo-live-py run gen, exit 2
Python: target with generators [] writes nothing and says so ✅ pass live /tmp/mo-live-py gen exit 0
Python: generators [entity] writes only its files; verify in sync; hand edit detected ✅ pass live /tmp/mo-live-py gen/verify exit 0 then 1
Python: undeclared, blank, and unknown-entity narrowing refused ✅ pass live /tmp/mo-live-py exit 1, 2, 2
Python: narrowed gen output identical to full run ✅ pass live /tmp/mo-live-py diff -r identical
C#: init scaffold writes Generators.cs, Program.cs, Codegen.csproj ✅ pass live /tmp/mo-live-cs dotnet-meta init
C#: narrowed --generators entity gen byte-identical to full run ✅ pass live /tmp/mo-live-cs Subscriber.g.cs cmp identical
C#: undeclared and blank --generators refused, exit 2 ✅ pass live /tmp/mo-live-cs exit 2 each
C#: dotnet meta docs refused with migration line ✅ pass live /tmp/mo-live-cs docs exit 2
C#: OutDir-only list verifies clean on fresh checkout ✅ pass live /tmp/mo-live-cs verify --codegen exit 0
C#: hand-edit drift detected ✅ pass live /tmp/mo-live-cs verify exit 1

Pipeline

Updates from git push no-mistakes

✅ **Intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 4 issues found → auto-fixed (5) ✅
  • ⚠️ server/csharp/MetaObjects.Cli/InitCommand.cs:109 - Intent says: "gen fails clearly on a 1.0-shape config and never silently generates nothing". A 1.0 C# project that had ejected a generator already has codegen/Program.cs with an inline list (IReadOnlyList<IGenerator> generators = [...]; return CodegenCli.Run(args, generators);) and a Codegen.csproj pinned to 1.0.x. The failure sequence is: (1) dotnet meta gen refuses with 'no codegen/Generators.cs ... dotnet meta init writes it'. (2) The user runs dotnet meta init. It writes an empty Generators.cs, prints 'kept codegen/Program.cs (already yours)' and claims Generators.cs 'is the generator list dotnet meta gen runs'. (3) RequireGeneratorList (Program.cs:141) checks only that the two files exist, so gen hands off to the old Program.cs. The old Program.cs never references Generators.All. Every entry the user then adds to Generators.cs, such as the ApiDocsGenerator the migration tells them to add, is ignored without a word, and gen exits 0. With the old pin left in place, the 1.0 CodegenCli.Run runs instead, including its additive --generators and its docs command. init does not warn about an older pin, though eject does (EjectCommand.cs:246). The same false claim appears in eject's 'codegen/Generators.cs lists the generator(s) you just ejected ... dotnet meta gen ... run that list' (EjectCommand.cs:234) when only Generators.cs was missing. The migration doc says to replace Program.cs by hand, but no tool enforces it. Possible remedies: have init and eject reuse OlderPinWarning for an existing csproj, and have init, eject or RequireGeneratorList report or refuse a Program.cs that does not run Generators.All. Each remedy adds new detection or refusal behavior, so it needs a ruling.
  • ⚠️ server/python/src/metaobjects/cli.py:1758 - verify --codegen --generators <subset> now narrows the regen (selection passed at cli.py:1731), but the diff still covers every committed outDir in full. Example: config api: {outDir: gen, generators: [entity, routes]} and docs: {outDir: docs, generators: [api-docs]}. Run metaobjects gen, then metaobjects verify --codegen --generators api-docs. The api target regenerates nothing (cli.py:1380 skips it), so its temp slot is empty. Every committed entity and router file is in the manifest, so is_ours returns true and each one is reported as extra:. The gate fails with exit 1 on output that is in sync. --entities narrowing (_narrow_entities on the closure) has the same effect for other entities' files. C# has the same defect: CodegenDrift.Compute (server/csharp/MetaObjects.Codegen/CodegenDrift.cs:84) always adds the run's --out root and diffs it whole. dotnet meta verify --codegen --out Generated --generators api-docs, with api-docs on its own OutDir, therefore reports every committed code file as '- committed but a fresh regen would not emit it'. Suggested fix: when a selection or entity filter narrows the run, skip directories no selected entry wrote to, and suppress the extra/'-' verdict, because those files cannot be attributed to the narrowed set. Related: a C# entry OutDir nested inside --out (or the reverse) makes the outer root's whole-tree diff report the inner entry's files as '-' drift.
  • ⚠️ docs/ports/csharp.md:461 - Only ApiDocsGenerator overrides IGenerator.OutDir, so a C# list that includes RequirementTestsGenerator cannot give its xUnit output a directory of its own. The docs here and in agent-context/skills/metaobjects-codegen/references/csharp.md:146 tell users to run two narrowed commands: gen --out <tests> --generators requirement-tests, and the code run 'names its own entries the same way'. That is the 1.0 selection pattern the intent replaces ('one command runs the list'). A plain dotnet meta gen ./metaobjects --out Generated runs the whole list and writes the xUnit tests into the app's --out, which does not reference xunit, so that build breaks. A plain verify --codegen --out Generated regenerates them too, so after the two-run workflow it reports the missing test files as drift. Remedy options: give RequirementTestsGenerator (or the reference generators generally) an OutDir init property, as api-docs has, or accept and document the limitation. Either is a product decision, so it needs a ruling.
  • ℹ️ server/python/src/metaobjects/cli.py:2496 - Several verify help strings still describe the 1.0 flag/config split. verify --generators says it 'must MATCH the gen that produced --out ... with none named there is nothing to check'; it now narrows the declared list, and with none named the whole list is checked. verify --config says 'Config mode runs when no <metadata_dir> is given'. The verify metadata_dir help says 'Omit to use a metaobjects.config.yaml (--codegen config mode, Python: declarative codegen config with a targets registry (parity with metaobjects.config.ts) #267)'. verify --out says 'committed output directory to diff against', but _verify_codegen_config now refuses it. On gen, --templates still says it is 'used by --template-spec', but that flag is refused. A related gap: verify --codegen &lt;metadata_dir&gt; given without --out silently ignores the positional for the codegen gate, which loads the config's metadata, while gen refuses the same positional.

🔧 Fix applied.
10 issues (5 warnings, 5 infos) still open:

  • ⚠️ server/python/src/metaobjects/cli.py:1758 - verify --codegen --generators &lt;subset&gt; now narrows the regen (selection passed at cli.py:1731), but the diff still covers every committed outDir in full. Example: config api: {outDir: gen, generators: [entity, routes]} and docs: {outDir: docs, generators: [api-docs]}. Run metaobjects gen, then metaobjects verify --codegen --generators api-docs. The api target regenerates nothing (cli.py:1380 skips it), so its temp slot is empty. Every committed entity and router file is in the manifest, so is_ours returns true and each one is reported as extra:. The gate fails with exit 1 on output that is in sync. --entities narrowing (_narrow_entities on the closure) has the same effect for other entities' files. C# has the same defect: CodegenDrift.Compute (server/csharp/MetaObjects.Codegen/CodegenDrift.cs:84) always adds the run's --out root and diffs it whole. dotnet meta verify --codegen --out Generated --generators api-docs, with api-docs on its own OutDir, therefore reports every committed code file as '- committed but a fresh regen would not emit it'. Suggested fix: when a selection or entity filter narrows the run, skip directories no selected entry wrote to, and suppress the extra/'-' verdict, because those files cannot be attributed to the narrowed set. Related: a C# entry OutDir nested inside --out (or the reverse) makes the outer root's whole-tree diff report the inner entry's files as '-' drift.
  • ℹ️ server/python/src/metaobjects/cli.py:2496 - Several verify help strings still describe the 1.0 flag/config split. verify --generators says it 'must MATCH the gen that produced --out ... with none named there is nothing to check'; it now narrows the declared list, and with none named the whole list is checked. verify --config says 'Config mode runs when no <metadata_dir> is given'. The verify metadata_dir help says 'Omit to use a metaobjects.config.yaml (--codegen config mode, Python: declarative codegen config with a targets registry (parity with metaobjects.config.ts) #267)'. verify --out says 'committed output directory to diff against', but _verify_codegen_config now refuses it. On gen, --templates still says it is 'used by --template-spec', but that flag is refused. A related gap: verify --codegen &lt;metadata_dir&gt; given without --out silently ignores the positional for the codegen gate, which loads the config's metadata, while gen refuses the same positional.
  • ⚠️ server/python/src/metaobjects/cli.py:1756 - Introduced in this run's fix round. if narrowed and not expected: continue treats a narrowed run that produced nothing as an outDir no selected entry targets. Example: metaobjects verify --codegen --entities Prgram (a typo) on a project whose gen/Program.py was hand-edited. The narrowed regen emits nothing, because run_gen only warns 'No entities to generate'. The outDir is then skipped and verify exits 0 with drift present. The cause is the same when --entities names only a sourceless object. Fix: skip an outDir only when no selected target/entry writes into it (decide from the closure and selection, not from the emitted file set). Also refuse --entities names that match no object. Add a regression test: an --entities name that matches nothing, plus a hand-edited file, must not exit 0.
  • ⚠️ server/csharp/MetaObjects.Codegen/CodegenDrift.cs:91 - A fix-round sibling of the new RequirementTestsGenerator.OutDir. CodegenDrift.Compute errors when any root directory is missing (CodegenDrift.cs:91-104). CodegenRunner.Run creates only config.OutDir (CodegenRunner.cs:68). An entry's OutDir is created only when a file is written (CodegenRunner.cs:220), and RequirementTestsGenerator returns [] when the model declares no requirement (RequirementTestsGenerator.cs:106). With the documented RequirementTestsGenerator { OutDir = &#34;tests/Acme.Shop.Tests/Generated&#34; } and no requirement declared yet, gen exits 0 and writes nothing. Every verify --codegen then exits 2 with 'no committed generated output ... Run dotnet meta gen first', and running gen does not fix it. A fresh clone hits the same error, because git does not track an empty directory. Sibling: an empty declared list. gen returns early at CodegenCli.cs:629 before --out is created, so init, then gen (exit 0), then verify --codegen (exit 2) loops. Fix in CodegenDrift.Compute: treat a missing root as an empty committed tree, or treat an empty suite as clean.
  • ⚠️ server/python/src/metaobjects/cli.py:1349 - Reported by a sub-reviewer and not independently re-traced. A target that omits the generators key now exits 0 with 'declares no generators; nothing written' (cli.py:1381). Before this change, an omitted key raised NoGeneratorsSelectedError and exited 1, and the schema calls generators required. Example: the 1.0-shape config targets: {py: {outDir: gen}} makes metaobjects gen exit 0 and write nothing. The intent requires that gen 'fails clearly on a 1.0-shape config and never silently generates nothing'. An explicit generators: [] is a separate case and is deliberate (test_cli.py:206). Fix: refuse a target with no generators key, exit 2 with the migration line, and add a test.
  • ⚠️ server/csharp/MetaObjects.Codegen/CodegenCli.cs:613 - The run's --template-root reaches only SelectDeclared (CodegenCli.cs:614). A declared TemplateSpecGenerator { Spec = ... } with no TemplateRoot falls back to DefaultTemplateRoot() (TemplateSpecGenerator.cs:30). With both prompts/ and templates/ present, dotnet meta gen --template-root templates renders the prompts/ bodies and exits 0, and verify --codegen agrees. Fix: pass the run's templateRoot to a spec entry that sets none, or refuse the flag when the entry sets its own.
  • ℹ️ server/python/src/metaobjects/cli.py:1355 - Reported by a sub-reviewer and not re-traced. zip(gens, t.generators) assumes one resolved generator per config token, but the resolver splits on commas. So generators: [&#34;entity, routes&#34;] resolves two generators against one token, and zip silently drops routes. gen and verify then both exit 0 without router files. Fix: refuse a comma inside a list entry, or resolve each token separately.
  • ℹ️ server/python/src/metaobjects/cli.py:1532 - This is fix-round narrowed verify. The shared enums.py is built from the matched entities only (entity_model.py:829-840). So verify --codegen --entities Program reports drifted: enums.py on in-sync output when another entity consumes a shared enum. Fix: under --entities, compare only the per-entity paths emitted for the named entities.
  • ℹ️ server/python/src/metaobjects/cli.py:1243 - --generators &#34;&#34; (for example an unset shell variable) makes _selection_from_args return None. The run then covers the whole declared list instead of refusing an empty narrowing. Fix: refuse an empty or blank --generators.
  • ℹ️ agent-context/skills/metaobjects-audit/references/csharp.md:27 - The audit reference still names ApiDocsGenerator as the only entry with its own OutDir. RequirementTestsGenerator now takes one too. Name both.

🔧 Fix applied.
4 issues (2 warnings, 2 infos) still open:

  • ⚠️ server/csharp/MetaObjects.Codegen/CodegenDrift.cs:91 - A fix-round sibling of the new RequirementTestsGenerator.OutDir. CodegenDrift.Compute errors when any root directory is missing (CodegenDrift.cs:91-104). CodegenRunner.Run creates only config.OutDir (CodegenRunner.cs:68). An entry's OutDir is created only when a file is written (CodegenRunner.cs:220), and RequirementTestsGenerator returns [] when the model declares no requirement (RequirementTestsGenerator.cs:106). With the documented RequirementTestsGenerator { OutDir = &#34;tests/Acme.Shop.Tests/Generated&#34; } and no requirement declared yet, gen exits 0 and writes nothing. Every verify --codegen then exits 2 with 'no committed generated output ... Run dotnet meta gen first', and running gen does not fix it. A fresh clone hits the same error, because git does not track an empty directory. Sibling: an empty declared list. gen returns early at CodegenCli.cs:629 before --out is created, so init, then gen (exit 0), then verify --codegen (exit 2) loops. Fix in CodegenDrift.Compute: treat a missing root as an empty committed tree, or treat an empty suite as clean.
  • ⚠️ docs/features/migrations/docs-are-generators.md:457 - The Python migration section gives the wrong module path for an owned api-docs copy. Line 457 says codegen.generators.api_docs_generator:api_docs_generator. metaobjects eject api-docs writes the copy to codegen/generators/api_docs.py: owned_stem turns the stable name api-docs into api_docs (eject.py:67-78). Its own wiring line is therefore codegen.generators.api_docs:api_docs_generator. That form is what docs/features/own-your-codegen.md:961 and the tests use (test_docs_generators.py:23, test_one_generator_list.py:137). An adopter or agent who follows the migration section, which the intent says must be agent-followable, gets an import failure from metaobjects gen. Fix: change the comment to codegen.generators.api_docs:api_docs_generator. No other changed doc uses the wrong form.
  • ℹ️ CHANGELOG.md:1142 - The 1.1.0 CHANGELOG Python entry does not mention four new refusals added in this branch's fix rounds. These change exit codes in scripts and CI:
  • A target with no generators key exits 2 with the migration line (cli.py:1231-1239). Before, it either exited 1 (NoGeneratorsSelectedError) or wrote nothing.
  • An --entities name that matches no object exits 2 (cli.py:1255-1276). Before, it warned 'No entities to generate' and exited 0.
  • A blank --generators exits 2.
  • A generators entry that contains a comma is refused.
    The migration doc covers only the first. Fix: add one line naming each refusal to the Python bullet near CHANGELOG.md:1142. The C# blank --generators refusal (CodegenCli.cs:342-346) is also unlisted.
  • ℹ️ server/csharp/MetaObjects.Codegen/CodegenDrift.cs:88 - This case was left behind by round 2's fix for cs-entry-outdir-never-created. That fix treats a missing entry OutDir as an empty committed tree. The run's own --out is still a hard error when it is missing (CodegenDrift.cs:88, :102), even when no entry writes there.

Reproduction:

  1. codegen/Generators.cs lists only entries with their own OutDir, for example ApiDocsGenerator { OutDir = &#34;docs/generated&#34; } plus RequirementTestsGenerator { OutDir = &#34;tests/X/Generated&#34; }.
  2. dotnet meta gen ./metaobjects --out ./Generated creates an empty ./Generated (CodegenRunner creates config.OutDir unconditionally).
  3. Git does not track an empty directory, so a fresh clone or CI checkout has no ./Generated.
  4. dotnet meta verify --codegen exits 2 with 'no committed generated output at "./Generated" ... Run dotnet meta gen first', and running gen does not fix it.

The same happens when the entries that write under --out emit nothing. Fix at the line 88 condition: add the --out root only when an entry without its own OutDir is in the suite, for both narrowed and full runs. Otherwise treat a missing --out as an empty tree, as entry roots now are.

🔧 Fix applied.
1 warning still open:

  • ⚠️ docs/ports/python.md:483 - The worked template-spec example in the Python port page declares a target with only outDir and templateSpec and no generators key:
targets:
  docs:
    outDir: generated
    templateSpec: template-spec.json

This branch's fix round made metaobjects gen and verify --codegen refuse any target where t.generators is None (cli.py:1231-1239), with exit 2 and the line "target(s) 'docs' declare no generators key". An adopter or agent who copies the documented example therefore gets exit 2 and no output. The passing test for this path (test_one_generator_list.py test_a_template_spec_on_a_target_writes_into_its_out_dir_and_verifies_clean) writes generators: [] beside templateSpec.

Fix: add generators: [] to the example (for example generators: [] # the spec&#39;s generators run after these). No other changed doc has a templateSpec-only target: cli.md:368-371 lists generators: [api-docs], and the agent-context references give no YAML example.

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

  • ⚠️ server/csharp/MetaObjects.Codegen/CodegenCli.cs:656 - The C# --generators narrowing (ADR-0034 Amendment 9 rule 5, new in this branch) computes cross-generator facts from the narrowed suite. It should compute them from the declared list. As a result, a narrowed run changes what the selected entries emit.

RunGen sets IncludeNames = generators.Any(g =&gt; g.Name == &#34;names&#34;) (CodegenCli.cs:227), and RunGenArgs passes it the narrowed suite (:656). EntityGenerator, DbContextGenerator, RoutesGenerator and CallableGenerator all switch on ctx.Config.IncludeNames, between [Table(OrderNames.SourcePrimaryTable)] and the literal [Table(&#34;orders&#34;)].

Concrete sequence:

  1. codegen/Generators.cs declares [EntityGenerator, NamesGenerator, DbContextGenerator].
  2. Run dotnet meta gen ./metaobjects --out ./Generated. This writes entity files that reference the Names constants.
  3. Run dotnet meta gen ./metaobjects --out ./Generated --generators entity. The skills and port docs advertise this exact form: "narrow to declared entries". It silently rewrites every &lt;Entity&gt;.g.cs with literal names. The files are pristine per the manifest, so they are overwritten, and the run exits 0.
  4. A full dotnet meta verify --codegen now reports drift on every entity file.

In the other direction, after a full gen, dotnet meta verify --codegen --out ./Generated --generators entity regenerates with IncludeNames = false (:755) and reports false ~ drift on every entity file. The added test Verify_narrowed_to_an_entry_with_its_own_directory_does_not_convict_the_rest_of_the_list (OneGeneratorListTests.cs:423) does not catch this, because its list declares no names.

Sibling sites with the same invariant (derive from the declared list, not the narrowing):

  • CodegenCli.cs:651: the UnsatisfiedRequires advisory runs on RegistryNames(suite, ...). --generators output-parser therefore warns "Add entity to codegen/Generators.cs" even when the list already declares entity.
  • CodegenCli.cs:755: RunVerifyArgs builds IncludeNames from the narrowed suite.

The Python port already avoids this: its requires warning runs on each target's full list, and its narrowed verify takes content from a declared-list regen.

Fix at the earliest shared boundary, RunGenArgs/RunVerifyArgs, which hold declared:

  • Compute includeNames from declared, and pass it into RunGen (an optional parameter) and into the verify GenConfig.
  • Run the requires advisory on RegistryNames(declared, ...).

Regression test: declare [entity, names] and run a full gen. Then gen --generators entity must leave the entity files byte-identical, and verify --codegen --generators entity must exit 0.

  • ℹ️ server/csharp/MetaObjects.Codegen/CodegenCli.cs:643 - In RunGenArgs, the message "the --generators selection matched no declared entry; nothing written" can never print. SelectDeclared returns an error for any selection that names an undeclared entry (:357-364), and for an empty selection (:342-346). So when suite.Count == 0 the selection is always null and the list is empty. Simplify the branch to the single "declares no generators; nothing written" message.

🔧 Fix applied.
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 11 of 11 scenarios driven live against the product
Scenario Result Live Evidence
Python: 1.0 docs-block config refused with migration line and nothing written ✅ pass live /tmp/mo-live-py run gen, exit 2
Python: target with generators [] writes nothing and says so ✅ pass live /tmp/mo-live-py gen exit 0
Python: generators [entity] writes only its files; verify in sync; hand edit detected ✅ pass live /tmp/mo-live-py gen/verify exit 0 then 1
Python: undeclared, blank, and unknown-entity narrowing refused ✅ pass live /tmp/mo-live-py exit 1, 2, 2
Python: narrowed gen output identical to full run ✅ pass live /tmp/mo-live-py diff -r identical
C#: init scaffold writes Generators.cs, Program.cs, Codegen.csproj ✅ pass live /tmp/mo-live-cs dotnet-meta init
C#: narrowed --generators entity gen byte-identical to full run ✅ pass live /tmp/mo-live-cs Subscriber.g.cs cmp identical
C#: undeclared and blank --generators refused, exit 2 ✅ pass live /tmp/mo-live-cs exit 2 each
C#: dotnet meta docs refused with migration line ✅ pass live /tmp/mo-live-cs docs exit 2
C#: OutDir-only list verifies clean on fresh checkout ✅ pass live /tmp/mo-live-cs verify --codegen exit 0
C#: hand-edit drift detected ✅ pass live /tmp/mo-live-cs verify exit 1
  • scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains
  • cd server/csharp && dotnet test MetaObjects.Cli.Tests (291 passed, 0 failed)
  • cd server/csharp && dotnet test MetaObjects.Codegen.Tests (841 passed, 1 skipped, 0 failed)
  • cd server/python && uv run pytest -q tests (3141 passed, 4 warnings, exit 0)
  • Live Python scenarios in /tmp/mo-live-py: 1.0 docs-block config refused exit 2; generators [] writes nothing; generators [entity] writes only its files, verify in sync, hand edit detected; undeclared, blank, and unknown-entity narrowing refused; narrowed gen identical to full run
  • Live C# scenarios in /tmp/mo-live-cs via dotnet-meta built from the worktree with a local ProjectReference: init scaffold; narrowed --generators entity byte-identical to full run; blank and undeclared refusals exit 2; dotnet meta docs refused exit 2; OutDir-only list verifies clean on fresh checkout; hand-edit drift exit 1
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Validation runs and size

  • Validation runs: 1. Review needed five fix rounds inside that run; test, document, lint, push and PR passed. The CI step was skipped by the triage rule (ci_mode: local).
  • Hand-written lines against main: production code +1501 / -1713 (net -212); tests +2061 / -701 (net +1360); docs, CHANGELOG and agent-context +889 / -434 (net +455).
  • Copied lines (regenerated fixtures/agent-context-conformance expected files): +137 / -137.

…are removed

ADR-0034 Amendment 9 for C# and Python. Every generator, the api reference
included, is an entry in the port's generator list, and the gen command runs
exactly that list.

C#: the list is codegen/Generators.cs, run by a two-line codegen/Program.cs.
New `dotnet meta init` writes the codegen project; eject writes the three
files when missing. `dotnet meta gen` and `verify --codegen` run the list and
refuse a project without one. `--generators` only narrows declared entries.
ApiDocsGenerator takes OutDir, ApiSubDir, ProjectName and ModelBaseUrl as init
properties; IGenerator.OutDir lets an entry write outside --out, and the drift
gate compares each directory. `dotnet meta docs` prints the removed-command
line; 1.0 docs flags on gen fail before writing.

Python: metaobjects.config.yaml is the list. gen and verify --codegen are
config-only; the positional --out form and --template-spec are not read
(targets gain templateSpec). api-docs is a target entry with a top-level
apiDocs block. `metaobjects docs` and a 1.0 docs block fail with the fixed
lines. Moved-module aliases warn with FutureWarning naming 2.0, and the
owned-router eject test compares lines in order.

Docs, skills, fixture copies, the showcase script and the CHANGELOG follow.
…st, never discovered

One list means one declaration (ADR-0034 Amendment 9). C#: the owned runner
no longer reads --template-spec or discovers a project-root
template-spec.json; a spec runs as a TemplateSpecGenerator entry in
codegen/Generators.cs (Spec, TemplateRoot, OutDir). A run that finds the
1.0 configuration with no such entry fails before writing, with a fixed
line naming the migration section.

Python already required a target's templateSpec; a leftover
template-spec.json that no target declares now fails gen and verify
--codegen the same way instead of being skipped.

The C# migration section gains the template-spec before and after, and the
port, concepts, own-your-codegen, cli and skill pages follow.
… garbled sentence; regen agent-context fixtures
@dmealing
dmealing merged commit 0affc6a into main Oct 14, 2026
1 check passed
@dmealing
dmealing deleted the fm/mo-1-1-0-onelist-cs-py branch October 14, 2026 21:52
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