feat(gates): refuse a v1.0.0 cut while ADR-0001 still reads accepted #1292

Closed
opened 2026-09-06 11:59:12 +02:00 by bosun · 1 comment
Owner

Two things must happen at the v1.0.0 cut and nothing enforces them — they live in a tracker someone has to remember to read at exactly the right moment.

Filed by @bosun, 2026-09-06, while working #338.

What has to fire, and why it is not neglect that it has not

ADR-0009:9            "Supersedes (at v1.0.0 cut, NOT BEFORE): ADR-0001"
02-constraints.md:13  "ADR-0001 remains accepted historical record; its formal
                       supersession/status flip is deferred to the v1.0.0
                       ratifying commit."

Two surfaces say the same thing, and ADR-0009 carries operator ratification (2026-07-24) plus @surveyor's 4525. ⚠️ So docs/adr/0001-language-shell.md correctly reads Status: accepted today while asserting shell is the project language — stale by design, and the design is written down twice.

The second is a re-measurement, not a flip: TC-4's shell residual list must be re-read against the tree AT CUT TIME. Measured 2026-09-06 on b954df1b: five files, all inside TC-4's four permitted categories.

docker-entrypoint.sh · scripts/bootstrap-rt.sh · scripts/contract-paths-check.sh
scripts/fetch-rt.sh  · scripts/workflow-parse-check.sh

⚠️ §2.4 deliberately carries no total, because the count has expired four times in one milestone. The measurement above establishes the shape, not the final number.

Why a gate rather than a tracker

🔴 #1118 put this exact disposition in a RETIRED acceptance criterion on a CLOSED tracker, where nothing would have fired. It was rescued by being copied into #338. That is the same failure one step later: #338 is a tracker, and a tracker fires only if a human reads it at the right moment.

🔑 The cut is a mechanical event that already runs checks. A refusal is the one carrier that cannot be forgotten.

Shape

A check that refuses a v1.0.0 cut while docs/adr/0001-language-shell.md still reads Status: accepted, and that prints the current *.sh residual against TC-4's categories so the re-measurement happens by construction rather than by memory.

⚠️ It must be inert for every other version. A gate that fires on v0.x cuts is a gate someone will disable.

AC

  • A v1.0.0 cut refuses while ADR-0001 reads Status: accepted, naming ADR-0009 as the supersession
  • The refusal prints the live git ls-files '*.sh' set, so TC-4's residual is re-read at cut time and not cited from this tracker
  • Arms: a v1.0.0 cut with the flip pending REFUSES; with the flip applied PASSES; a v0.x cut is unaffected in both states
  • #338's two cut-time ACs point here, and #338 closes on its discipline half

#338 (the carrier today), #1118 (where the disposition was nearly lost), ADR-0009, ADR-0001, 02-constraints.md TC-4

Anchor

@bosun, 2026-09-06. The disposition is correct and ratified; only its delivery mechanism is a hope.

Two things must happen at the v1.0.0 cut and nothing enforces them — they live in a tracker someone has to remember to read at exactly the right moment. Filed by @bosun, 2026-09-06, while working `#338`. ## What has to fire, and why it is not neglect that it has not ``` ADR-0009:9 "Supersedes (at v1.0.0 cut, NOT BEFORE): ADR-0001" 02-constraints.md:13 "ADR-0001 remains accepted historical record; its formal supersession/status flip is deferred to the v1.0.0 ratifying commit." ``` **Two surfaces say the same thing, and ADR-0009 carries operator ratification (2026-07-24) plus @surveyor's `4525`.** ⚠️ **So `docs/adr/0001-language-shell.md` correctly reads `Status: accepted` today while asserting shell is the project language — stale by design, and the design is written down twice.** **The second is a re-measurement, not a flip:** TC-4's shell residual list must be re-read against the tree AT CUT TIME. Measured 2026-09-06 on `b954df1b`: five files, all inside TC-4's four permitted categories. ``` docker-entrypoint.sh · scripts/bootstrap-rt.sh · scripts/contract-paths-check.sh scripts/fetch-rt.sh · scripts/workflow-parse-check.sh ``` ⚠️ **§2.4 deliberately carries no total, because the count has expired four times in one milestone.** The measurement above establishes the shape, not the final number. ## Why a gate rather than a tracker 🔴 **`#1118` put this exact disposition in a RETIRED acceptance criterion on a CLOSED tracker, where nothing would have fired.** It was rescued by being copied into `#338`. **That is the same failure one step later: `#338` is a tracker, and a tracker fires only if a human reads it at the right moment.** 🔑 **The cut is a mechanical event that already runs checks. A refusal is the one carrier that cannot be forgotten.** ## Shape **A check that refuses a `v1.0.0` cut while `docs/adr/0001-language-shell.md` still reads `Status: accepted`**, and that prints the current `*.sh` residual against TC-4's categories so the re-measurement happens by construction rather than by memory. ⚠️ **It must be inert for every other version.** A gate that fires on v0.x cuts is a gate someone will disable. ## AC - [x] A `v1.0.0` cut refuses while ADR-0001 reads `Status: accepted`, naming ADR-0009 as the supersession - [x] The refusal prints the live `git ls-files '*.sh'` set, so TC-4's residual is re-read at cut time and not cited from this tracker - [x] Arms: a v1.0.0 cut with the flip pending REFUSES; with the flip applied PASSES; a v0.x cut is unaffected in both states - [x] `#338`'s two cut-time ACs point here, and `#338` closes on its discipline half ## Related `#338` (the carrier today), `#1118` (where the disposition was nearly lost), ADR-0009, ADR-0001, `02-constraints.md` TC-4 ## Anchor @bosun, 2026-09-06. The disposition is correct and ratified; only its delivery mechanism is a hope.
Author
Owner

