CLOSED 2026-09-09 — the cut shipped. This is history, not a live gate list. npm / PyPI / NuGet are at
1.0.0, Maven Central at8.0.0, andmetamodelVersionis frozen at1.0. The declared blocking path this doc named in its own footer — G3d, then G1/G2/G4 — is closed end to end: G3d on nine adopter estates that ran the release candidate with their own drift gates enforced (not a baremeta verify), and G1/G2/G4 at the cut itself.Items still marked 🔶/⬜ below (C2, D3, E3, F3 — D2 closed in 1.0.1) were never on that path and are not waivers. They are ordinary post-1.0 work, tracked where that work is tracked. Their marks are left exactly as they stood at the cut rather than tidied, so the record shows what was true then rather than what would look neatest now.
Do not reintroduce a waiting gate in any form — not a release count, not a calendar window, not "one more quiet cut" (ADR-0035 Amendment 3; the reasoning is under G3 below, kept because it is the point). Post-1.0 the live documents are
docs/compatibility-policy.mdfor what 1.0 promises andCHANGELOG.mdfor what changed.
A living checklist for cutting the first stable release. The strategy + the compatibility surface it commits to are in ADR-0035.
Thesis: scaffold-and-own (ADR-0034) shrank the surface 1.0 must keep stable to
the metamodel spine + CLI + wire format + the scaffold-and-own contract — the
generated code is owned/disposable, so its internals are no longer a public API. The
metaobjects-audit skill is the maturity signal that the surface has set. 1.0 is
gated less by features than by dropping the breaking-change rate so we can
promise "no breaking until 2.0."
Status legend: ✅ done · 🔶 in progress · ⬜ not started · ❓ [RATIFY] decision needed.
- ✅ A1. Version-line strategy — RATIFIED 2026-07-02: decouple. npm/PyPI/NuGet
0.15 → 1.0.0, Java/Kotlin7.7 → 8.0.0(both forward — Maven cannot go backwards;1.0.0 < 7.7.1in every resolver), tied by a sharedMetamodel 1.0spec version (OpenTelemetry / protobuf-editions model; the Java jump is the Angular 2→4 precedent). ADR-0035 §2 (rewritten). Unblocks G1; adds C4. - ✅ A2. FR-024 declared-API — RATIFIED: DEFER.
api.*/operation.*/binding.*stay reserved-but-unregistered post-1.0 (already absent fromexpected-registry.json; additive to add later). Removes the last candidate for a breaking round, and is the ratified half of the scope statement G3a asks for. Tracked in #10. - ✅ A3. Deprecated
codegen-ts/generatorsexport — RATIFIED: REMOVE at the cut. Removed as part of the 1.0/8.0 major bump (G2), not before — removing an export is breaking, so the major absorbs it. Consumers use themeta init-scaffolded owned copies. - ✅ A4. Cross-port "own your codegen" — RATIFIED: idiomatic + documented. The
per-port split (TS
meta init; JVM/Python/C# via build config) is intentional, not a parity gap. → D4 written:docs/features/own-your-codegen.md.
- ✅ B1. Compat policy published —
docs/compatibility-policy.md: the covered surface (breaking ⇒ major), what is not covered, the Metamodel-spec-version- decouple scheme, and the MINOR-vs-PATCH rule. (Consumer-facing form of ADR-0035 §1.)
- ✅ B2.
0.x → 1.0migration guide written —docs/features/migrations/0.x-to-1.0.md: version re-baseline + verify strict-default, jsonb parsed-value, timestamp instant-default,@dbColumnTypeslim,index.*/@uniqueremoval, passthrough type-preservation (@convert), deprecated-export removal. FR-024 deferred → no declared-API vocab to migrate.
-
✅ C1.
registry-conformanceenforces the vocabulary byte-identically across all 5 ports. -
🔶 C2. No pending vocabulary churn. The metamodel-1.0 vocabulary program (
0.15.0/7.7.0; ADR-0036/0037/0038) was the intended pre-1.0 vocabulary finalization —field.uri/field.inet,@stringFormat, instant-default timestamps +@localTime, the@dbColumnTypeslim, reverse-nav finders — and ADR-0037 now gives a durable framework for any future addition. Only A2 (FR-024) remains open. Audit the registry for anything still experimental. -
✅ C3. The last breaking metamodel/wire move is
0.25.0/ Maven7.25.0—metamodelVersion0.13→0.14, spent on one theme — a name resolved twice by two different mechanisms — of which the metadata-visible half is that an authored<type>.basenode no longer loads (ERR_ABSTRACT_SUBTYPE_AUTHORED). The full chain, newest first:0.25.0·0.24.1(two previously-loading forms refused, #342/#335) ·0.24.0(the coordinated batch) ·0.21.0·0.15.1/7.7.1·0.15.0/7.7.0·0.14.0/7.6.0. Every one of them is written up in0.x-to-1.0.md, which is the artifact that has to stay current (G3c) — this bullet is a pointer to it, not a second copy of it.This item used to say the last breaking move was
0.15.xand to cite the quiet-period clock; both were stale. The clock is retired — see G3. -
✅ C4.
metamodelVersionmarker SHIPPED (PR #145, all 5 ports,"0.9"). A single rolled-up spec-version string the loader/registry exposes and every port emits, asserted by the conformance matrix — the artifact that lets packages version independently while all claiming "implements Metamodel 1.0." Pre-cut the value is"0.9"(still0.x/ unstable, but near-final going into the quiet period; decoupled from every package line); the cut freezes it as1.0. Design (agreed 2026-07-02):- One rolled-up number, not per-provider.
Metamodel 1.0is a named freeze of the core provider set (distribution-version pattern, à la OTel spec releases / protobuf editions). It is a version tag on the existingfixtures/registry-conformance/expected-registry.jsonmanifest — which is already the byte-exact bill of materials every port is gated against — not a new per-provider versioning system. Per-provider precision lives in that manifest, machine-checked, never human-tracked. - Registry/runtime property, NOT a per-file stamp. It does not go in the YAML/JSON metadata documents (validity is decided by the loader's registered providers, so there is no yaml/json duplication to manage). (An optional per-file "targets Metamodel X.Y" is separable and YAGNI for 1.0.)
- Core vocabulary only. Downstream-added providers are outside the promise (ADR-0023) and do not change this number; an app may version its own providers if it cares — the library's concern is the core set.
- Scope: pick the exposure API per port (e.g.
registry.metamodelVersion), stamp the manifest, assert byte-equality of the string across all five ports inregistry-conformance.
- One rolled-up number, not per-provider.
-
✅ C5. The minor-vs-patch cadence rule is operative (ADR-0035 §3 + Amendments 1 and 2). Written up as the decision table in
docs/RELEASING.mdand applied every cut since0.23.2; Amendment 1 splits "a newly-supported vocab member" three ways (attribute ⇒ PATCH, top-level type ⇒ MINOR, subtype ⇒ PATCH when inert), and Amendment 2 separates the package and metamodel axes. Trigger is new public surface, not code size: package MINOR only for newly-dependable surface (codegen output, CLI flag, a newly-supported vocab member); package PATCH for bugfix/refactor; the Metamodel spec version bumps only on a shared vocabulary/wire change. The fast7.3→7.7minor run was breaking velocity; decouple- this rule ends the churn.
- ✅ D1.
verifystrict-by-default +--lax/-Dmeta.laxconsistent across all 4 CLI ports (shipped 0.14.0/7.6.0). - ✅ D2. CLI flag surface documented + frozen (1.0.1) —
init/gen/verify(+ subverbs,--lax,--template-spec, target/output flags) per port, locked against the CLI architecture (ADR-0015) and surfaced in one matrix. The one divergence this gate existed to surface was F101: the prompt directory was spelled--prompts(Node),--templates-root(Python) and--templates <root>(C#). Resolved by CONVERGING on--promptseverywhere and keeping both old spellings as accepted aliases — additive, so a frozen surface permits it in a patch; the aliases are deprecated in 1.1 and removed no earlier than a major. The matrix lives indocs/features/cli.md. - 🔶 D3. Scaffold-and-own contract stable (TS) — what
meta initscaffolds + theGeneratorinterface owned templates implement. A3 resolved: the deprecatedcodegen-ts/generatorsre-export is removed at the cut (G2). - ✅ D4. "Own your codegen" documented for every port (A4 ratified) —
docs/features/own-your-codegen.md: the TSmeta initflow + the JVM/Python/C# build-config equivalents, side by side.
-
✅ E1. All shared corpora green (metamodel, render, persistence, api-contract, registry, verify) on
main. -
✅ E2. Cross-port api-contract jsonb gate wired for all 5 ports (TS/Python + Java/Kotlin/C#).
-
🔶 E3. Sweep for any per-port-only contract behavior that should be cross-port gated before the freeze. The thesis keeps paying out: the ADR-0039 own-accessor program (
0.15.0/7.7.0) added a sharedextends-inheritance conformance fixture and its independent cross-port review caught a real latent class the sweep missed (an M:N junction inheriting itsidentity.referencechildren was falsely rejected in all three non-TS ports; a Python runtime path dropped an inherited M:N) — assume more lurk, and lead every cross-port fix with a shared fixture.Known instance, measured 2026-09-06 — the four entity-scoped cross-field validators.
validator.comparison,requiredWhen,presentIffandatLeastOneare registered vocabulary in all five ports and are taught by the shippedmetaobjects-authoringskill, but their only enforcement is the databaseCHECKthat TS-ownedmigrateemits (gated bymigrate-ts/test/validator-check/cross-field.test.ts, 7 tests). No port's generated API tier validates them: Python'sentity_model.pyand C#'sEntityGenerator.csdispatch only on the four FIELD-scoped validators (length,array,numeric,regex), and the sharedvalidation-conformancecorpus covers only those same four — zero fixtures across every behavioral corpus touch the cross-field family. They are also four of the 13 entries inregistry-conformance/coverage-report.json'suntestedSubTypes, which is the same list the<type>.basedefect hid in until0.25.0.Consequence for an adopter today: a request body violating a declared cross-field rule is refused by the DATABASE, not by the API, in every port — a constraint violation rather than a 400.
This does not block the cut, and the reason is worth stating so it is not re-litigated: the vocabulary is enforced (the CHECK reaches every port, because schema is TS-owned per ADR-0015), and adding API-tier enforcement later changes no load verdict and no metadata — it is additive, and reaches an adopter as a wrong request failing earlier and more clearly. What must NOT happen is adding a cross-port
validation-conformancefixture for these before the ports implement the tier; that turns three lanes red to record a gap this bullet already records.
- ✅ F1.
metaobjects-auditskill shipped + run against two real deep adopters. - ✅ F2. A real adopter reaches "Deep/Exemplary" with the CI drift gate wired — DONE 2026-09-09, and by nine of them rather than one; see G3d. — the strongest evidence the surface is stable enough to promise. (Both audited adopters were Deep; neither had the drift gate enforced in CI yet — closing that on at least one adopter is the proof point.)
- ⬜ F3. Fold the audit findings that point at the framework back in (e.g. the jsonb runtime-write gaps the gate surfaced) so 1.0 ships without known adoption friction.
-
✅ G1. Execute A1 (the decoupled cut) — DONE 2026-09-09: npm/PyPI/NuGet
→1.0.0, Java/Kotlin→8.0.0, spec version frozen atMetamodel 1.0(C4). Both forward; no backwards move on any registry. -
✅ G2. Remove the deprecated export — DONE 2026-09-07.
entityFile/queriesFile/routesFile/barrelare gone from@metaobjectsdev/codegen-ts/generators; owned copies (meta init,meta eject <name>) are the path, as0.x-to-1.0.md§11 already promised.Scoped by NAME, not by path — the checklist's own shorthand was wrong. That subpath carries ~20 exports and only those four were deprecated; the rest (
promptRender,outputParser,outputPrompt,extractor,renderHelper,traceHelperFile,routesFileHono,namesFile,callableFile) have no other public import path, and the CLI's own prompt-gate warning tells adopters to importpromptRenderfrom exactly there. Of six adopter estates surveyed before the cut, five import only non-deprecated names from that subpath and one imports the four: a path-wide removal breaks all six and the owned-copy remedy applies to one. A3's ratified intent and the published guide both name the four, so this is that decision implemented, not a narrowing of it. -
✅ G3. Scope declared, promise livable, publications current, RC exercised. (G3a/G3b/G3c done 2026-09-06/07; G3d done 2026-09-09 — it was the last gate blocking G1, and G1 followed the same day.)
RULING (2026-09-06, maintainer): the quiet-period criterion is RETIRED. It measured the wrong variable. G3 used to read "at least one coordinated release after the last breaking move with no metamodel-breaking changes, to prove the rate has actually dropped." That is replaced by the three gates below. This bullet records the reasoning, because the doc's other rulings are written so a reader sees a decision rather than a gate that quietly vanished.
The retirement is NOT "the gate never converged." That reading was checked against the tags and is false —
metamodelVersionwas0.10atv0.24.0,0.12atv0.24.1, then0.13atv0.24.2,v0.24.3,v0.24.4ANDv0.24.5: four consecutive coordinated releases with no metamodel move. The gate converged, was ruled satisfied by0.24.2, and held for three more releases.0.25.0then moved it to0.14by decision — the pre-1.0 breaking slot, spent deliberately on one theme.That is exactly why the criterion fails as a gate: it samples a variable the maintainer sets. A quiet period measures whether we CHOSE to break something lately, and reports the answer back to us as though it were evidence about the design. Intent is better declared than sampled. Worse, its incentive runs backwards — the cheapest way to earn 1.0 under it is to stop improving the metamodel, and at this project's cadence the rate it reads is velocity, not instability. Most of
0.25.0's breaking changes were DEFECTS being corrected, which is evidence of convergence, not against it.And the harm it guards against is unprotected post-1.0 regardless. Under ADR-0035 Amendment 2 the metamodel and package axes are separate contracts, so after the cut
^1.0.0accepts1.1.0and a metamodel change reaches an adopter on a routine update with no package major to refuse it. A quiet release before the cut buys no part of that. What protects an adopter is the correction bar and themeta upgradepath (G3b), not a calendar.What replaces it:
-
✅ G3a — the declared scope is covered, and no known gap needs NEW vocabulary. DECLARED 2026-09-07 (maintainer).
What Metamodel 1.0 covers is the registered vocabulary named by
fixtures/registry-conformance/expected-registry.jsonat the cut commit, together with the canonical authoring/interchange format and the wire contract — the setdocs/compatibility-policy.mdlists under "Covered by the METAMODEL version." Everything still outstanding only ADDS to that set. Nothing outstanding changes or removes any part of it.The declared claim is deliberately the weaker one. It is not "the vocabulary is finished" — FR-024's declared-API tier and FR-034's ecosystem tier are both real, both wanted, and both still unbuilt. It is that finishing them is additive, so freezing now costs an adopter nothing later. Three checks back it:
-
Nothing in the registry is experimental.
expected-registry.jsoncarries zeroexperimentalmarkers, anddocs/compatibility-policy.mdalready excludes "anything explicitly marked experimental or reserved and not yet in the registry" from the covered set — so the covered surface IS the manifest at the cut commit. -
Every outstanding roadmap item is ADDITIVE. 1.1 holds FR-024's declared-API tier (
api.*/operation.*/binding.*— reserved, unregistered, additive by construction: A2), FR-034's ecosystem tier (same shape), FR-026's view-render polish (TS web only), FR-028's object↔JSON parity +meta export, and FR-030's serializer SPI. FR-037 R3/R4/R5 and the FR-038 stub generator are explicitly additive (spec/roadmap.md). No breaking change is chartered anywhere. -
The open issues are additive, with ONE named exception to watch. 31 are open. The bulk are CLI surfaces over existing vocabulary (
meta why#306,@deprecatedenforcement #305,meta fmt#304), roadmap FRs already tiered post-1.0 (#159 and #200 say so in their titles), or additions to shipped features (#222 extends #207's filter). The exception is #202 —@requiredis global to the object, and a response object populated by two producers cannot express it. That touches the semantics of a SHIPPED attribute and has no design yet, so whether its fix is additive is not yet knowable. The likely shape is additive (a way to express per-producer required-ness alongside@required, not a redefinition of it), but this bullet does not claim that — it names #202 as the one open item that could force a non-additive change, so a future reader sees it was considered rather than missed.Correction (2026-09-07): this bullet first said "the three open issues," a count taken from a summary listing only the three newest. There are 31. The audit above is the corrected one.
ADR-0035 §1 already promises this by excluding reserved-but-unregistered vocabulary from the compat surface. G3a is where the project records that it checked, rather than assumed. Re-run the three checks at the cut commit before G1 — the declaration is about the manifest that ships, not the one that was current when this was written.
-
-
✅ G3b — the 1.0 promise is livable: the correction bar is ratified. DONE 2026-09-06.
docs/compatibility-policy.mdnow carries "Correcting input we wrongly accepted" — the three-part bar under which a form that never had a valid meaning may stop loading in a PATCH, explicitly NOT covering retirement of vocabulary that worked. This was the real blocker. Without it, the first post-1.0 defect of the kind this project ships routinely is either unfixable or a 2.0, because ADR-0023's sealed registry gives a refusal no deprecation shim. The same section discloses thatcheck-metamodel-version.mjscannot see this class — it diffs the manifest, and the correction lives in loader validation. -
✅ G3c — the published artifacts are current at the cut. DONE 2026-09-06. B1 and B2 are published with 1.0, so a stale one ships as the promise.
0.x-to-1.0.mdhad ended at the0.24.0batch and carried none of0.24.1's two refusals or0.25.0's six breaks; both are written now. Re-check this at the cut commit, not before it. -
✅ G3d — an adopter estate runs the RELEASE CANDIDATE before promote. DONE 2026-09-09. Nine maintainer-owned estates ran
1.0.0-rc.7(and the advisory it produced,rc.8) with their own declared gates enforced — not a baremeta verify, the command each project's CI actually runs. Evidence, per estate: live-Postgresverify --dbschema in sync on three of them; a full 32-migration chain replayed from an EMPTY database followed by 1795/1822 tests green; a JVM adopter's whole ten-module Maven reactor built and tested (382 tests, 0 failures); two Python adopters' prompt- and codegen-drift gates green at a version READ BACK from the interpreter rather than taken from a pin; and a local-D1verify:dbon a fourth.It found five things, which is the point of the gate rather than an argument against it: a build-output false positive in the advisory scanner (fixed, and now measured 41→40 and 27→26 findings on two estates, removing exactly the bundles); a D1 failure that named a warning as its cause (fixed, measured on the failure itself); an adopter script that named no database, so one drift gate could never have passed (an estate defect, but the reason that gate had never run); and — twice, in two independent estates —
<EntityFetcherProvider value={…}>, the prop 1.0 renamed away, shipped past both projects' drift gates, unit tests and builds because neither hadtscin its gate chain. That last one became a newmeta verifyadvisory before the cut, validated across all nine estate codebases (0 false positives over six real provider mounts, and it fires on the exact line when the defect is reintroduced).Original ruling, kept because the reasoning is the point: RULED 2026-09-07 (maintainer): this DOES block the cut.
Concretely: publish the RC, wire it into a real adopter's CI with the drift gate ENFORCED (not advisory), run
meta gen+meta verify+ a migration against that estate, and fix what it finds before promoting. This is the evidence half that replaces the retired quiet period — real usage instead of a calendar.The argument against it was heard and rejected, and is recorded so the decision is legible. All six known adopters are maintainer-owned, so this is closer to self-validation than to independent evidence, and it makes the cut wait on work in a repository that is not the one being frozen. The ruling is that it is still worth it: a conformance corpus gates the five ports against each other, never against use, and every port could agree perfectly on a contract that is wrong in practice. The
0.24.3and0.24.5lines are the precedent — both were largely defects found by running the documented path, which no gate in this repo was asking.Pairs with F2, which asks the same question from the adoption-maturity side. F2 is satisfied by an adopter reaching Deep/Exemplary with the gate wired; G3d additionally requires that the RC specifically — not a released version — is what got exercised.
Deliberately NOT a gate: another waiting period, in any form. Neither a release count, a calendar window, nor "one more quiet cut." If a replacement gate is ever proposed whose pass condition is the passage of time or the absence of our own recent choices, it belongs to the criterion retired here.
-
-
✅ G4. Cut Metamodel 1.0 — DONE 2026-09-09 through the
releasingskill (RC → smoke → confirm → promote), with the migration guide (B2) + compat policy (B1) published alongside. G3a's three evidence checks and G3c were re-run AT the cut commit, as those items require.
Closed 2026-09-09 with the 1.0.0 / 8.0.0 cut. Before that: the quiet-period criterion (G3)
was RETIRED on 2026-09-06 and replaced by G3a/G3b/G3c; G3b and G3c were closed the same day
(the compatibility policy gained the correction bar; the 0.x → 1.0 migration guide gained
0.24.1 and 0.25.0). C3 and C5 were stale and were corrected. The last breaking metamodel
move under the 0.x line was 0.25.0 / 7.25.0 (metamodelVersion 0.14); 1.0.0 /
8.0.0 froze it at 1.0. A1 ratified — decouple.
Owner: maintainer. This checklist is finished — do not add items to it. Post-1.0 work
belongs in spec/roadmap.md and the issue tracker; the 1.0 promise itself lives in
compatibility-policy.md.