Bitspark constellation
accepted source ↗

0027 — release-identity vocabulary: the component release is the atomic unit; a repo tag is only a batch label

Status: accepted Date: 2026-06-17

Ratifies the release-identity question left open by atlas#507 and refines — without yet changing the machinery — the identity assumptions in 0004, 0007, 0008, 0023, and 0025. Grounded in research-docs/0002-polyglot-cross-repo-dependency-coherence.md and the external-expert advice recorded beside it.

This ADR establishes vocabulary and a boundary. It deliberately does not remove the substrate's global closure; that is later, sequenced work (see Consequences → Roadmap). The closure stays in force until its replacements exist.

Context

The substrate (0008) holds the whole multi-repo, multi-language fleet to one coherent cross-repo version closure at every release. docs/ATLAS_FRICTION.md traces six recurring failure classes to a single root: an independently-released, polyglot, multi-repo system forced through one global closure, with the release-identity question never ratified — "is a release identified by its git tag, or by its per-lane manifest versions?" (atlas#507).

The acute symptom: an additive release of a multi-component repo deadlocks. When only one component of ontos changed (ontos-data), the unchanged components (ontos-core, ontos-codec) legitimately did not re-publish their byte-identical npm packages — so their TS lane still recorded the old commit while the new repo tag pointed at the new one. The one-commit rule (a per-component anti-skew check) then refused the release: "a release's lanes must not mix two commits." The only way through was to force-republish byte-identical code at new version numbers — exactly the wrong long-term discipline.

The root error is a conflation of three different things under one word ("release"): the repo tag, the per-component identity, and the per-lane coordinate. External-expert review (research-docs/0002…) and our own analysis converge on the fix: name them distinctly, and make the component release — not the repo tag — the atomic identity.

Decision

1. The vocabulary (ratified terms)

  • Lane — one language output of a component: its Rust crate, its Go module, or its npm package. ("tri-code" components have three lanes; most components have one.)
  • Lane coordinate — the native, per-ecosystem locator of a lane: a Cargo git tag → resolvedCommit, a Go module@version + go.sum, an npm version + integrity + gitHead. Lane coordinates are evidence, not identity.
  • Logical component version — the single human-facing version of a component (e.g. ontos-data 0.5.0), shared across its lanes where the ecosystems permit and supplied by the component release where they do not. This is the component's identity.
  • Component release — the atomic identity object: one component, built from one source commit for that component release, with one logical version, all its supported lanes, and one verification verdict (for tri-code components, one byte-parity vector-suite pass). A component release is immutable and content-addressable.
  • Repo batch — a repo-level git tag (e.g. ontos v0.4.0). It is a human communication label for a set of component releases cut together. It is NOT the identity of the components inside it, and it is NOT a source identity for an unchanged component.
  • Wire epoch — the byte-format-compatibility identity of a serialization format: the byte language a version emits/accepts, decoupled from the logical version. A format carries a writerEpoch (what it emits) and a set of readerEpochs (what it promises to read). Compatibility is asserted between epochs, never inferred from a semver major.
  • Deployed service revision — what a running service reports about itself: its git revision, the component versions it links, and the wire epochs it writes and reads.
  • Environment compatibility verdict — a per-(environment, runtime-edge) judgment that two co-deployed services exchanging a format are wire-compatible (producer.writerEpoch ∈ consumer.readerEpochs). Owned by the runtime plane, scoped to one environment.

2. The principles these terms ratify

  1. The component release is the atomic unit of identity and admission. A repo batch is a label over a set of component releases. Unchanged components are referenced by their prior component-release id — they are never force-republished to satisfy a repo-level tag or a one-commit rule.
  2. Lane coordinates are evidence; the logical component version is identity. When the ecosystems can share one version string, they should; when a lane carries an independent version line, the component release still supplies the shared identity.
  3. Two component categories, both legitimate:
    • Lockstep component — its lanes are one artifact surface that must be published together from one commit (e.g. a contract/kernel pair; a known runtime break occurred when two such npm packages were built from different commits). The one-commit rule is correct here, scoped to the single component.
    • Independent component — independently versioned within a multi-component repo (e.g. ontos-core / ontos-codec / ontos-data). A new release of one does not force the others to republish.
  4. Three gates, nested, with a strict boundary — no gate may masquerade as another:
    • Gate A — build correctness (per repo): native package managers + committed lockfiles resolve; tests pass; no type-identity-critical package is duplicated in the resolved graph (cargo tree -d, go list -m all, the npm lockfile/npm ls).
    • Gate B — component coherence (per producing repo): a component release is internally coherent — one spec, one commit, all lanes, one byte-parity verdict.
    • Gate C — runtime compatibility (per environment): co-deployed services that exchange a format are wire-compatible on each declared runtime edge. The anti-patterns to reject: letting "the fleet is current" stand in for "this repo is build-safe"; letting "this repo is build-safe" stand in for "this deployment is wire-compatible"; or letting "this deployment is wire-compatible" force unrelated repos to republish.
  5. Type-identity-critical dependencies use exact pins, never semver ranges (already the intent of 0008; this ratifies it as a checked rule and flags the caret-range examples still in some member docs as drift to fix).
  6. Wire compatibility is declared, not inferred from semver — especially pre-1.0. At 0.x, a minor bump may break the byte format. So a format-owning repo publishes explicit compatibility declarations (writerEpoch, readerEpochs, and the vector/fuzz proof that justifies them); the runtime plane enforces them per edge.

3. Scope of this ADR

This ADR ratifies the vocabulary, the identity unit, and the three-gate boundary. It does not itself remove the global closure, rewrite the BOM, or change any workflow — those follow as sequenced steps (below), and the closure remains the live release gate until its per-component / per-environment replacements are built and proven.

Consequences

What it enables. Additive releases stop deadlocking — a changed component cuts a new component release; unchanged siblings are referenced by their prior ids. Independent repo cadence is legitimate at the model level, not a drift to be policed. The unratified identity question (atlas#507) is answered: identity is the component release; the repo tag is a batch label; lane coordinates are evidence.

What it supersedes / refines (identity only, not mechanism). The "one logical version per component, every dependent on it, every ecosystem published together, no lag" intent of 0007 and the single-closure admission model of 0004/0008 are narrowed: atomicity binds a component release, not a fleet release. The content-addressed identity of 0023 and the event model of 0025 remain, re-anchored on the component release as the event subject. The one-commit rule (0008 §3) is correct but mis-scoped today — it is a per-(lockstep-)component rule, not a per-repo or cross-repo rule.

What stays. atlas remains the generated-evidence, topology, package-identity, lint, compatibility-schema, and desired-runtime-intent layer — it is demoted from being the global build-time admission gate, not deleted. The substrate machinery keeps running as the live gate until replaced.

Roadmap (this ADR is Step 1). Sequenced so the closure is shrunk last, never ripped out first: (1) this ADR — ratify the vocabulary; (2) component-release manifests (changed components emit; unchanged reused by reference); (3) exact-pin + no-duplicate lints in every consumer (Gate A); (4) a per-service runtime-metadata endpoint (versions + wire epochs written/read); (5) a runtime-plane (pharos) environment-compatibility view — surface-only, per declared edge (Gate C); (6) ontos wire-epoch compatibility declarations; (7) pre-deploy admission verdict; (8) an ordered breaking-roll planner (dual-read → switch-write → drain → retire); (9) only then shrink the atlas global closure to a compatibility/projection tool.

Risks / things to hold. (a) The runtime plane must check only declared runtime edges within one environment — a fleet-wide "all services on one ontos" check would silently recreate the global closure at deploy time. (b) Pre-1.0 churn means wire-epoch declarations must be cheap and constant, not occasional. (c) TypeScript duplicate detection is the weakest natively (types erase at runtime) and must be a hard lockfile check, not best-effort. (d) Concentrating runtime authority in pharos makes RBAC, audit, dry-run, break-glass, and rollback load-bearing, not optional. (e) Because docs already drift from policy, these rules should be machine-checked or generated, not prose.

Alternatives considered

A full menu (monorepo; git-SHA pinning without packages; a hermetic build system (Nix/Bazel); collapsing the three language implementations to one canonical) is evaluated in research-docs/0002-polyglot-cross-repo-dependency-coherence.md. All are ruled out by the fixed constraints: separate repos, real packages, tri-code as an optional per-repo property, independent release cadence. This ADR takes the remaining direction — native package resolution per repo, a per-component coherence gate, and a per-environment runtime-compatibility plane — and ratifies the vocabulary it requires.

The Bitspark constellation — how the systems are built and relate.

GitHub