Skip to content

Commit ce9b01c

Browse files
authored
feat: enforce eject contract across generators and gate final releases on it (#435)
* test(eject): hold every generator in every port to the eject contract Every stock generator is meant to be ejectable, with the owned copy holding its emit. Three pieces of work each made a named list ejectable and no test stated the rule, so the newest generators shipped as thin copies with every lane green. This adds the test, in all five ports, driven by one shared file. The shared file is fixtures/generator-registry-conformance/registry.json. Each generator now carries "eject": "full" or an exemption with its reason and ruling. A top-level "eject" block holds, per port, the packaged modules an owned copy may still import (the engine surface), the exempt non-generator emitters, and a "pending" list of every place the contract does not hold yet. Each port's eject-contract test makes three assertions: - Census: every generator implementation the port ships is in its registry. - Completeness: every registered generator ships eject source unless exempt, and the other lists of ejectable generators are derived from the registry or held equal to it. - Depth: each generator, ejected alone, compiles or loads, references nothing in the codegen packages outside the engine surface, and emits what the packaged generator emits. A pending entry that passes fails the test, and so does a gap that is not listed. PENDING_LIST_MAY_BE_NON_EMPTY in the shared file skips each port's "the pending list is empty" test until the release branch sets it false: 1.1.0 does not ship with entries left. The census registered seven generators that no registry listed: TypeScript requirements and agent-docs, and Java plantuml, html-docs, ai-docs, json-schema and the deprecated mustache. Registering PlantUML put it in two registry-driven suites for the first time; both failed on a StringIndexOutOfBoundsException for any single-segment package, which is fixed here. The tests that asserted a gap are gone, with the hand-typed lists they pinned. ADR-0034 Amendment 8 records the six rulings, the engine surface, the exemptions, the gap list and the release gate, and supersedes FR-040 section 4.2 and the eject-in-every-port design's thin-copy rule. This closes none of the pending gaps. It also found one the audit did not list: an owned Java entity constructs the packaged extractor generator when no extractor is in the run (gap 13). * feat(release): refuse a final release while the eject pending list has entries The eject contract's release rule was a switch a person had to remember to flip. It is now enforced by the release scripts, and the guard's scans are tighter. - scripts/eject-release-gate.mjs refuses a version with no prerelease suffix while fixtures/generator-registry-conformance/registry.json has an entry in eject.pending, or while PENDING_LIST_MAY_BE_NON_EMPTY is anything but false. scripts/release.mjs calls it first in its preflight and scripts/finish-release.mjs calls it as gate 0, before the tag. A release candidate is allowed. An unreadable manifest is refused. - The "pending list is empty" test now reports as skipped in TypeScript and C# while the switch is on, as it already did in Java, Kotlin and Python. It used to return early and count as a pass. - The TypeScript reference scan also counts type-only imports, inline import("...") types and dynamic import() of a package module. The Python scan also counts importlib.import_module and __import__ on a string literal. Each scan has a test over a file whose answer is known. - The tighter TypeScript scan found the owned requirement-tests generator importing two types from the packaged generator and its template. The renderer's argument record and signature are now declared beside the requirement walk. The package's exports are unchanged. relation-resolver.ts and render-context.ts join the TypeScript engine surface: an owned copy imports their types. - ADR-0034 Amendment 8 states the limits of the guard with the reason for each, lists the engine-surface names that were a judgment call, and records that gap 13 closes with the Java work on gaps 4, 5 and 7. * test(eject): read the pending census names once per manifest pass The Java registry-conformance test rebuilt the set of pending census names for every manifest entry. It now builds it once. Also splits a comment in the TypeScript generators barrel back into its two subjects. * no-mistakes(review): remove eject release switch; execute release.mjs wiring test * no-mistakes(review): gate publish workflows; dedupe shape checks; widen Python scope * no-mistakes(review): accept PEP 440 prereleases; gate hand-typed publish routes * test(eject): drop the nine C# pending entries the emitter hand-over closed Rebased onto the change that hands the C# emitters over on eject and makes api-docs ejectable there. All nine C# pending entries now pass the guard, so they leave the list: the api-docs census entry and eight depth entries (gaps 4 and 5). 49 entries are left: typescript 22, java 10, python 9, kotlin 8. - C# eject now writes the emitters beside the generator, so the contract test compiles and scans every file eject writes under codegen/generators. It used to require exactly one file. - DocsRunFacts, GeneratorApplicability and IDocsGenerator join the C# engine surface: the owned api-docs copy reads the docs run's facts and asks the packaged generators whether they apply, and writes no output through them. - ADR-0034 Amendment 8: gaps 4, 5 and 6 are closed in C#. * no-mistakes(review): compare real paths in eject gate entry guard
1 parent 0293bcb commit ce9b01c

56 files changed

Lines changed: 3855 additions & 212 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/publish-csharp.yml‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ jobs:
4141
steps:
4242
- uses: actions/checkout@v4
4343

44+
# A final version is not published while the eject pending list has entries
45+
# (ADR-0034 Amendment 8). A release candidate passes, which is why the `version`
46+
# input is read first.
47+
- name: Eject release gate
48+
env:
49+
VERSION_INPUT: ${{ github.event.inputs.version }}
50+
run: |
51+
COMMITTED="$(sed -n 's:.*<Version>\(.*\)</Version>.*:\1:p' server/csharp/Directory.Build.props | head -n 1)"
52+
node scripts/eject-release-gate.mjs "${VERSION_INPUT:-$COMMITTED}"
53+
4454
- name: Set up .NET
4555
uses: actions/setup-dotnet@v4
4656
with:

‎.github/workflows/publish-java.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ jobs:
3333
steps:
3434
- uses: actions/checkout@v4
3535

36+
# A final version is not published while the eject pending list has entries
37+
# (ADR-0034 Amendment 8). A release candidate passes.
38+
- name: Eject release gate
39+
run: node scripts/eject-release-gate.mjs "$(sed -n 's:.*<version>\(.*\)</version>.*:\1:p' server/java/pom.xml | head -n 1)"
40+
3641
- name: Set up JDK 21 + Maven settings (Central server + GPG import)
3742
uses: actions/setup-java@v4
3843
with:

‎.github/workflows/publish-npm.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ jobs:
3939
steps:
4040
- uses: actions/checkout@v4
4141

42+
# A final version is not published while the eject pending list has entries
43+
# (ADR-0034 Amendment 8). A release candidate passes.
44+
- name: Eject release gate
45+
run: node scripts/eject-release-gate.mjs "$(node -p "require('./server/typescript/packages/cli/package.json').version")"
46+
4247
- name: Set up Bun
4348
uses: oven-sh/setup-bun@v2
4449

‎.github/workflows/publish-python.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ jobs:
2828
steps:
2929
- uses: actions/checkout@v4
3030

31+
# A final version is not published while the eject pending list has entries
32+
# (ADR-0034 Amendment 8). A release candidate passes.
33+
- name: Eject release gate
34+
run: node scripts/eject-release-gate.mjs "$(sed -n 's/^version *= *"\(.*\)".*/\1/p' server/python/pyproject.toml | head -n 1)"
35+
3136
- name: Set up uv
3237
uses: astral-sh/setup-uv@v5
3338

‎AGENTS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ MetaObjects is a **cross-language metadata standard** for declaring typed entity
66

77
The metamodel is the **durable spine**; generated code is the **disposable artifact**. Substrate is local-first: typed metadata lives in your repo, generated code is idiomatic per-language output with **no proprietary runtime**: the generated entity and model code imports nothing from MetaObjects at runtime, and the REST, prompt, client and runtime tiers import ordinary Apache-2.0 packages you can vendor, fork or replace. If `@metaobjectsdev/*` disappears tomorrow, you keep working code.
88

9-
**Core vs helpers (ADR-0034 Amendment 3, 2026-09-22).** Only the core is a product promise: the metamodel, loader, canonical format and registry; runtime metadata access (the `ObjectManager` and its drivers; the HTTP adapters that mount it on a web framework are helpers, 2026-09-24 ruling); schema migrations (`meta migrate`); the drift gates (`meta verify`); prompt render and the reply parser. Every generator that writes application code into an adopter's repo — routes, controllers, ORM wiring, DTOs, forms, grids, hooks, filter allowlists — is a **reference helper**: it must compile and pass its reference fixtures, and the adopter copies it with `meta eject` and owns the copy. Mechanical test: what the tool guarantees is core; what it writes into your repo is a helper. Every port can eject: `meta eject` (TypeScript), `metaobjects eject` (Python), `mvn metaobjects:eject` (Java/Kotlin), `dotnet meta eject` (C#). Not every generator ships a reference copy: `meta gen --list` marks the ones that do not `package-only` (in TypeScript: `template` alone; ADR-0034 Amendment 3 correction, 2026-09-26, narrowed by Amendments 5 and 7). When you find a generator defect, fix the reference; do not describe it as a broken core guarantee.
9+
**Core vs helpers (ADR-0034 Amendment 3, 2026-09-22).** Only the core is a product promise: the metamodel, loader, canonical format and registry; runtime metadata access (the `ObjectManager` and its drivers; the HTTP adapters that mount it on a web framework are helpers, 2026-09-24 ruling); schema migrations (`meta migrate`); the drift gates (`meta verify`); prompt render and the reply parser. Every generator that writes application code into an adopter's repo — routes, controllers, ORM wiring, DTOs, forms, grids, hooks, filter allowlists — is a **reference helper**: it must compile and pass its reference fixtures, and the adopter copies it with `meta eject` and owns the copy. Mechanical test: what the tool guarantees is core; what it writes into your repo is a helper. Every port can eject: `meta eject` (TypeScript), `metaobjects eject` (Python), `mvn metaobjects:eject` (Java/Kotlin), `dotnet meta eject` (C#). Not every generator ships a reference copy: `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; ADR-0034 Amendment 3 correction, 2026-09-26, narrowed by Amendments 5 and 7, and held to the shared manifest by Amendment 8). When you find a generator defect, fix the reference; do not describe it as a broken core guarantee.
1010

1111
## Six pillars
1212

‎CHANGELOG.md‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,29 @@ generator catalog and the Atlas evaluation are 1.2 work.
105105

106106
### Added
107107

108+
- **The eject contract: every generator hands over its emit, and a test in every port holds
109+
it to that** (ADR-0034 Amendment 8). `fixtures/generator-registry-conformance/registry.json`
110+
now says, for each generator, `"eject": "full"` or an exemption with its reason, and lists
111+
per port the packaged modules an owned copy may still import (the engine surface). Each
112+
port's eject-contract test asserts three things from it: every generator the port ships is
113+
in its registry; every registered generator ships eject source unless exempt; and each
114+
generator, ejected alone, compiles or loads, references no emit code left in the package,
115+
and emits what the packaged generator emits. `template` is exempt in every port that ships
116+
it, with the deprecated Java Mustache generator and the docs-site builders. Where the
117+
contract does not hold yet, the file's `eject.pending` list names the generator, the port
118+
and the packaged modules its owned copy still calls. A port's test fails on an unlisted gap
119+
and on a listed one that has closed. The release does not ship while the list has entries:
120+
`scripts/release.mjs`, `scripts/finish-release.mjs` and the four publish workflows refuse a
121+
version with no prerelease suffix until the list is empty (`scripts/eject-release-gate.mjs`); a
122+
release candidate is allowed. No generator emits different bytes because of this change.
123+
- **Seven generators that no registry listed are registered**, so `--list`, the eject
124+
commands and the registry-driven tests see them. TypeScript: `requirements` (the ledger
125+
pages `meta docs` writes) and `agent-docs` (the pages `meta docs --agent` writes), both
126+
neutral and both still run by `meta docs`; a `generators` entry naming either string is
127+
refused the way `"docs"` is. Java: `plantuml`, `html-docs`, `ai-docs`, `json-schema` and the
128+
deprecated `mustache`, each by the class name a pom already uses. None of the seven ships
129+
eject source yet; `mustache` will not.
130+
108131
- **The Java `entity` and `extractor` generators can be ejected.**
109132
`mvn metaobjects:eject -Dnames=entity,extractor -Dport=java` copies each generator, with
110133
the rest of its emit, into your `codegen/` module. They were the two Java generators that
@@ -872,6 +895,13 @@ until you regenerate.
872895

873896
### Fixed
874897

898+
- **The Java PlantUML generator no longer throws on a single-segment package.**
899+
`PlantUMLGenerator` failed with `StringIndexOutOfBoundsException` for any model whose
900+
package has one segment (`acme`, not `acme::shop`), which is most small models. It was in
901+
no registry, so no registry-driven test ran it; registering it (above) put it in two such
902+
suites and both failed. A package with no parent now draws no parent namespace. Output
903+
for a multi-segment package is unchanged.
904+
875905
- **A doc generator skipped by `meta gen` is now reported in every output form.** `meta docs`
876906
is the door for documentation (ADR-0025), so `meta gen` warns about and skips a `docsFile()`
877907
or `apiDocsFile()` listed under `generators`. That has not changed. What changed is who hears

‎agent-context/skills/metaobjects-codegen/references/typescript.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,10 @@ name. Import by default and eject to customize: an owned copy you never change o
126126
behind the reference. (`entityFile` / `queriesFile` / `routesFile` / `barrel` were removed
127127
from that subpath at 1.0 and restored in 1.1, so on a 1.0.x install an owned copy is the
128128
only path for those four.) The engine primitives come from the package main entry,
129-
`@metaobjectsdev/codegen-ts`. One entry cannot be ejected and is `package-only` in
130-
`meta gen --list`: the `template` primitive, which has no emit logic of its own.
129+
`@metaobjectsdev/codegen-ts`. Three entries cannot be ejected and are `package-only` in
130+
`meta gen --list`: the `template` primitive, which has no emit logic of its own, and
131+
`requirements` and `agent-docs`, the pages `meta docs` writes, which do not ship a
132+
reference template yet.
131133

132134
Three ejectable entries behave differently from the rest:
133135

‎docs/CONFORMANCE.md‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ the corpora above do two different jobs. Only the first is a promise to adopters
8787
exporter's output, a TypeScript reference helper),
8888
`requirement-test-identity-conformance/` (which tests the `requirement-tests` reference
8989
generator yields from a ledger and what each is called; the text it writes is not
90-
pinned) and the codegen-compile gate. They check that the reference generators are correct
91-
starting points; an adopter's ejected copy is theirs and is not gated. The hand-rolled
90+
pinned), the codegen-compile gate and the eject contract (below). They check that the
91+
reference generators are correct starting points; an adopter's ejected copy is theirs and is not gated. The hand-rolled
9292
reference-server lane of `api-contract-conformance/` still pins the wire contract that
9393
the runtimes and the client speak, which is core.
9494

