Merge pull request 'chore(governance): stamp nine merged ADRs Accepted + pin the invariant in smoke' (#114) from chore/adr-status-governance into main

Reviewed-on: #114
This commit is contained in:
Joshua Matthew-Catudio Shay 2026-07-25 21:55:32 +00:00
commit 0a26787f72
12 changed files with 326 additions and 9 deletions

View file

@ -67,6 +67,16 @@ TEST_SUITES: dict[str, tuple[str, ...]] = {
# substantive override of the served surface. Cheap, and the axis is
# invisible until it breaks silently.
"tests/test_prior_surface_deduction_binding.py",
# ADR governance. An ADR that governs a default-ON flag records a
# decision already in force, so leaving it "Proposed" makes the
# governance record assert something false about the running system —
# which is what happened to ADR-0256 while deduction serving was live.
# Filesystem + regex only, ~1.2s, and it is the record for a serving
# path, so it belongs on the pre-push gate rather than under `full`.
# test_adr_index.py rides along: it landed in #113 registered in NO
# curated suite, which is the same silent-red shape it exists to catch.
"tests/test_adr_status_governance.py",
"tests/test_adr_index.py",
),
"runtime": (
"tests/test_chat_runtime.py",
@ -97,6 +107,11 @@ TEST_SUITES: dict[str, tuple[str, ...]] = {
"tests/test_math_composition_ratification.py",
"tests/test_teaching_coverage_cli.py",
"tests/test_proposal_queue.py",
# The ratification ceremony is the only path that turns a reviewed
# proposal into a routable ratified chain — i.e. the one mechanism that
# can move the curriculum-volume constraint. It landed in #113 in NO
# curated suite, so its 14 tests ran only under `full`.
"tests/test_ratification_ceremony.py",
),
"packs": (
"tests/test_pack_draft_serve_boundary.py",

View file

@ -1,6 +1,6 @@
# ADR-0254: Grounded-Open Hedge Arm for the Shadow Coherence Gate
**Status:** Proposed (predicate pre-authorized by Joshua Shay, 2026-07-22 weekly-audit ruling T13 decision 2; ratify on PR #103 merge)
**Status:** Accepted — ratified by Joshua Shay via the PR #103 merge (`da3447e9`, 2026-07-23), discharging this ADR's own ratify-on-merge predicate (pre-authorized by the 2026-07-22 weekly-audit ruling T13 decision 2)
**Date:** 2026-07-23
**Deciders:** Joshua Shay (ruling authority) + Claude (implementation)
**Companion docs:** [`ADR-0036-safety-refusal-policy.md`](ADR-0036-safety-refusal-policy.md), [`ADR-0037-per-predicate-ethics-refusal.md`](ADR-0037-per-predicate-ethics-refusal.md), [`ADR-0038-hedge-injection.md`](ADR-0038-hedge-injection.md), [`ADR-0252-problem-solving-paradigm-consolidation.md`](ADR-0252-problem-solving-paradigm-consolidation.md)

View file

@ -1,6 +1,6 @@
# ADR-0256: Deduction Serving Governed by an Earned Reliability License
**Status:** Proposed (deduction-serve arc, Phase 3 — ratify-on-merge)
**Status:** Accepted — ratified by Joshua Shay via the deduction-serve Phase 5 merge (`1a6ccaf9`, 2026-07-23), discharging this ADR's own ratify-on-merge predicate. The `deduction_serving_enabled` default was flipped False → True by a separate, explicit ratification (`5dd61803`, 2026-07-24)
**Date:** 2026-07-23
**Deciders:** Joshua Shay (ruling authority) + Claude (implementation)
**Companion docs:** [`docs/research/deduction-serve-arc-phase0-baseline-2026-07-23.md`](../research/deduction-serve-arc-phase0-baseline-2026-07-23.md), [`docs/research/deduction-serve-arc-phase1-turn-spine-2026-07-23.md`](../research/deduction-serve-arc-phase1-turn-spine-2026-07-23.md), [`docs/research/deduction-serve-arc-phase2-eval-lane-2026-07-23.md`](../research/deduction-serve-arc-phase2-eval-lane-2026-07-23.md), [`docs/adr/ADR-0175-...`](.) (calibrated learning), [`docs/adr/ADR-0199-...`](.) (cross-domain arena), [`docs/adr/ADR-0206-...`](.) (response-governance bridge)

View file

@ -1,6 +1,6 @@
# ADR-0257 — English-clause argument band (Band v2-EN): opaque-atom propositional serving
- **Status:** Proposed
- **Status:** Accepted — ratified by Joshua Shay via the PR #107 merge (`9405cf19`, 2026-07-23)
- **Date:** 2026-07-23
- **Relates to:** ADR-0256 (deduction-serve earned license), ADR-0201 (ROBDD
keystone), ADR-0218 (proof-carrying substrate), ADR-0175/0199 (calibrated

View file

@ -1,6 +1,6 @@
# ADR-0258 — Member-chain band (Band v3-MEM): singular membership + universal premises
- **Status:** Proposed
- **Status:** Accepted — ratified by Joshua Shay via the PR #108 merge (`dd2245a7`, 2026-07-24)
- **Date:** 2026-07-23
- **Relates to:** ADR-0257 (Band v2-EN — this is its scope-out #1), ADR-0256
(earned license), ADR-0201 (ROBDD keystone), ADR-0175/0199 (calibrated

View file

@ -1,6 +1,6 @@
# ADR-0259 — Conditional-membership fusion band (Band v4-CM)
- **Status:** Proposed
- **Status:** Accepted — ratified by Joshua Shay via the PR #109 merge (`5224b5e0`, 2026-07-24)
- **Date:** 2026-07-23
- **Relates to:** ADR-0258 (Band v3-MEM — this is its scope-out #1), ADR-0257
(Band v2-EN — the connective grammar this band reuses), ADR-0256 (earned

View file

@ -1,6 +1,6 @@
# ADR-0260 — Band v5-VP: verb-predicate arguments, decided
- **Status:** Proposed
- **Status:** Accepted — ratified by Joshua Shay via the PR #111 merge (`2a82c8a3`, 2026-07-24)
- **Date:** 2026-07-24
- **Arc:** deduction-serve (ADR-0256 → 0257 → 0258 → 0259 → this)
- **Governs:** `generate/proof_chain/verb.py`, the `en_verb_*` shape-bands,

View file

@ -1,6 +1,6 @@
# ADR-0261 — Band v6-EX: existential arguments, decided
- **Status:** Proposed
- **Status:** Accepted — ratified by Joshua Shay via the `feat/existential-band` merge (`a8488e9c`, 2026-07-24)
- **Date:** 2026-07-24
- **Arc:** deduction-serve (ADR-0256 → 0257 → 0258 → 0259 → 0260 → this)
- **Governs:** `generate/proof_chain/exist.py`, the `en_exist_*` shape-bands,

View file

@ -1,6 +1,6 @@
# ADR-0262 — Curriculum-grounded serving: exams answered from what was taught
- **Status:** Proposed
- **Status:** Accepted — ratified by Joshua Shay via the `feat/curriculum-serve-physics` merge (`0ae54ebb`, 2026-07-24). Accepting this ADR does NOT enable `curriculum_serving_enabled`, which stays False pending ratified curriculum volume (§5.1)
- **Date:** 2026-07-24
- **Arc:** generalization Phase 2 (docs/plans/generalization-arc-2026-07-24.md §4)
- **Governs:** `teaching/curriculum_premises.py`, `chat/curriculum_surface.py`,

View file

@ -1,6 +1,6 @@
# ADR-0263 — The ratified-ledger bridge
- **Status:** Proposed
- **Status:** Accepted — ratified by Joshua Shay via the `feat/curriculum-serve-physics` merge (`0ae54ebb`, 2026-07-24). Amended 2026-07-25 with rule 5 (ledger-absence policy declared, not passed)
- **Date:** 2026-07-24
- **Arc:** generalization Phase 3.3 (docs/plans/generalization-arc-2026-07-24.md §2)
- **Governs:** `core/ratified_ledger.py` and the three adapters over it —

View file

@ -0,0 +1,126 @@
# 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:
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 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.
2. **A `ratify-on-merge` predicate cannot coexist with `Proposed`.** Self
discharging and needs no bookkeeping: the file being present on `main` *is*
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 `xfail`ed, 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`.

View file

@ -0,0 +1,176 @@
"""An ADR that governs live behaviour must not still be stamped ``Proposed``.
Nine ADRs (0254, 0256-0263) sat at ``Proposed`` while merged into ``main``,
two of them carrying an explicit ``ratify-on-merge`` predicate that the merge
had already discharged, and one -- ADR-0256 -- governing
``deduction_serving_enabled``, which was ratified ``True`` and serving live
traffic. Nothing failed. The governance record and the running system
disagreed for a day and the only reason it surfaced was a human reading the
files.
This is the "pinned list goes stale" family that has now been caught four
separate ways in this repo (a stale suite tuple, a stale lane roster, an
unregistered grounding label, a masked lane pin). Every previous instance was
fixed by making the divergence loud. This module does that for ADR status.
Deliberately NOT a repo-wide status-vocabulary check. The 312-file ADR corpus
carries real drift -- 27 files whose status line this module's regex cannot
parse, plus ``draft`` / ``ratified`` / ``active`` / ``accepted.`` variants.
Normalizing that is a separate, larger job (recorded in
``docs/research/adr-status-governance-2026-07-25.md``). Asserting a closed
vocabulary here would fail on ~35 pre-existing files and get muted, which is
worse than no check. So the scope is exactly the ADRs whose status is
*load-bearing right now*, and the parser fails loudly on any of those it cannot
read rather than skipping it.
"""
from __future__ import annotations
import re
from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parents[1]
ADR_DIR = REPO_ROOT / "docs" / "adr"
CONFIG_PATH = REPO_ROOT / "core" / "config.py"
#: ``**Status:** X`` / ``- **Status**: X`` / ``**Status**: **X**`` all appear in
#: the corpus. Tolerant on the label, strict on capturing the remainder.
_STATUS_RE = re.compile(
r"^[ \t]*(?:[-*][ \t]*)?\*\*Status\*{0,2}[ \t]*:?[ \t]*\*{0,2}[ \t]*:?[ \t]*(?P<value>.+?)[ \t]*$",
re.MULTILINE,
)
_BOOL_FIELD_RE = re.compile(r"^\s*(?P<name>\w+)\s*:\s*bool\s*=\s*(?P<default>True|False)\b")
_ADR_REF_RE = re.compile(r"ADR-(\d{4})")
_RATIFY_ON_MERGE_RE = re.compile(r"ratify[- ]on[- ]merge|ratify on PR", re.IGNORECASE)
def _adr_paths() -> dict[str, Path]:
"""Map a four-digit ADR number to its file.
Numbers are not unique in principle (``ADR-0131.3`` exists), so the key is
the leading four digits and the first match wins deterministically by name.
"""
out: dict[str, Path] = {}
for path in sorted(ADR_DIR.glob("ADR-*.md")):
match = re.match(r"ADR-(\d{4})", path.name)
if match:
out.setdefault(match.group(1), path)
return out
def _status_of(path: Path) -> str | None:
"""Return the first status value in ``path``, or ``None`` if unparseable."""
match = _STATUS_RE.search(path.read_text(encoding="utf-8"))
if match is None:
return None
return match.group("value").strip().strip("*").strip()
def _is_proposed(status: str) -> bool:
return status.lower().lstrip("*").startswith("proposed")
def _flag_adr_citations() -> dict[str, tuple[str, frozenset[str]]]:
"""Derive ``flag -> (default, cited ADR numbers)`` from ``core/config.py``.
Derived, never restated. A hand-copied mapping here would be the very
defect ADR-0256's own arc fixed in ``workbench/api.py``: a second copy of a
closed set that falls behind the original in silence. A flag's comment
block citing ``ADR-NNNN`` is read as a claim that the ADR governs the flag.
"""
lines = CONFIG_PATH.read_text(encoding="utf-8").splitlines()
out: dict[str, tuple[str, frozenset[str]]] = {}
for index, line in enumerate(lines):
field = _BOOL_FIELD_RE.match(line)
if field is None:
continue
comment: list[str] = []
cursor = index - 1
while cursor >= 0:
stripped = lines[cursor].strip()
if stripped.startswith("#"):
comment.append(stripped)
elif not stripped and not comment:
pass # blank lines before the block start are skipped
else:
break
cursor -= 1
cited = frozenset(_ADR_REF_RE.findall("\n".join(comment)))
out[field.group("name")] = (field.group("default"), cited)
return out
def test_config_flag_parse_is_not_vacuous() -> None:
"""Guard the derivation itself.
If ``core/config.py`` is reformatted such that the field regex stops
matching, every downstream assertion would pass on an empty set and this
module would go quietly blind -- silence reading as success, which is the
failure mode it exists to prevent.
"""
flags = _flag_adr_citations()
assert len(flags) >= 25, f"expected the runtime config's bool flags; parsed only {len(flags)}"
assert any(
default == "True" for default, _ in flags.values()
), "no default-True flag parsed — the field regex has drifted from core/config.py"
assert any(
cited for _, cited in flags.values()
), "no flag cites an ADR — the comment-block walker has drifted"
@pytest.mark.parametrize(
("flag", "adr"),
sorted(
(flag, adr)
for flag, (default, cited) in _flag_adr_citations().items()
if default == "True"
for adr in cited
),
)
def test_default_on_flag_is_not_governed_by_a_proposed_adr(flag: str, adr: str) -> None:
"""A flag that is ON by default is serving live traffic.
The ADR that governs it therefore records a decision already in force.
Leaving it ``Proposed`` makes the governance record assert something false
about the running system -- exactly the asymmetry ADR-0256's arc found in
``workbench/api.py``, where the honest path degraded and the stale copy
lied.
If this fails because a flag's comment cites an ADR only as background
rather than as its governing decision, move the citation into prose outside
the flag's comment block. Do not weaken the assertion.
"""
paths = _adr_paths()
assert adr in paths, f"{flag} cites ADR-{adr}, which does not exist in docs/adr/"
status = _status_of(paths[adr])
assert status is not None, (
f"ADR-{adr} governs default-on flag {flag!r} but its status line is unparseable; "
"a load-bearing ADR must declare a readable status"
)
assert not _is_proposed(status), (
f"{flag} defaults to True (live serving) but ADR-{adr} is still {status!r}. "
"Either stamp the ADR with the decision that put the flag on, or turn the flag off."
)
@pytest.mark.parametrize("adr_path", sorted(ADR_DIR.glob("ADR-*.md")), ids=lambda p: p.name)
def test_ratify_on_merge_predicate_is_discharged(adr_path: Path) -> None:
"""An ADR whose status says "ratify on merge" cannot be merged AND Proposed.
ADR-0254 (``ratify on PR #103 merge``) and ADR-0256 (``ratify-on-merge``)
both sat ``Proposed`` after the merges that were supposed to ratify them.
The predicate is self-discharging: the file being present on ``main`` IS
the merge having happened, so the two states are contradictory by
construction and need no external bookkeeping to detect.
"""
status = _status_of(adr_path)
if status is None or not _is_proposed(status):
return
assert not _RATIFY_ON_MERGE_RE.search(status), (
f"{adr_path.name} is on main and still {status!r}. Its own status line makes the "
"merge the ratifying act, so the merge already discharged it — stamp it Accepted "
"and record which merge did so."
)