Repository navigation
feat(cli): add eject --undo, gen --strict and init --with-docs - #505
Merged
Merged
Conversation
…dead api-docs model link
…; drop the dead api-docs model link (#488)
…es, api-docs follow the generator list (#490)
…e, handEdits advice, one install line, llms.txt copy, CHANGELOG
eject --undo: Python de-duplicates names, TS refuses a never-ejected name like the other ports. gen: the TS stock-copy notice cannot abort a run, the strict hint prints only for ERR_UNKNOWN_ATTR, C# gen --strict has its own hint and loads once, the C# notice catches any failure. init --with-docs says so when metaobjects.config.ts already exists. Python apiPrefix is limited to path characters so it cannot break the generated router.
The generated web app's default host builder watches appsettings.json with an inotify instance, and the per-user limit is shared with every other process the user runs. When it is spent the test dies in WebApplication.CreateBuilder with an IOException that has nothing to do with the code under test. The polling file watcher needs no inotify instance.
Share one findOwned and the codegen directory name in the Maven plugin, compute the identical set once in Python undo, reuse the config-name and api-prefix constants, and collapse a duplicated list in the TypeScript undo.
…cs entity symbols
…ring # Conflicts: # docs/llms/llms.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What Changed
meta eject --undo [names] [--force](TypeScript),metaobjects eject --undo(Python),mvn metaobjects:eject -Dundo=true(Java/Kotlin) anddotnet meta eject --undo(C#) remove owned copies that match the shipped reference, plus template modules only they used. Named copies that differ block the whole call unless--force. Undo never edits config and prints the import to restore.meta genprints one line naming unchanged owned copies. Docs state thateject --listis the drift report.gen --strict(TypeScript, Python, C#) loads metadata the wayverifydoes, so unregistered or misspelled attributes stop the run before anything is written.meta init --with-docs(TypeScript, C#) andmetaobjects init --with-docs(Python) wire the docs and api-docs generators; the default scaffold is unchanged.modelBaseUrlis set (Python, C#, Java, Kotlin). TypeScript api-docs describe only what the run writes.entityTypeNames(obj)is exported from@metaobjectsdev/codegen-tsand documented (Document the type names the TypeScript generators emit per entity #490). Python codegen readsapiPrefixfrommetaobjects.config.yaml, default/api(Python codegen: add an apiPrefix setting instead of hard-coding /api #488). The C# eject end-to-end test setsDOTNET_USE_POLLING_FILE_WATCHER=1so it no longer exhausts the inotify limit.Fixes #490
Fixes #488
Fixes a flaky test on main: C# eject end-to-end
EjectEndToEndTestsgenerated a web app whose default host builder watchesappsettings.jsonwith an inotify instance. The per-user inotify limit is shared with every other process, so when it is spent the app dies inWebApplication.CreateBuilderwith an IOException that has nothing to do with the code under test. The test now setsDOTNET_USE_POLLING_FILE_WATCHER=1for the child process; the polling watcher needs no inotify instance. Measured before and after: 2 of 10 runs failed before the change, and 10 of 10 pass after it. The Python half of this fix already merged in #499. It is its own commit (fix(test): keep the C# eject e2e off inotify).Gate runs
This took one no-mistakes gate run (it went through review twice for fixes and one test-command re-run; the single red was the mutation step being killed by SIGTERM, exit 143, and it passed on the base commit and on re-run).
Validation
Risk Assessment
✅ Low: The round-1 and round-2 fixes check out against the code: all three ports now treat a changed owned runtime adapter as non-stock, TS entity-module gating mirrors the
includeQueriespath, and the C# test now asserts on the active list; remaining edges are minor and consistent with the intent.Testing
I drove the real TypeScript, Python and C# CLIs live in disposable projects:
init --with-docs,gen,gen --strict,ejectandeject --undo, plus PythonapiPrefix. Those scenarios passed live. The Java and Kotlin undo and notice paths were not driven by hand against the Maven plugin; they are covered only by the java, java-integ and kotlin-integ lanes' own tests, so they are recorded as untested. The full regression run (scripts/ci-local.sh --strict-toolchains) ended with "LOCAL CI PASSED" across all 11 lanes in 11m41s, per the CI log, but I did not drive it as a live product scenario, so it is also recorded as untested. The baseline (ts-fast,ts-unit) had already passed. No UI surface, so no screenshots. The worktree is clean and the temp projects are removed.meta init --with-docsthenmeta genwrites docs and API pages that name no symbol nothing generatesentityFile()was wired, then listed Model and Validation symbolsmeta eject --undoremoves it with its templates and runtimecodegen/runtimeadapter file keeps the generator out of the notice and out of no-name undo--force; a never-ejected name exits 2gen --strictstops on a typo'd attr and writes nothing, with a gen-specific hint; lenient gen warns and writesentityTypeNamesis exported from@metaobjectsdev/codegen-tsinit --with-docswires api-docs; plaininitleaves the list empty; api-docs omit the Model/metadata linkapiPrefix(default, /v2, root, special chars) drives routers and api-docs; an invalid prefix is rejectedgen --strictinit --with-docswrites an active ApiDocsGenerator entry;eject --undo nosuchexits 2; the Cli tests (including undo, strict, runtime-changed and init-with-docs) passscripts/ci-local.sh --strict-toolchains)Evidence: TS: runtime adapter changed keeps generator
Evidence: TS: eject --undo variants
Evidence: TS: gen --strict
Evidence: TS: api-docs with entity generator wired
Evidence: Python: init --with-docs and gen
Evidence: Python: apiPrefix
Evidence: Python: eject and notice
Evidence: Python: undo and strict
Evidence: C#: init --with-docs
Evidence: C#: live eject --undo
Evidence: C#: Cli.Tests run
Evidence: Full ci-local run (LOCAL CI PASSED, not live-driven)
Pipeline
Updates from git push no-mistakes
✅ **Intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 4 issues found → auto-fixed (2) ✅
server/typescript/packages/cli/src/commands/eject.ts:602- In TypeScript, C# and Java, the 'unchanged copy' check ignores the owned HTTP/helper runtime adapter. Python includes it. Failure path in TypeScript: the project ownsentity(orroutes/routes-hono), the generator file is identical to the reference, and the user changed a file undercodegen/runtime/.generatorListGroups(eject.ts:445-488) folds template status into the row but never runtime status, soisStockRowreturns true.meta genthen tells the user the copy is 'the shipped reference, unchanged', andmeta eject --undowith no names removes the generator. The changed adapter file goes into keptCompanions and stays on disk. But the packaged generator imports@metaobjectsdev/runtime-ts, so the user's adapter change silently stops reaching the generated code. own-your-codegen.md:180-188 says to keep the owned generator in exactly this case ('Stop here for that generator'). It also contradicts the docs claim that 'A copy you changed is never touched unless you name it and pass the force flag'. Sibling sites with the same gap: server/csharp/MetaObjects.Cli/OwnedCopy.cs:133 (IdenticalCopiesusesStatus, which covers companions but notRuntimeStatus); server/java/maven-plugin/src/main/java/com/metaobjects/mojo/OwnedStock.java:70 (identical()explicitly excludes the helper runtime, which eject rewired via RUNTIME_PACKAGE). Related partial-removal path: eject.ts:733 and server/csharp/MetaObjects.Cli/EjectCommand.cs:359 delete the identical runtime files and keep the changed ones, which leaves a half-copied adapter tree (C# also prints 'The helper runtime copy is gone'). Fix: treat a generator that reaches runtime (ejectsRuntime / HelperRuntime.UsedBy / entry.runtime) as non-stock whenever its owned runtime copy differs, for both the notice and no-name undo, matching Python'sidentical_owned.server/typescript/packages/codegen-ts/src/generators/api-model.ts:469- The TypeScript api-docs now leave out queries and REST routes when no generator writes them. They still document the entity module (<Entity>model, the Insert/Update Zod schemas fromvalidationSymbols,relations()) whatever the generator list holds. The newmeta init --with-docsscaffold wires onlydocsFile()andapiDocsFile(), so its firstmeta genwrites API pages that namesrc/generated/<Entity>.tsexports nothing generates. That is the same class of defect this change fixes for queries. The CHANGELOG line 'TypeScript API docs describe only what the run writes' (CHANGELOG.md:222) overstates the fix. The intent scoped item 4 to queries and routes, so gating the entity-module symbols on anemitsEntity-style marker extends scope. The alternative is to narrow the CHANGELOG wording. Asking which one the author wants.docs/ports/python.md:177- The docs saidmetaobjects gen --strictprints 'verify's message and hint'. Per the recorded decision, the hint is gen-specific (drop --strict). Reworded to 'verify's message and a gen-specific hint (drop--strict)'.docs/ports/csharp.md:235- The docs saiddotnet meta gen --strictprints 'the same ERR_UNKNOWN_ATTR lines and hint' as verify. It printsGenStrictHint, not verify's--laxhint. Reworded to 'the same ERR_UNKNOWN_ATTR lines with a gen-specific hint (drop--strict)'.🔧 Fix applied.
2 issues (1 warning, 1 info) still open:
server/csharp/MetaObjects.Cli.Tests/InitWithDocsTests.cs:20-With_docs_lists_the_api_docs_generator_and_says_how_it_is_drift_checkedcannot fail when--with-docsdoes nothing. The scaffold template always contains the commented example// new MetaObjects.Codegen.Generators.ApiDocsGenerator { OutDir = "docs/csharp", ProjectName = "shop" },(InitCommand.cs:84). The first assertion,Assert.Contains("new MetaObjects.Codegen.Generators.ApiDocsGenerator { OutDir = \"docs/csharp\"", List), is a substring match, so it passes against that comment even ifwithDocsadds no entry. The second assertion strips that exact comment line and then asserts the// new ... OutDirprefix is absent. That is true by construction, and theProjectName = "shop"text in the Replace never matches the real temp-dir name anyway. The assertions on the printedlinesonly check for the words "csharp" and "outDir", which the guidance text always contains. A plausible wrong behavior,InitCommand.Run(root, withDocs: true)passing[]toWriteMissing, would still pass. Fix: assert the entry appears in the active list, for example on the text afterpublic static IReadOnlyList<IGenerator> All, or as a line that does not start with//. Assert it is absent from the same region whenwithDocsis false. The sibling testWithout_the_flag_the_list_is_unchangedis fine. The TypeScript and Python--with-docstests parse real output or rungen, so they are fine.docs/llms/llms.txt:53- The intent says thellms.txtcopy no longer "leans on 'spine'". The lede and the guaranteed-versus-yours paragraph were rewritten. Two metaphorical uses remain: line 53, "capabilities of the same metadata spine", and line 108, "on one spine". The other hits are proper names: the "contract spine" assessment scope on line 21 and the essay title on line 145. Reword lines 53 and 108 in plain prose.🔧 Fix applied.
✅ Re-checked - no issues remain.
conformance:mutationexited 143, SIGTERM); every test suite in that run reported 0 failures, the command passes on the base commit, and the re-run on the same head passed (LOCAL CI PASSED).scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchainsscripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains(exit 0: build+typecheck, TS conformance, TS unit suites, mutation gate)meta init --with-docsin a scratch projectmeta eject entity queriesthenmeta gen(unchanged-copy notice)meta gen --strictwith an unregistered attribute, then with valid metadatameta eject --undo nosuch,--undo entityon a customised copy,--undowith no names,--undo entity --forceAPI pages from a docs-only generator listentityTypeNames({name:"Item"})from codegen-tsPythonmetaobjects init --with-docsin a scratch dirpython3 -m pyteston the Python apiPrefix test files (32 passed)🔧 No changes applied.
2 infos still open:
docs/features/cli.md:251- Text says 'No port has a doctor command:eject --list(or the matching goal) is the report.' C#dotnet meta eject --listis rejected as an unknown flag. The C# report isdotnet meta gen --list, which docs/ports/csharp.md states correctly. Minor imprecision in the shared doc.meta eject --undoremoves the copies, empty directories remain (codegen/runtime/drizzle-fastify). Cosmetic only.meta init --with-docsthenmeta genwrites docs and API pages that name no symbol nothing generatesentityFile()was wired, then listed Model and Validation symbolsmeta eject --undoremoves it with its templates and runtimecodegen/runtimeadapter file keeps the generator out of the notice and out of no-name undo--force; a never-ejected name exits 2gen --strictstops on a typo'd attr and writes nothing, with a gen-specific hint; lenient gen warns and writesentityTypeNamesis exported from@metaobjectsdev/codegen-tsinit --with-docswires api-docs; plaininitleaves the list empty; api-docs omit the Model/metadata linkapiPrefix(default, /v2, root, special chars) drives routers and api-docs; an invalid prefix is rejectedgen --strictinit --with-docswrites an active ApiDocsGenerator entry;eject --undo nosuchexits 2; the Cli tests (including undo, strict, runtime-changed and init-with-docs) passscripts/ci-local.sh --strict-toolchains)scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchainsmeta init --with-docsthenmeta genin a fresh TS project: docs and API pages written, no phantom queries/routes/entity symbolsmeta eject entity,meta gen: one-line unchanged-copy notice; withentityFile()wired, API docs list the Model and Validation symbolsTS: editedcodegen/runtime/drizzle-fastify/filter-allowlist.ts, notice disappears andmeta eject --undokeeps the generator as customisedTS:meta eject --undo(unchanged, removes generator, templates and runtime),--undo queries(exit 2),--undo entityon edited copy (refused, exit 2),--force(removes)TS:meta genvsmeta gen --strictwith typo'd@maxLenth(lenient warns and writes; strict exits 1, writes nothing;verifyrejects)TS:entityTypeNamesis exported from@metaobjectsdev/codegen-ts(function)Python:metaobjects init(empty list) vsinit --with-docsthengen; no 'Model / metadata' link in api-docsPython:apiPrefixdefault /api, "/v2", "", "/a/b-c_d.e" in routers and AGENT-API.md; "/bad prefix!" rejected with a clear errorPython:eject entity, gen notice, changedcodegen/runtimefile suppresses notice and no-name--undokeeps it, unchanged--undoremoves copy and runtime,--undo routesexits 2,gen --strictexits 1 and writes nothingC#:dotnet meta init --with-docswrites an active ApiDocsGenerator entry;eject --undo nosuchexits 2;gen --strictflag presentdotnet test MetaObjects.Cli.Testswith DOTNET_USE_POLLING_FILE_WATCHER=1 (334 passed)scripts/ci-local.sh --strict-toolchains(all lanes: gates, showcase, cube, python, java, ts-integ, ts, java-integ, mutation, csharp, kotlin-integ) — LOCAL CI PASSED per CI log, not live-driven✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.