A release names the member it cuts, and the gate reads BOTH that member's tree and the spine's

Accepted

ontoref
MEASURED 2026-08-29, BY RUNNING THE RELEASE MODE. The 0.1.10 cut stopped at step 2. `tree-clean`

Context

MEASURED 2026-08-29, BY RUNNING THE RELEASE MODE. The 0.1.10 cut stopped at step 2. `tree-clean` states its own purpose — «Refuse to proceed over uncommitted or unpushed work. A tag over a dirty tree points at a state nobody tested» — and runs `ontoref validate state`, whose `state-root` resolves to `ONTOREF_PROJECT_ROOT`, the constellation root, and whose `state-check-uncommitted` runs `git -C <root> status` there.

In the same minute: `validate state` reported «working tree clean» while `git status` inside `code/` listed SIXTEEN uncommitted files. The gate said clean about a tree it never opened.

A CUT SPANS TWO REPOSITORIES, and that is the fact the gate is missing:

code/ Cargo.toml (the crate version) · CHANGELOG.md · README.md · AND THE TAGS — `git tag` in code/ returns v0.1.8 and v0.1.9; in the parent it returns nothing parent .ontoref/reflection/releases.ncl (the declared course, `owed`) .ontoref/reflection/version.ncl (protocol_version)

So the DECISION lives on the spine and the ARTEFACT lives in a member, and the gate reads only the first. «Wrong tree» is the wrong diagnosis: it reads the deciding tree and not the tagged one, and a cut needs both.

IT WAS CORRECT BEFORE THE SPLIT. Until adr-048 moved the spine to the parent, root and tagged tree were the same directory and `git -C <root> status` covered everything. The layout changed and the check did not, so this is a gate that runs and cannot see — the failure mode `enforcement-vs-emergence` recorded in its fifth reading, where a hook «can be WIRED and still structurally unable to fire, which reads identically to compliance».

WHAT THE MODEL ALREADY KNOWS AND WHAT IT DOES NOT. `ontology/constellation.ncl` declares every member with its `name`, `path`, `category` and forges, and adr-062 makes «a distinct release lifecycle» one of the criteria for being Primary. NOTHING declares which member a release of THIS project cuts. The taxonomy answers «who may have their own repo»; nobody answers «whose tree does a tag land in».

Decision

A RELEASE NAMES THE MEMBER IT CUTS, and the gate that guards it reads BOTH that member's tree and the spine's.

THE SUBJECT IS DECLARED, NEVER INFERRED. A cut declares its member by name — the same name `constellation.ncl` already uses. Inference from «where the tags are» would make the gate correct only after the first tag exists, and inference from «the member holding Cargo.toml» would bake one ecosystem's layout into a Base rule.

ABSENCE MEANS THE ROOT, and that is what keeps this optional. A project with one repository has no member to name: its subject IS its root, the current behaviour is already right, and it declares nothing. The field is added to the release record as optional — every existing `releases.ncl` stays valid unchanged and no consumer is obliged to classify anything (formalization-vs-adoption: «schemas are optional layers, not mandatory gates»).

BOTH, NOT EITHER. The spine holds the decision — the declared course, what the release owes, the protocol version — and the member holds the artefact and the tag. A cut over a dirty spine tags a version whose course is unrecorded; a cut over a dirty member tags a state nobody tested. Reading one and calling it clean is the present defect, and it is the same defect in whichever direction it is left.

THE TWO PLANES ARE NOT FUSED. This does not move the spine into the member or the member's version into the spine. adr-031 holds that duality constitutive and adr-048 placed the spine at the parent deliberately; what changes is that ONE READER now opens both, which is adr-105's rule applied to trees instead of to check dialects.

WHAT THIS DELIBERATELY DOES NOT DECIDE: whether the OTHER members must also be clean. A constellation holds outreach, vault and addons that move on their own cycles, and requiring all of them would make every release hostage to unrelated work in progress. Where the threshold sits between «this member's state is part of the cut» and «it is not» is a habitability question, and `plane-habitability` forbids hard-coding an acceptability rule that forecloses either side. The subject is named; the rest is reported, never refused.

Constraints

  • Hard The check guarding a release reads the working tree of the member being cut, in addition to the spine's.
  • Soft The member a release cuts is named in the release record; absence means the project root, and no discovery rule infers it.

Alternatives considered

  • The gate reads every member's treerejected: Makes every release hostage to unrelated work in progress. A constellation holds members on independent cycles — outreach publishes a site, vault holds private strategy — and a draft post would block a protocol release. It also hard-codes the acceptability threshold `plane-habitability` forbids fixing: whether another member's state is part of a cut is a judgement that admits degrees, and this would answer «always».
  • The gate reads only the tagged member, not the spinerejected: Trades one blind spot for its mirror. The spine holds the declared course, what the release owes and the protocol version; cutting over a dirty spine tags a version whose own record is unwritten. It is the same defect this ADR exists to close, pointing the other way — which is exactly why the constraint's gate_source demands both fixtures.
  • Discover the subject — the member holding the tags, or a Cargo.toml, or the sole non-outreach Primaryrejected: Each rule is right for this constellation and wrong as a Base rule. Tags are absent before the first release, so the gate would be correct only after it was first needed; Cargo.toml assumes Rust; «the Primary that is not outreach» assumes this project's member list. A declared name costs one line and carries to a consumer whose layout nobody here has seen.
  • Move the spine into the cut member so root and tagged tree coincide againrejected: Reverses adr-048 to fix a check. The spine sits at the parent because it governs the whole constellation and not one member of it, and fusing them would also collapse the ontology/reflection duality adr-031 holds constitutive. The layout is not the defect; the reader is.
  • Require the member name on every release recordrejected: Obliges every single-repo consumer to declare a subject they do not have. adr-096's precedent is explicit — the set that obliges starts EMPTY and grows one author's declaration at a time — and the mandatory version is the ceremony-capture failure adr-029 names.

Anti-patterns

  • Guarding the record instead of the artefact — A release check inspects where the decision is written and reports on the state of the repository nobody is about to tag. It is not a wrong check — it is a check whose subject moved when the layout did, and it reports the reassuring answer while doing so.
  • Guessing which member is cut — The release subject is derived from where tags are, or from which member holds a build manifest. The rule is correct on the constellation it was written against and silently wrong on the next one, and being derived it cannot be contradicted by an author who knows better.

Related ADRs

ADR-048 · ADR-062 · ADR-031 · ADR-105 · ADR-082

Was this useful? Rate it
Got something to add? Tell me what you think, what you'd suggest, or whether we should keep exploring this topic.
· reads

We use cookies to help this site function, understand service usage, and support marketing efforts. Cookie Policy for more info.