Skip to content

feat(csharp): run the codegen gate on bare verify via csharp.outDir - #477

Merged
dmealing merged 4 commits into
mainfrom
fm/mo-csharp-verify-outdir
Oct 28, 2026
Merged

dmealing merged 4 commits into
mainfrom
fm/mo-csharp-verify-outdir

Conversation

@dmealing

Copy link
Copy Markdown
Member

What Changed

  • Adds a csharp.outDir setting to .metaobjects/config.json, read by NeutralConfig and surfaced on ResolvedMetadata.OutDir. Relative paths resolve against the directory holding .metaobjects/. Unknown keys, a non-object block, or an empty or non-string outDir are refused with ERR_BAD_ATTR_VALUE.
  • dotnet meta gen and dotnet meta verify --codegen fall back to csharp.outDir when --out is omitted, so a bare dotnet meta verify runs the codegen gate against the directory gen writes to. An explicit --out still wins. With neither set, the skip note now names the setting. A malformed config is reported as the real error, not as a usage line.
  • The TypeScript ConfigSchema declares the csharp block so the shared config validates without unknown-key warnings. Docs (docs/features/cli.md, docs/ports/csharp.md), the CHANGELOG entry, and the C# agent-context references are updated to match.
Validation

Risk Assessment

✅ Low: Change is small and additive: csharp.outDir is read, validated and resolved consistently for gen and verify, an explicit --out still wins, and the prior round's malformed-config fix now reports the real error. The other ports ignore the new config block, and the TypeScript reader declares it. No concrete failing path found.

Testing

Driven live against the built C# CLI in a disposable project. Bare verify runs the codegen gate from csharp.outDir: clean passes, drift and missing files fail naming the file. --out overrides config, no-config keeps the skip note, and malformed config shows the real error on bare verify, gen and verify --codegen, so the review-1 fix holds. Full C# solution tests passed with 0 failures, plus the TS baseline. Overall pass; one cosmetic info on the drift hint wording.

  • Live validation: ✅ go - 10 of 10 scenarios driven live against the product
Scenario Result Live Evidence
Plain bare verify with csharp.outDir configured: gate runs and passes on in-sync output ✅ pass live ~/.no-mistakes/evidence/01M60KBFDTQZ64FPKQ6C2D3V8J/csharp-bare-verify-outdir.txt (S1: verify metaobjects gives 'verify --codegen: OK', exit 0)
Bare verify catches a hand-edited generated file and exits 1 naming the file ✅ pass live csharp-bare-verify-outdir.txt (S2: '~ Author.g.cs', exit=1)
Bare verify catches a deleted generated file and exits 1 ✅ pass live csharp-bare-verify-outdir.txt (S3: '+ Author.g.cs not committed', exit=1)
Explicit --out overrides csharp.outDir for verify ✅ pass live csharp-bare-verify-outdir.txt (S4: verify --out empty dir reports drift; S5: --out generated checks that dir)
gen with no --out writes to csharp.outDir ✅ pass live csharp-bare-verify-outdir.txt (initial gen wrote generated/Author.g.cs; S9 reports unchanged)
No csharp.outDir: bare verify keeps the skip note and exits 0 ✅ pass live csharp-bare-verify-outdir.txt (S6: codegen gate skipped note, exit=0)
No csharp.outDir and no --out: gen prints usage naming the setting, exit 2 ✅ pass live csharp-bare-verify-outdir.txt (S6b, exit=2)
Adversarial: non-string csharp.outDir shows the real config error, not usage, across bare verify, gen and verify --codegen ✅ pass live csharp-bare-verify-outdir.txt (S7, S7b, S7c: error: ERR_BAD_ATTR_VALUE ... must be a non-empty string, exit=2)
Adversarial: misspelled key csharp.outdir and non-object csharp block are refused with the real error ✅ pass live csharp-bare-verify-outdir.txt (S8, S8b, exit=2)
Config restored after the error cases: gen and bare verify work again ✅ pass live csharp-bare-verify-outdir.txt (S9, S9b, exit=0)
Evidence: Live transcript: bare verify and gen scenarios S1 to S9b
== S1 clean bare verify
$ dotnet MetaObjects.Cli.dll verify metaobjects
dotnet meta: running the generator list in codegen/Generators.cs — `dotnet run --project codegen -- verify --codegen metaobjects --out /tmp/mo-verify-e2e/proj/generated`.
dotnet meta verify --codegen: OK (generated output is in sync)
dotnet meta verify — running --templates --codegen (default). Explicit subverbs: --templates (prompt drift), --codegen (regen-vs-committed drift); --db is not supported in the C# port.
dotnet meta verify --templates: OK
exit=0

