Nine ADRs (0254, 0256-0263) were merged into main and left stamped Proposed. Two carried an explicit ratify-on-merge predicate their own merge had already discharged, and ADR-0256 governs deduction_serving_enabled, which was ratified True on 2026-07-24 and is serving live traffic. So the governance record asserted "not yet decided" about a decision already in force. That is the asymmetry the assessment arc (#113) found one file over in workbench/api.py: the honest path degrades, the stale record lies. An unwritten ADR is a visible gap; a Proposed one that is actually in force is a false statement. Each stamp records the ACTUAL ratifying act -- "Accepted, ratified by Joshua Shay via <merge> (<sha>, <date>)" -- derived from the commit that added the file and verified an ancestor of main, not assumed. Merge authority is Shay's alone (AGENTS.md: no merge automation), so the merge IS the ratifying act. No ADR content changed. No flag changed. ADR-0262's stamp says so explicitly: accepting it does NOT enable curriculum_serving_enabled, which stays False pending ratified volume -- eleven bands re-measured today, still 24x-73x short. tests/test_adr_status_governance.py pins two independent invariants in the smoke (pre-push) suite: 1. A default-ON flag is not governed by a Proposed ADR. The flag -> ADR mapping is DERIVED by walking core/config.py for `<name>: bool = True` and reading ADR refs from the preceding comment block -- not a hand-written table, which would be the same second-copy-of-a-closed-set defect ADR-0256's arc fixed. 2. A ratify-on-merge predicate cannot coexist with Proposed. Self-discharging: the file being on main IS the merge having happened. Plus a vacuity guard, because a derivation that parses zero flags would make every other assertion pass on an empty set. Registered two orphans found in passing: test_adr_index.py (5) and test_ratification_ceremony.py (14) landed in #113 in NO curated suite, so 19 tests -- including the one mechanism that can move curriculum volume -- ran only under `full`, which gates nothing. Fifth instance of this shape. Deliberately NOT fixed, recorded in the research doc: the 312-file corpus has 27 unparseable status lines and draft/ratified/active variants. A closed-vocab assertion would fail on ~35 pre-existing files and get muted, and a muted gate reads as coverage. [Verification]: smoke 555 passed in 137.73s (236 baseline + 314 + 5, +1.2s); governance pin 314 passed in 1.18s standalone and MUTATION-CHECKED -- reverting ADR-0256 to Proposed fails both invariants independently (2 failed/312 passed); orphans 19 passed; ruff clean. Canonical Python 3.12.13, uv sync --locked.
6.1 KiB
ADR Status Governance — Nine Stamps and an Enforcement Pin
Date: 2026-07-25 · Follow-on to the assessment-verification arc (PR #113). Evidence + mechanism only. No flag was touched; no ADR's content was changed.
1. What was wrong
Nine ADRs were merged into main and left stamped Proposed:
| ADR | ratifying merge | why the stamp was contradictory |
|---|---|---|
| 0254 | PR #103 → da3447e9 (2026-07-23) |
its own status line said "ratify on PR #103 merge" |
| 0256 | 1a6ccaf9 (2026-07-23) |
its own status line said "ratify-on-merge", and it governs deduction_serving_enabled, ratified True on 2026-07-24 |
| 0257 | PR #107 → 9405cf19 |
Band v2-EN, live in the serving cascade |
| 0258 | PR #108 → dd2245a7 |
Band v3-MEM, live |
| 0259 | PR #109 → 5224b5e0 |
Band v4-CM, live |
| 0260 | PR #111 → 2a82c8a3 |
Band v5-VP, live |
| 0261 | a8488e9c |
Band v6-EX, live |
| 0262 | 0ae54ebb |
curriculum-grounded serving (flag still OFF — see §3) |
| 0263 | 0ae54ebb |
ratified-ledger bridge, consumed by three capabilities |
Every merge above was verified an ancestor of main before stamping
(git merge-base --is-ancestor), and each ADR's ratifying merge was derived
from the commit that added the file, not assumed.
ADR-0256 is the sharp case. It governs a flag that was ratified ON and serving
live traffic, so the governance record asserted "this decision is not yet made"
about a decision already in force. That is the same asymmetry the arc that just
closed found in workbench/api.py: the honest path degrades, the stale
record lies. An unregistered grounding label reported
epistemic_state_needed; a hand-copied whitelist reported none. Here, an
unwritten ADR would have been a visible gap; a Proposed one that is actually
in force is a false statement.
2. What the stamps say
Status lines now record the actual ratifying act rather than inventing a
ceremony: Accepted — ratified by Joshua Shay via <the merge> (<sha>, <date>).
Merge authority in this repo is Shay's alone (AGENTS.md: no merge automation;
green PRs sit until explicit authorization), so the merge is the ratifying
act and naming it is traceable rather than decorative.
3. Accepting an ADR is not flipping its flag
ADR-0262's stamp says so explicitly. curriculum_serving_enabled stays
False; its blocker is ratified curriculum volume (§5.1), and eleven live
bands remain 24×–73× short of the entailed-bucket floor — re-measured
2026-07-25 against the live loader, unchanged from the Tier-S S6 figures
(physics causal 7, modal 9, systems_software sequence 3,
mathematics_logic modal 4). An accepted ADR records a decision about
mechanism; a flipped flag records a decision about earned evidence. Two
different rulings, and this document conflates neither.
4. The pin
tests/test_adr_status_governance.py, registered in the smoke suite (the
pre-push gate). Two independent invariants, both mutation-checked — reverting
ADR-0256 to Proposed fails each one separately:
- A default-ON flag is not governed by a
ProposedADR. The flag → ADR mapping is derived by walkingcore/config.pyfor<name>: bool = Trueand reading the ADR references out of the preceding comment block. Deliberately not a hand-written table: a second copy of a closed set that falls behind the original in silence is the exact defect ADR-0256's arc fixed one file over. - A
ratify-on-mergepredicate cannot coexist withProposed. Self discharging and needs no bookkeeping: the file being present onmainis the merge having happened, so the two states are contradictory by construction.
Plus test_config_flag_parse_is_not_vacuous, because a derivation that silently
parses zero flags would make every other assertion pass on an empty set —
silence reading as success.
Measured: 314 passed in 1.18s standalone; 2 failed / 312 passed under mutation.
Current binding scope is exactly one flag/ADR pair
(deduction_serving_enabled → 0256), because only one of the 32 runtime bool
flags is both default-True and ADR-citing. Narrow today by construction, and it
widens automatically the moment another earned path is ratified ON — which is
the point.
5. Two orphaned test files, registered in passing
tests/test_adr_index.py (5 tests) and tests/test_ratification_ceremony.py
(14) landed in PR #113 in no curated suite. full is the directory
tests/, so they ran there and nowhere that gates. Nineteen tests outside every
gate — including the ceremony, the one mechanism that can move the
curriculum-volume constraint, and an index test whose whole purpose is catching
staleness.
That is the fifth instance of this shape: a stale suite tuple (S5), a stale
lane roster (#113), a masked lane pin (S2), an unregistered grounding label
(#113), and now unregistered test files. Registered test_adr_index.py into
smoke alongside the new pin, and test_ratification_ceremony.py into
teaching beside test_proposal_queue.py.
6. Deliberately NOT fixed — recorded so it is not re-found
The ADR corpus is 312 files and its status vocabulary has real drift:
| status token | files |
|---|---|
accepted |
201 |
proposed |
67 |
| (status line unparseable) | 27 |
draft |
4 |
ratified |
3 |
accepted. / active / phase |
2 each |
implemented / empirical / superseded |
1 each |
A closed-vocabulary assertion over all 312 would fail on ~35 pre-existing files
and get muted or xfailed, which is worse than no check — a muted gate reads as
coverage. So the pin's scope is exactly the ADRs whose status is load-bearing
now, and it fails loudly on any of those it cannot parse rather than
skipping it.
The open item: normalize the 27 unparseable status lines and collapse
ratified/draft/active/implemented into the intended lifecycle, then
widen the pin to a closed vocabulary. Sizeable, mechanical, and a good
delegation candidate — it is a docs sweep with a test as its acceptance
criterion, not a design question.
Relates to project-generalization-arc, docs/adr/INDEX-by-domain.md,
docs/handoff/BRIEF-CLOSE-assessment-verification-2026-07-25.md.