feat: C4 metamodelVersion marker — rolled-up Metamodel spec version (all 5 ports) - #145
Merged
Conversation
…nifest tag Adds a rolled-up Metamodel spec-version string to the registry manifest as the first top-level key (`metamodelVersion: "0"` — pre-1.0/unstable; the 1.0 cut freezes it to "1.0"). A version TAG on the existing expected-registry.json manifest, exposed via METAMODEL_VERSION + emitted by buildRegistryManifest, gated byte-for-byte by registry-conformance (all ports must emit the same string). Not per-provider, not per-file. TS reference; C#/Java/Kotlin/Python fan-out follows against the shared fixture. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
Emit `metamodelVersion: "0"` as the first top-level key in the C# registry manifest, mirroring the TS reference. Byte-matches the shared fixture `fixtures/registry-conformance/expected-registry.json`. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
Add METAMODEL_VERSION = "0" constant and emit metamodelVersion as the first top-level key in build_registry_manifest(), mirroring the TS reference. Registry-conformance byte-match now green (3/3). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
Add METAMODEL_VERSION = "0" constant and emit metamodelVersion as the first top-level JSON key in RegistryManifest.serialize(), matching the TS reference. Both Java and Kotlin conformance runners byte-match the shared expected-registry.json fixture. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
…ts + fixture Change the pre-1.0 spec-version value from "0" to "0.9" — still 0.x/unstable per semver, but signals near-final going into the quiet period. Decoupled from every package line (npm 0.15 / Maven 7.7 etc.); it's the spec version, not a package version. Updated all 5 emitters (TS/C#/Java/Python; Kotlin shares the JVM emitter) + the shared expected-registry.json + docs. The 1.0 cut (G1) flips it to "1.0". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
dmealing
pushed a commit
that referenced
this pull request
Jul 2, 2026
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
This was referenced Aug 20, 2026
docs(versioning)!: two contracts, two numbers — a metamodel break stops forcing a package major
#321
Merged
dmealing
added a commit
that referenced
this pull request
Aug 21, 2026
Review + simplify pass on #322. Four findings, all real; two were the gate failing in exactly the way it takes pains to refuse elsewhere. **It failed open on ANY baseline error.** `manifestAt()` swallowed every exception into `null`, and `main()` read `null` as "pre-marker tag — skipping" and exited 0. So a typo'd `--against v9.9.9` printed `fatal: invalid object name`, then a green tick (verified). Same for a renamed manifest path, malformed baseline JSON, or a partial clone whose objects were never fetched. That is precisely the failure the no-baseline branch already refuses by name — it does not get a side door. The read now distinguishes "genuinely not there yet" (skip) from "something went wrong" (fail), asking the TREE via `git ls-tree` whether the path exists rather than inferring absence from a failed `git show` — in a blobless clone the ref and trees resolve while the blob does not, and that means "not fetched", not "not present". **`satisfies()` accepted a version REGRESSION.** `cur.major > base.major || cur.minor > base.minor` reads as "moved somehow": `1.0 → 0.11` passed it (0 > 1 false, 11 > 0 true), as did `2.0 → 1.9`. Concretely, post-1.0 someone typing `--set 0.11` out of pre-1.0 habit — or a bad merge resolving the manifest to an older value — would ship a release declaring a LOWER metamodel version than the one before it, with every port byte-matching the manifest so registry-conformance stays green too. Now a `(major, minor)` tuple compare. The existing "0.10 → 0.9 must not satisfy" case only covered a SAME-major regression, which is what made this look tested; the two cross-major cases and a forward `1.9 → 2.0` are pinned now. **The pre-marker skip was keyed on the wrong marker.** It triggered when the manifest FILE was absent, but the file predates the `metamodelVersion` key (PR #145) — so `--against v0.15.0`, a documented flag on a real tag, died with `metamodelVersion must be "<major>.<minor>" (got undefined)`. It now skips on the KEY's absence and says so. **`--against --explain` silently became a ref named "--explain"**, which resolved to nothing and (before the first fix) passed. A flag is no longer swallowed as a value. **Stale prose that `--set` re-stales every bump.** `registry-manifest.ts`'s JSDoc said the value is `"0.9"` directly above the line this PR set to `"0.10"`; RELEASING.md, ADR-0035 Am. 2 and the design doc each restated it too. `--set` rewrites declarations, not sentences, so every copy in prose goes stale on the next bump. Dropped the literal from all four rather than adding prose sites to `SITES` — matching a sentence with a regex is the fragile half of that choice, and the value already lives authoritatively one line below. The JSDoc now says why, so it does not come back. Simplifier pass (behaviour-preserving): cached `Boolean(isArray)` and `min ?? 0`, each computed twice per side, matching the `bv`/`cv` and `bMax`/`cMax` caching already in those same functions; replaced the file's only nested ternary. It also caught a miscount in my header comment ("6 sites" / "five port constants" against a 5-entry `SITES`) — the runtime output was always right. Every finding re-probed closed: bad ref → exit 1, flag-as-value → exit 1, `v0.15.0` → skip with a clear reason, `1.0 → 0.11` and `2.0 → 1.9` → false. 43 classifier checks (was 40), `ci-local --only gates` green, TS build + typecheck clean, metadata 2406 pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DhpswkF1NvwxhFWMmdAT15
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.
Adds a single rolled-up
metamodelVersionstring as the first key of the registry manifest, exposed per port and gated byte-for-byte by registry-conformance (all five ports emit the same value). Value"0.9"— pre-1.0/near-final, decoupled from every package line; the 1.0 cut (readiness G1) freezes it to"1.0". Not per-provider, not a per-file stamp — a version tag on the existingexpected-registry.jsonmanifest. Implements readiness C4 (design indocs/1.0-readiness.md). Additive/non-breaking. Cross-port conformance green.🤖 Generated with Claude Code