== S2 drift bare verify
$ dotnet MetaObjects.Cli.dll verify metaobjects
dotnet meta: running the generator list in codegen/Generators.cs — `dotnet run --project codegen -- verify --codegen metaobjects --out /tmp/mo-verify-e2e/proj/generated`.
dotnet meta verify --codegen: drift (1 file(s) differ from a fresh regen):
  ~ Author.g.cs (committed content differs from a fresh regen)
Run 'dotnet meta gen' to regenerate (it needs --out, the same directory you gave this command), then commit the result.
dotnet meta verify — running --templates --codegen (default). Explicit subverbs: --templates (prompt drift), --codegen (regen-vs-committed drift); --db is not supported in the C# port.
dotnet meta verify --templates: OK
exit=1

== S3 missing generated file bare verify
$ dotnet MetaObjects.Cli.dll verify metaobjects
dotnet meta: running the generator list in codegen/Generators.cs — `dotnet run --project codegen -- verify --codegen metaobjects --out /tmp/mo-verify-e2e/proj/generated`.
dotnet meta verify --codegen: drift (1 file(s) differ from a fresh regen):
  + Author.g.cs (a fresh regen would emit it; not committed — run 'dotnet meta gen')
Run 'dotnet meta gen' to regenerate (it needs --out, the same directory you gave this command), then commit the result.
dotnet meta verify — running --templates --codegen (default). Explicit subverbs: --templates (prompt drift), --codegen (regen-vs-committed drift); --db is not supported in the C# port.
dotnet meta verify --templates: OK
exit=1

== S4 --out overrides config (empty elsewhere dir)
$ dotnet MetaObjects.Cli.dll verify metaobjects --out /tmp/mo-verify-e2e/elsewhere
dotnet meta: running the generator list in codegen/Generators.cs — `dotnet run --project codegen -- verify --codegen metaobjects --out /tmp/mo-verify-e2e/elsewhere`.
dotnet meta verify --codegen: drift (1 file(s) differ from a fresh regen):
  + Author.g.cs (a fresh regen would emit it; not committed — run 'dotnet meta gen')
Run 'dotnet meta gen' to regenerate (it needs --out, the same directory you gave this command), then commit the result.
dotnet meta verify — running --templates --codegen (default). Explicit subverbs: --templates (prompt drift), --codegen (regen-vs-committed drift); --db is not supported in the C# port.
dotnet meta verify --templates: OK
exit=1

== S5 --out wins: stale configured dir ignored when --out clean
$ dotnet MetaObjects.Cli.dll verify metaobjects --out generated
dotnet meta: running the generator list in codegen/Generators.cs — `dotnet run --project codegen -- verify --codegen metaobjects --out generated`.
dotnet meta verify --codegen: drift (1 file(s) differ from a fresh regen):
  ~ Author.g.cs (committed content differs from a fresh regen)
Run 'dotnet meta gen' to regenerate (it needs --out, the same directory you gave this command), then commit the result.
dotnet meta verify — running --templates --codegen (default). Explicit subverbs: --templates (prompt drift), --codegen (regen-vs-committed drift); --db is not supported in the C# port.
dotnet meta verify --templates: OK
exit=1

$ dotnet MetaObjects.Cli.dll verify metaobjects
dotnet meta: running the generator list in codegen/Generators.cs — `dotnet run --project codegen -- verify --codegen metaobjects --out /tmp/mo-verify-e2e/proj/generated`.
dotnet meta verify --codegen: OK (generated output is in sync)
dotnet meta verify — running --templates --codegen (default). Explicit subverbs: --templates (prompt drift), --codegen (regen-vs-committed drift); --db is not supported in the C# port.
dotnet meta verify --templates: OK
exit=0

== S6 no outDir: bare verify skip note
$ dotnet MetaObjects.Cli.dll verify metaobjects
dotnet meta verify — codegen gate skipped: set "csharp": { "outDir": "<dir>" } in .metaobjects/config.json (or pass --out <dir>) naming the committed generated output to diff against, and have codegen/Generators.cs to run it
dotnet meta verify — running --templates --codegen (default). Explicit subverbs: --templates (prompt drift), --codegen (regen-vs-committed drift); --db is not supported in the C# port.
dotnet meta verify --templates: OK
exit=0

