core/tests/test_adr_status_governance.py
Claude 6c67e88df1
feat(config,specs,tests): PR-5 — the flag register, profiles as units, and the daemon's missing fourth flag (R-3 + R-4)
Rank 3 on the docket. RuntimeConfig carries 32 boolean fields and nothing in
the repository stated that set, distinguished a capability flag from a posture
flag from a deployment knob, or recorded what evidence would flip any of them.
Two independent counts of the same set differed by eleven — the assessment said
"seventeen capability flags," N-7 measured 28 default-off plus 4 default-on —
and neither was wrong about what it looked at. They counted different things
because nothing declared what the set was.

R-3 (ruled A — incomplete flag set). accrue_realized_knowledge joins
CONTINUOUS_LIFE_CONFIG_FLAGS. Step D consolidates the realized facts Step B
writes, and the daemon forced the consumer without the producer: a continuous
life consolidating an empty set, which the mastery framework calls "garbage at
high speed." It shipped that way for six weeks.

The decisive evidence was in the code, not the documents. BOTH flags' comment
blocks in core/config.py already described the corrected profile —
accrue_realized_knowledge says "the production L10 process enables it alongside
persist_session_state", consolidate_determinations says "...alongside
accrue_realized_knowledge + persist_session_state". Three records (two
docstrings and the 07-25 verification doc) agreed with each other and disagreed
with four lines of code. Dormancy was a coherent ruling and would have cost
correcting three records that were RIGHT about the design; incompleteness cost
one line.

So the planned "N-6 docstring correction" deliverable DISSOLVED rather than
shipping: adding the flag made both docstrings true. Recorded explicitly,
because a PR that quietly ships less than it promised is the same divergence
class this arc exists to close.

R-4 (ruled A) — docs/specs/flag_register.md. All 32 booleans with class,
governing ADR or an honest "none", recorded rationale, and what evidence would
flip it. The classification is the load-bearing half and is stated as a
taxonomy rather than a label:
  CAPABILITY — what CORE can do; engineering may flip it on named evidence
  POSTURE    — what CORE serves or refuses AS TRUE; the wrong=0 boundary,
               ruling-only
  DEPLOYMENT — cost, lifecycle, process shape; per-deployment
estimation_enabled and composed_surface are both "= False" and are not the same
kind of thing. Nothing said so before.

