Release criteria derive from the declared relation, not from the diff — and the protocol version and the artefact version stay two namespaces
Accepted
Context
MEASURED 2026-08-07/08, while repairing the newcomer install path.
· NO GOVERNED SURFACE. `qa list` over *release* returns exactly one entry, protocol-version-chain-valid, which governs the migration CHAIN and says nothing about when to cut. There is no release mode: reflection/modes/ holds typedialog-release (another project's) and publish-event. The criteria an agent had been applying — patch on a migration with surface change, minor on an FSM dimension reaching desired — lived ONLY in that agent's memory. The project's own GOVERNANCE RULE names this exact shape as drift by construction: a rule that must hold, registered where nobody can query or verify it.
· TWO NAMESPACES THAT ALREADY COLLIDED. `code/Cargo.toml` says 0.1.8; `reflection/version.ncl` says 0.1.19. The version.ncl header records the collision in the maintainer's own words: from 2026-07-17 to 2026-07-26 the protocol field held "0.1.8" — the CRATE version — written by a 27-file commit, "por el único camino que ninguna comprobación vigila, una edición a mano dentro de un commit grande". It also records that the field never satisfied check_chain before that: the repo was born red at C2.
· A THIRD, UNDECLARED NUMBERING. outreach/presentations carries rev_1_5, rev_1_6 and rev_1_7 intro decks in EN and ES — one per crate release, v0.1.5/6/7. v0.1.8 has none. The practice existed and broke silently at exactly the release nobody announced, whose CHANGELOG [Unreleased] is empty across 32 commits and whose installer pointed at a host that does not resolve.
· THE RELATION IS DECLARED AND NOBODY CONSULTS IT. connections.ncl names three downstream consumers with the protocol adopted. ADR-073 types the dependency axis with six DDD edge kinds, and as of today the axis is populated for the first time. Nothing in cutting a release reads either.
Decision
Release criteria are evaluated against the DECLARED RELATION — the bond's dependency edge and the connections graph — not against the diff.
WHAT A CHANGE OWES IS A PROPERTY OF THE EDGE IT LEAVES BY, NOT OF ITS SIZE. The same commit reaching a 'Conformist consumer (which absorbs whatever ships, and breaks silently) owes something different from one crossing an 'OpenHostService edge (a published contract, where the change IS the breakage). "How much changed" cannot see that difference; the declared edge already carries it.
The DDD kinds are borrowed as the dependency-edge enum ONLY. The frame stays native ontoref — nodes, edges, axes, planes, Spiral (qa::bond-carrier-layout-howto, forbidden patterns). This ADR does not adopt Context-Mapping as a way of thinking about the project; it reads a typed field the project already declares.
THE THREE NUMBERS STAY THREE, AND GAIN ONE PLACE THAT DECLARES THEM. Not fusing them is the decision; what changes is that a single queryable surface names every version-bearing artefact together with (a) who may move it, (b) what derives it, and (c) where it is published:
protocol_version reflection/version.ncl maintainer ONLY, never an agent derived: head of the migration chain (check_chain) published: the protocol surface consumers adopt
crate/release code/Cargo.toml release decision derived: nothing — it is chosen published: git tag, bundles, DEFAULT_VERSION in install.sh
difusión outreach/presentations difusión decision derived: the crate release it introduces published: decks EN+ES, and whatever the spine declares
THE CRITERIA ARE DATA, NOT PROSE, so another ontoref-governed project redefines them for its own artefacts in its own domain without touching the mechanism that evaluates them. A project with different edges has different obligations; the verb is shared, the thresholds are not.
Constraints
- Hard The release criteria are reachable through the CLI as a governed surface — never only in a carrier, a comment, or an agent's memory.
- Hard The protocol version and the crate version are never written from each other; reflection/version.ncl carries only a value the migration chain derives.
- Soft Cutting a release consults the declared dependency edges and reports what each is owed, rather than deriving the decision from the size of the change.
- Soft A release declares what it owes the outward surface — CHANGELOG, README, and the difusión deck — and an unmet item is reported as owed, never silently absent.
Alternatives considered
- One version number for the protocol and the artefact — rejected: Couples the release cadence to the migration cadence, or reintroduces the lag inside a single field where the two can no longer be compared. The collision of 2026-07-17 was detectable exactly because there were two.
- Derive the version from the diff — count migrations with a surface delta, watch the FSM dimensions — rejected: Cannot see that the same change owes different things on different edges. It is also the criterion that was in use, and it lived only in an agent's memory, which is how it went unaudited.
- Leave the criteria informal and decide per release — rejected: That is the status quo, and it produced an empty [Unreleased] across 32 commits, a missing deck, and a published installer pointing at a dead host. Informality did not fail through carelessness; nothing ever asked.
- Write the criteria into CLAUDE.md or an agent-memory file — rejected: Forbidden by the project's GOVERNANCE RULE and by ADR-074: a carrier invokes the governed surface and never holds the rule itself. A rule in a carrier is unqueryable by the next carrier and unverifiable by anyone.
Anti-patterns
- A release criterion that only an agent remembers — The rule for when to cut, and what a bump means, held in a memory file or a carrier instead of a governed surface. It reads as governance to whoever holds it and is invisible to everyone else — including the next session of the same agent. Measured 2026-08-07: the criteria applied to this project's releases existed nowhere else.
- Sizing the bump by what changed — Counting migrations, files or FSM dimensions to pick patch/minor/major. It measures the change and not the obligation, so it gives the same answer for a change absorbed by a 'Conformist consumer and one that breaks a published 'OpenHostService contract.
- Writing the artefact version into the protocol field — The protocol version and the crate version are different claims about different things. Collapsing them — deliberately, or by a hand-edit inside a large commit — destroys the only signal that would reveal the drift. It happened on 2026-07-17 and stood for nine days.
- The release deck as a habit rather than an owed step — A practice performed for three consecutive releases and then not performed, with nothing reporting the absence. v0.1.5, v0.1.6 and v0.1.7 each got an intro deck in two languages; v0.1.8 got none, and the gap was found only by listing the directory a week later.
Related ADRs
ADR-001 · ADR-010 · ADR-029 · ADR-035 · ADR-038 · ADR-062 · ADR-069 · ADR-072 · ADR-073 · ADR-074