== S6b no outDir: gen without --out usage
$ dotnet MetaObjects.Cli.dll gen metaobjects
dotnet meta: running the generator list in codegen/Generators.cs — `dotnet run --project codegen -- gen metaobjects`.
usage: dotnet meta gen [<metadataDir>] --out <dir> [--namespace <ns>] [--generators <a,b,c>] [...]
  (or set "csharp": { "outDir": "<dir>" } in .metaobjects/config.json)
exit=2

== S7 non-string outDir: bare verify
$ dotnet MetaObjects.Cli.dll verify metaobjects
error: ERR_BAD_ATTR_VALUE: /tmp/mo-verify-e2e/proj/.metaobjects/config.json: "csharp.outDir" must be a non-empty string
exit=2

== S7b non-string outDir: gen
$ dotnet MetaObjects.Cli.dll gen metaobjects
dotnet meta: running the generator list in codegen/Generators.cs — `dotnet run --project codegen -- gen metaobjects`.
error: ERR_BAD_ATTR_VALUE: /tmp/mo-verify-e2e/proj/.metaobjects/config.json: "csharp.outDir" must be a non-empty string
exit=2

== S7c non-string outDir: verify --codegen
$ dotnet MetaObjects.Cli.dll verify --codegen metaobjects
error: ERR_BAD_ATTR_VALUE: /tmp/mo-verify-e2e/proj/.metaobjects/config.json: "csharp.outDir" must be a non-empty string
exit=2

== S8 misspelled key: gen
$ dotnet MetaObjects.Cli.dll gen metaobjects
dotnet meta: running the generator list in codegen/Generators.cs — `dotnet run --project codegen -- gen metaobjects`.
error: ERR_BAD_ATTR_VALUE: /tmp/mo-verify-e2e/proj/.metaobjects/config.json: "csharp" has an unknown key "outdir" (expected "outDir")
exit=2

== S8b csharp not object: verify
$ dotnet MetaObjects.Cli.dll verify metaobjects
error: ERR_BAD_ATTR_VALUE: /tmp/mo-verify-e2e/proj/.metaobjects/config.json: "csharp" must be an object
exit=2

== S9 configured outDir restored: gen
$ dotnet MetaObjects.Cli.dll gen metaobjects
dotnet meta: running the generator list in codegen/Generators.cs — `dotnet run --project codegen -- gen metaobjects`.
  unchanged: Author.g.cs
dotnet meta gen: 0 file(s) written
exit=0

== S9b verify after restore
$ dotnet MetaObjects.Cli.dll verify metaobjects
dotnet meta: running the generator list in codegen/Generators.cs — `dotnet run --project codegen -- verify --codegen metaobjects --out /tmp/mo-verify-e2e/proj/generated`.
dotnet meta verify --codegen: OK (generated output is in sync)
dotnet meta verify — running --templates --codegen (default). Explicit subverbs: --templates (prompt drift), --codegen (regen-vs-committed drift); --db is not supported in the C# port.
dotnet meta verify --templates: OK
exit=0
  • Outcome: ⚠️ 1 info across 1 run (18m15s)

Pipeline

Updates from git push no-mistakes