Profiles are declared as the UNIT of decision (R-4's mechanism): one-shot/eval
and continuous-life. The absence of a SERVING profile is stated deliberately —
the four POSTURE serving flags each ride their own license, and grouping them
before the ledger is sealed would license by association, the outcome R-8
exists to prevent.

tests/test_flag_register.py, on the gate. BIDIRECTIONAL, because either
direction alone would have passed through both failures it exists to prevent:
a flag added to config.py and not registered fails; a register row outliving
its flag fails. The counts the register's prose states (32 / 4 ON) are pinned,
since a stale count is how this register came to be needed. Four sabotages
observed red — new unregistered flag, deleted row, orphaned row, and a flag
silently flipped ON, the last caught by two independent pins.

THREE FINDINGS THE ITEM DID NOT ANTICIPATE, all from measuring rather than
reading.

1. Accumulated permissiveness, not just hesitancy. Of the four default-ON
   flags, ONE has a governing ADR (deduction_serving_enabled / ADR-0256) and
   TWO have no recorded reason of any kind — allow_cross_language_recall and
   use_salience carry no comment block, no ADR, no criterion. In an
   architecture built on earned licenses, two permanently-on capability flags
   with no recorded decision is G-8's question inverted. Registered and
   deliberately NOT fixed: writing a rationale after the fact would invent a
   decision nobody made, which is worse than recording the absence.

2. A hollow gate inside the governance pin itself.
   test_default_on_flag_is_not_governed_by_a_proposed_adr is parameterized over
   (default-ON flag x cited ADR), so the three uncited ON flags contribute ZERO
   cases — it covered exactly one flag. Its non-vacuity guard asserted only
   that SOME flag cites an ADR, which default-off flags satisfy in abundance.
   One reformatted comment away from zero coverage with everything green: the
   failure state indistinguishable from the success state, inside the module
   written to prevent exactly that. Guard tightened to require a non-empty
   parametrization, and observed red by stripping ADR-0256's citation.

3. The daemon's own tests ran on no gate. R-3 changes what the always-on
   process does every beat, and tests/test_l10_always_on_daemon.py was in no
   curated suite — so this behaviour change would have shipped guarded solely
   by tests no gate invokes, which is the hollow-gate pattern landing on the
   exact file covering the change. Promoted to smoke WITH the change it guards.
   Measured +9.4s, +4.3% of smoke — paid deliberately, not estimated. Removed
   from full_only_baseline.txt in the same edit (both-directions ratchet; the
   baseline shrank 747 -> 746).

Red before green throughout: the assertion cfg.accrue_realized_knowledge is
True was added to the daemon test and OBSERVED FAILING before the flag existed.

H-8(c) closed by AMENDING the correction note rather than deleting it, and the
amendment is the more useful artifact. PR-1's note said the 07-25 document's
claim was false; R-3 then made that claim TRUE, so the correction note itself
went stale — in the direction nobody guards, a correction made wrong by its own
subject being fixed. Both notes now carry the commit window they apply to. A
dated correction survives its subject changing; an undated "this is false" does
not.

Also delivered, from the 2026-07-28 external-assessment triage: §5, the
declared-table index. Eight single-source-of-truth tables and the pin that makes
each true. A reader's aid, explicitly NOT a central contracts.toml — that would
add a fifth copy needing agreement with four generators and put four different
authorities in one merge surface.

Handed forward to PR-11: the 5000-beat soak recorded in
evals/l10_always_on/contract.md (2026-07-19) predates this change and therefore
describes a configuration that NO LONGER SHIPS. PR-11's re-run is the first soak
of the corrected profile, and Step B feeding Step D is exactly what a long
horizon stresses.

Closes G-6, G-8, H-6, H-8(c), H-8(d) — and H-8 in full. Wave 4's F-6 half-gate
is lifted; the Track C half is untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wcw2pnMBwyvmNyQg4uPEt4
2026-07-28 06:47:21 +00:00

205 lines
9.1 KiB
Python

"""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"
# PR-5, 2026-07-28 — the guard above was one citation away from being hollow.
# ``test_default_on_flag_is_not_governed_by_a_proposed_adr`` is parameterized over
# (default-ON flag x cited ADR), so a default-ON flag citing NO ADR contributes
# ZERO cases and is silently uncovered. Measured at the time this was added:
# of four default-ON flags, three cite nothing -- ``allow_cross_language_recall``,
# ``use_salience``, ``discourse_planner`` -- so the parametrization generated
# exactly ONE case, for ``deduction_serving_enabled`` (ADR-0256). The assertion
# above stayed green throughout, because default-*off* flags cite ADRs in
# abundance. If ADR-0256's citation were ever reformatted out of that comment
# block, the governance test would generate zero cases and every part of this
# module would still report success. That is the failure state looking identical
# to the success state, inside the module written to prevent exactly that.
#
# The three uncited ON flags are a real, recorded gap -- docs/specs/flag_register.md
# §1 -- and NOT something this pin should force a fake ADR reference to satisfy.
# So the assertion is the honest one: the parametrization must not be empty.
governed_on = [
(flag, adr)
for flag, (default, cited) in flags.items()
if default == "True"
for adr in cited
]
assert governed_on, (
"no default-ON flag cites an ADR, so "
"test_default_on_flag_is_not_governed_by_a_proposed_adr now generates zero "
"cases and passes vacuously. Either a citation was lost from a comment block "
"in core/config.py, or the ON surface changed — see docs/specs/flag_register.md §1"
)
@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."
)