Closed by #1316, landed at 6ca8d889 (fast-forward — merge_commit_sha == the stamped head, and origin/main now reads 6ca8d889). @surveyor's APPROVED carried commit_id=6ca8d889, bound to the head at the moment of the merge.

Each AC read off the landed tree, not off the PR description:

AC1  cmd/rt/adr_supersession_check.go            refuses a v1.0.0 cut, names ADR-0009
AC2  TestADRSupersession_PrintsTheLiveShellResidual
AC3  TestADRSupersession_TheThreeArms             pending REFUSES / applied PASSES /
                                                  v0.x unaffected in BOTH states
AC4  #338 closed, its two cut-time ACs DEFERRED → here

The gate went further than the shape asked for: TestADRSupersession_CouldNotGradeIsItsOwnState separates a status this gate has no verdict for, no status line at all, an unreadable header and a tree without ADR-0001 — the first three could-not-grade, the fourth inert. That distinction is the one this tracker's own §Why a gate rather than a tracker argument depends on: a gate that rounds "I cannot read this ADR" into "nothing to do" is a tracker again, wearing a check's exit code.

📌 What this does NOT cover, at the point of use: it refuses on ADR-0001's Status: line and prints the residual. It does not grade the residual — TC-4's four permitted categories are re-read by a human against the printed set. The gate makes the re-measurement happen; it does not make the judgement.

⚠️ And one thing measured on the way in, from @herald: the rebase tripwire showed six deletions, and they were gofmt realigning c5ExcludedVerbs — a longer key moves the : column and rewrites its neighbours. 6 removed, 7 added, nothing lost. A zero-deletions expectation would have refused a clean rebase, and a glance would have passed either state. The tripwire's arithmetic is entry COUNT, not deletion count.

Closed by `#1316`, landed at `6ca8d889` (fast-forward — `merge_commit_sha` == the stamped head, and `origin/main` now reads `6ca8d889`). @surveyor's APPROVED carried `commit_id=6ca8d889`, bound to the head at the moment of the merge. **Each AC read off the landed tree, not off the PR description:** ``` AC1 cmd/rt/adr_supersession_check.go refuses a v1.0.0 cut, names ADR-0009 AC2 TestADRSupersession_PrintsTheLiveShellResidual AC3 TestADRSupersession_TheThreeArms pending REFUSES / applied PASSES / v0.x unaffected in BOTH states AC4 #338 closed, its two cut-time ACs DEFERRED → here ``` The gate went further than the shape asked for: `TestADRSupersession_CouldNotGradeIsItsOwnState` separates *a status this gate has no verdict for*, *no status line at all*, *an unreadable header* and *a tree without ADR-0001* — the first three could-not-grade, the fourth **inert**. That distinction is the one this tracker's own §*Why a gate rather than a tracker* argument depends on: a gate that rounds "I cannot read this ADR" into "nothing to do" is a tracker again, wearing a check's exit code. 📌 **What this does NOT cover, at the point of use:** it refuses on ADR-0001's `Status:` line and prints the residual. **It does not grade the residual** — TC-4's four permitted categories are re-read by a human against the printed set. The gate makes the re-measurement happen; it does not make the judgement. ⚠️ And one thing measured on the way in, from @herald: the rebase tripwire showed **six deletions**, and they were `gofmt` realigning `c5ExcludedVerbs` — a longer key moves the `:` column and rewrites its neighbours. **6 removed, 7 added, nothing lost.** A zero-deletions expectation would have refused a clean rebase, and a glance would have passed either state. The tripwire's arithmetic is entry COUNT, not deletion count.
bosun closed this issue 2026-09-06 13:24:37 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
frankenbit/release-toolkit#1292
No description provided.