✅ **Intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • ⚠️ server/csharp/MetaObjects.Codegen/CodegenCli.cs:667 - ConfiguredOutDir swallows MetaModelException/MetadataLocationException and returns null. In the owned gen and verify --codegen runners, a malformed config (e.g. &#34;csharp&#34;: { &#34;outdir&#34;: &#34;x&#34; }, a non-string outDir, or a bad sources) with no --out hits the usage branch. The runner prints usage: ... --out &lt;dir&gt; and (or set &#34;csharp&#34;: { &#34;outDir&#34;: ... }), then exits 2. The real error (ERR_BAD_ATTR_VALUE naming the bad key) is never shown. The user is told to set the very key they already set wrongly. Same class at CodegenCli.cs:854 (verify --codegen runner). Program.cs bare verify is unaffected because ResolveMetadataDirOrExit reports first. Fix: when resolution throws, fall through to ResolveOrReport so the real error prints, instead of reporting usage.
  • ℹ️ server/csharp/MetaObjects.Codegen/CodegenCli.cs:597 - The XML doc summary for ResolveOrReport had been displaced above the new ConfiguredOutDir method, leaving two stacked summaries on one member. Moved the ResolveOrReport summary back above ResolveOrReport. Comment-only change.

🔧 Fix applied.
1 info still open:

  • ℹ️ server/csharp/MetaObjects.Codegen/CodegenCli.cs:597 - The XML doc summary for ResolveOrReport had been displaced above the new ConfiguredOutDir method, leaving two stacked summaries on one member. Moved the ResolveOrReport summary back above ResolveOrReport. Comment-only change.
⚠️ **Test** - 1 info
  • ℹ️ server/csharp/MetaObjects.Codegen/CodegenCli.cs - Drift hint still says it needs --out, the same directory you gave this command, after a bare verify that took its directory from csharp.outDir. Cosmetic only; exit codes and the gate are unaffected.
  • Live validation: ✅ go - 10 of 10 scenarios driven live against the product
Scenario Result Live Evidence
Plain bare verify with csharp.outDir configured: gate runs and passes on in-sync output ✅ pass live ~/.no-mistakes/evidence/01M60KBFDTQZ64FPKQ6C2D3V8J/csharp-bare-verify-outdir.txt (S1: verify metaobjects gives 'verify --codegen: OK', exit 0)
Bare verify catches a hand-edited generated file and exits 1 naming the file ✅ pass live csharp-bare-verify-outdir.txt (S2: '~ Author.g.cs', exit=1)
Bare verify catches a deleted generated file and exits 1 ✅ pass live csharp-bare-verify-outdir.txt (S3: '+ Author.g.cs not committed', exit=1)
Explicit --out overrides csharp.outDir for verify ✅ pass live csharp-bare-verify-outdir.txt (S4: verify --out empty dir reports drift; S5: --out generated checks that dir)
gen with no --out writes to csharp.outDir ✅ pass live csharp-bare-verify-outdir.txt (initial gen wrote generated/Author.g.cs; S9 reports unchanged)
No csharp.outDir: bare verify keeps the skip note and exits 0 ✅ pass live csharp-bare-verify-outdir.txt (S6: codegen gate skipped note, exit=0)
No csharp.outDir and no --out: gen prints usage naming the setting, exit 2 ✅ pass live csharp-bare-verify-outdir.txt (S6b, exit=2)
Adversarial: non-string csharp.outDir shows the real config error, not usage, across bare verify, gen and verify --codegen ✅ pass live csharp-bare-verify-outdir.txt (S7, S7b, S7c: error: ERR_BAD_ATTR_VALUE ... must be a non-empty string, exit=2)
Adversarial: misspelled key csharp.outdir and non-object csharp block are refused with the real error ✅ pass live csharp-bare-verify-outdir.txt (S8, S8b, exit=2)
Config restored after the error cases: gen and bare verify work again ✅ pass live csharp-bare-verify-outdir.txt (S9, S9b, exit=0)
  • scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains
  • dotnet build server/csharp/MetaObjects.Cli/MetaObjects.Cli.csproj -c Release -o /tmp/mo-verify-e2e/cli
  • Disposable project in /tmp/mo-verify-e2e/proj: dotnet MetaObjects.Cli.dll init, codegen/Codegen.csproj ProjectReference to worktree MetaObjects.Codegen, EntityGenerator in list, .metaobjects/config.json with csharp.outDir
  • Live scenarios S1 to S9b via dotnet MetaObjects.Cli.dll gen|verify (transcript: csharp-bare-verify-outdir.txt)
  • dotnet test server/csharp/MetaObjects.Cli.Tests/MetaObjects.Cli.Tests.csproj (317 passed)
  • dotnet test server/csharp/MetaObjects.sln (Render 332, Conformance 2094, Cli 317, Codegen 1037 passed, 1 skipped, 0 failed)
  • Baseline scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains (already green)
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…fig.json

A plain `dotnet meta verify` skipped the codegen gate unless --out was passed.
`csharp.outDir` now names the directory; gen writes there, verify diffs against
it, and --out still wins.
…ures/cli.md and docs/ports/csharp.md. Doc-only change; no code or tests touched. CHANGELOG and agent-context references already matched the new behavior.
@dmealing
dmealing merged commit 71e9cd7 into main Oct 28, 2026
1 check passed
@dmealing
dmealing deleted the fm/mo-csharp-verify-outdir branch October 28, 2026 07: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