@@ -99,6 +99,23 @@ corpora, or between its *vocabulary* and its *behaviour* — never gets a row of
9999
own, and its boundary ends up discoverable only from the feature doc. The split ones
100100
are recorded here instead.
101101

102+
**The eject contract** ([ADR-0034](../spec/decisions/ADR-0034-codegen-scaffold-and-own.md)
103+
Amendment 8). A gate, not a corpus: it has no fixtures of its own, and reads
104+
`generator-registry-conformance/registry.json`.
105+
106+
- *Gated in all five ports.* Each port's eject-contract test asserts that every generator it
107+
ships is in the registry (census), that every registered generator ships eject source
108+
unless the shared file exempts it (completeness), and that each generator ejected alone
109+
compiles or loads, references nothing in the codegen packages outside the engine surface
110+
and emits what the packaged generator emits (depth). TypeScript:
111+
`cli/test/eject-contract.test.ts`. C#: `EjectContractTests`. Java: `EjectContractTest` in
112+
`maven-plugin`. Kotlin: `EjectContractTest` in `codegen-kotlin`. Python:
113+
`tests/conformance/test_eject_contract.py`.
114+
- *Not yet true everywhere, and the shared file says where.* `eject.pending` lists every
115+
generator that still fails an assertion. A port's test fails when an unlisted gap appears
116+
and when a listed one closes without its entry being removed.
117+
- *Not gated: an adopter's owned copy.* The contract is about what the reference hands over.
118+
102119
**Capability requirements** ([features/requirements.md](features/requirements.md)):
103120

104121
- *Vocabulary — gated in all five ports.* The types, their attributes and their closed

‎docs/RELEASING.md‎

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
> - **Maven Central** → push a `java-v<maven-version>` tag (`publish-java.yml` signs and deploys
1111
> from repo secrets), or locally `~/.m2/settings.xml` (server ids `central` +
1212
> `gpg-credentials`) + the local GPG signing key and `mvn -Prelease deploy` from
13-
> `server/java` (autoPublish — no manual staging promotion). One route per version, never both.
13+
> `server/java` (autoPublish, so no manual staging promotion; run the eject release gate
14+
> first, see the Java procedure). One route per version, never both.
1415
> - **NuGet** → keyless OIDC via the `publish-csharp.yml` workflow: `gh workflow run
1516
> publish-csharp.yml` (or push a `csharp-v<version>` tag).
1617
>
@@ -48,6 +49,26 @@ Decide explicitly for every branch the scan returns: merge it into this cut, or
4849
Note that `no-mistakes/*` branches are usually gate replays of work already on `main` — confirm
4950
rather than assume. Do not skip past the list.
5051

52+
**The eject pending list (1.1.0).** 1.1.0 does not ship while
53+
`fixtures/generator-registry-conformance/registry.json` has an entry in `eject.pending`
54+
(ADR-0034 Amendment 8). The scripted paths enforce it: `scripts/release.mjs` (first check of
55+
its preflight), `scripts/finish-release.mjs` (gate 0, before the tag) and the four publish
56+
workflows (the step after checkout) refuse a version with no prerelease suffix while the list
57+
holds an entry. A release candidate (`1.1.0-rc.3`, or `1.1.0rc3` on PyPI) is allowed.
58+
59+
A publish you type by hand passes through none of those, so run the gate yourself as the
60+
command before it. This document has it before each of the four: the npm promote in step 3,
61+
the isolated one-package `bun publish`, the PyPI fallback `uv publish`, and the local
62+
`mvn -Prelease deploy`. It exits non-zero on a refusal:
63+
64+
```
65+
node scripts/eject-release-gate.mjs <version> # the version that registry is about to receive
66+
```
67+
68+
To clear it for a final release: close the gaps and delete their entries. There is no other
69+
step. Each port's eject-contract test fails on a gap that is not listed, so an entry cannot be
70+
deleted before its gap is closed.
71+
5172
**Why this is step 0 and not a nicety:** `0.21.1` shipped *without* a `timestampMode` fix that was
5273
already written and sitting on an unmerged branch. Because versions are immutable on all four
5374
registries, the only correction was a full coordinated `0.21.2` within the hour — four publishes, an
@@ -549,6 +570,7 @@ scripts/ci-local.sh --strict-toolchains # must end "LOCAL CI PASSED"
549570
# bump the candidate set to the final <version>
550571
rm bun.lock && bun install
551572
# verify packed deps (rule 2); commit "chore(release): <version>"
573+
node scripts/eject-release-gate.mjs <version> # must exit 0; refuses while eject.pending has entries
552574
( cd <pkg-dir> && bun publish ) # default tag = latest, tier order
553575
git push origin main # NO tag here — see golden rule 7
554576
```
@@ -587,8 +609,8 @@ CDN-cached and lags right after publish.
587609
## Isolated patch (one package)
588610
589611
If only one package changed (e.g. a `cli` bugfix), bump just that package, `rm bun.lock && bun
590-
install`, verify, and `bun publish` it — the others stay at their current version. Tag scoped
591-
(e.g. `cli-v0.5.1`).
612+
install`, verify, run `node scripts/eject-release-gate.mjs <version>` (it must exit 0), and
613+
`bun publish` it. The others stay at their current version. Tag scoped (e.g. `cli-v0.5.1`).
592614
593615
# Releasing the C# packages to NuGet
594616
@@ -750,9 +772,10 @@ Project `metaobjects` → **Settings → Publishing → Add a new GitHub publish
750772
python-v<version>`. **Do not ALSO publish by hand.** The `1.0.3` cut ran a local
751773
`uv publish` and then pushed the tag, so the workflow went red with `400 File already
752774
exists` on a release that had shipped — a red run that reads exactly like a failed publish.
753-
The local route is the fallback only when the workflow cannot run: `UV_PUBLISH_TOKEN=<a PyPI
754-
API token> uv publish dist/*` from `server/python`, and then do not push a `python-v*` tag
755-
for that version.
775+
The local route is the fallback only when the workflow cannot run. Run
776+
`node scripts/eject-release-gate.mjs <version>` from the repository root first (it must exit
777+
0; the workflow runs the same gate), then `UV_PUBLISH_TOKEN=<a PyPI API token> uv publish
778+
dist/*` from `server/python`, and then do not push a `python-v*` tag for that version.
756779
4. **Verify:** `curl -s https://pypi.org/pypi/metaobjects/json | python3 -c "import sys,json;print(json.load(sys.stdin)['info']['version'])"`.
757780
758781
# Releasing the Java/Kotlin modules to Maven Central
@@ -789,7 +812,9 @@ versioned on its own major line — npm major + 7, so `7.x` while npm was `0.x`
789812
with `Component … already exists`): push a `java-v<maven-version>` tag, and
790813
`publish-java.yml` runs the deploy below in CI with the signing secrets (how `8.0.4`
791814
shipped; it takes no version override, so it deploys the COMMITTED version — do not fire
792-
it for an uncommitted pre-release); or run `mvn -Prelease deploy` from `server/java`.
815+
it for an uncommitted pre-release); or run
816+
`node scripts/eject-release-gate.mjs <maven-version>` from the repository root (it must exit
817+
0; the workflow runs the same gate) and then `mvn -Prelease deploy` from `server/java`.
793818
The `central-publishing-maven-plugin`
794819
(`<publishingServerId>central</publishingServerId>`, `<autoPublish>true</autoPublish>`) uploads
795820
the signed bundle and auto-releases — **no manual staging → release promotion**. Auth + the GPG

‎docs/features/own-your-codegen.md‎

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,13 @@ you how far it is from the reference.
159159

160160
## The docs generators and `shared-model` (TypeScript)
161161

162-
Every stock TypeScript generator can be ejected except one. `template` is the Mustache
162+
Every stock TypeScript generator can be ejected except three. `template` is the Mustache
163163
primitive: you already supply its template, its walk and its name, and it has no emit
164-
logic of its own, so a copy would own nothing. `meta gen --list` marks it `package-only`.
165-
The C#, Java and Python ports rule the same way for theirs (ADR-0034 Amendment 7).
164+
logic of its own, so a copy would own nothing. That exemption is permanent, and the C#,
165+
Java and Python ports rule the same way for theirs (ADR-0034 Amendment 7). `requirements`
166+
and `agent-docs`, the pages `meta docs` and `meta docs --agent` write, were registered by
167+
the eject census and do not ship a reference template yet (ADR-0034 Amendment 8).
168+
`meta gen --list` marks all three `package-only`.
166169

167170
Three of the ejectable ones differ from the rest in what runs them or in what the copy may
168171
change.
@@ -274,7 +277,11 @@ directory that holds `metaobjects.config.ts`.
274277
## What `meta eject` hands over, and what stays core (TypeScript)
275278

276279
Owning a generator means owning the code it writes **and the code that code calls**,
277-
when that code is a helper. On TypeScript the generated routes call an HTTP adapter —
280+
when that code is a helper. That is the rule for every generator in every port
281+
(ADR-0034 Amendment 8). It is not yet true of all of them: several owned copies still call
282+
emit code in the package, and `fixtures/generator-registry-conformance/registry.json` lists
283+
each one under `eject.pending` until it is closed. What follows is the part that is
284+
already handed over. On TypeScript the generated routes call an HTTP adapter —
278285
the mount helpers, the filter/sort parser, the error envelopes, pagination — and that
279286
adapter is where most route defects have lived. So ejecting a generator whose output
280287
calls it copies the adapter's source too:
@@ -997,10 +1004,11 @@ against your copy, ignoring the package line and `^// metaobjects:owned-support`
9971004
have no edits of your own (`-Dlist` says `identical`), re-eject with `-Dforce`.
9981005

9991006
`-Dport` is needed only when a name exists in both ports and your project's dependencies
1000-
do not say which. Every Kotlin generator is ejectable, and every Java generator except
1001-
`template`. `template` is the Mustache primitive: the template you supply is its emit
1002-
logic, so the generator has none of its own to hand over. That holds in every port that
1003-
ships it.
1007+
do not say which. Every Kotlin generator is ejectable. Every Java generator is too, except
1008+
`template`, the deprecated `mustache`, and four that are registered and do not ship eject
1009+
source yet (`plantuml`, `html-docs`, `ai-docs`, `json-schema`). `template` is the Mustache
1010+
primitive: the template you supply is its emit logic, so the generator has none of its own
1011+
to hand over. That holds in every port that ships it.
10041012

10051013
## C#: `dotnet meta eject`
10061014

0 commit comments

Comments
 (0)