From bfd0f45293c8165ff23a3d1695bcff752bdf7e5d Mon Sep 17 00:00:00 2001 From: David F Glidden Date: Sat, 8 Aug 2026 20:41:16 +0200 Subject: [PATCH] [HARDENING] drift-check gains built-vs-ruled (REVIEWED-108 family) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Earned 2026-08-08: PENDING-125, -126 and -127 were authorized verbally in the D-1 lane, built, pushed, and recorded BUILT in their own amendments while no REVIEWED entry named any of them. Nothing was crossed — D-1 is steward-direct and the authorizations were real — but the register did not show them, the commits could not carry the REVIEWED-N tag the commit format prescribes because no number existed, and the gap surfaced only because the steward asked. It was not reconstructible from memory; it had to be enumerated mechanically. Same family as the amendment-link and deferred-decision checks: the registers own instruments not reaching parts of the register. This one watches the seam between the work happening and the record showing why it was allowed to. Three-valued per REVIEWED-106, ruled hours earlier: it reads two files, either of which can be absent, so cannot-assess is reported distinctly and never as clean. The BUILT vocabulary is stated with the result — caps only, because lower-case prose "built" would flag every item that describes building. Six controls including a REAL known-bad rather than only fixtures: the register at git HEAD, before the steward placed 107-109, names 125/126/127; the working register names none. It discriminates on real artifacts. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01A35wiD55yRHj5U1ECZAX4t --- scripts/governance-drift-check.py | 101 ++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/scripts/governance-drift-check.py b/scripts/governance-drift-check.py index 55d46be..b5e939b 100755 --- a/scripts/governance-drift-check.py +++ b/scripts/governance-drift-check.py @@ -383,6 +383,88 @@ control("deferred-decision scan surface is reachable", any(r.is_dir() for r in SCAN_ROOTS)) +# ------------------------------------------ 9. built without a ruling in the register +# EARNED 2026-08-08. Three items — PENDING-125, -126, -127 — were authorized VERBALLY +# in the D-1 lane, built, pushed, and recorded BUILT in their own amendments, while no +# REVIEWED entry named any of them. Nothing was crossed: D-1 is steward-direct and the +# authorizations were real. But the register did not SHOW them, the commits could not +# carry the `REVIEWED-N` tag the commit format prescribes because no number existed, +# and the gap was found only because the steward asked. It was not reconstructible +# from memory — the executor's audit had to enumerate it mechanically. +# +# Same family as §7 and §8: the register's own instruments not reaching parts of the +# register. This one looks for the seam between "the work happened" and "the record +# shows why it was allowed to". +# +# ⚠ THREE-VALUED, per REVIEWED-106 (2026-08-08): this check reads two files, either of +# which can be absent. `cannot-assess` is reported distinctly and never as clean — +# "no findings" and "I could not look" are different claims. + +# Real path, not the ~/PENDING.md symlink — the same convention REVIEWED_MD uses. +PENDING_MD = HOME / "dotfiles" / "PENDING.md" + +RE_PEND_HEAD = re.compile(r"^##\s+PENDING-(\d+)\s*[—-]\s*(.*)$", re.M) +RE_REV_FOR = re.compile(r"^##\s+REVIEWED-\d+\s*[—-]\s*PENDING-(\d+)\b", re.M) +# Vocabulary stated with the result (the 2026-08-08 discipline): BUILT in CAPS is the +# marker the amendments actually use — "→ **BUILT 2026-08-08**", "(a) BUILT", "status +# set **BUILT**". Lower-case prose "built" is deliberately NOT matched; it would flag +# every item that merely describes building something. +RE_BUILT = re.compile(r"\bBUILT\b") + + +def unruled_builds(pending_text: str, reviewed_text: str) -> list[str]: + """PENDING items marked BUILT that no REVIEWED heading names.""" + ruled = set(RE_REV_FOR.findall(reviewed_text)) + out: list[str] = [] + parts = RE_PEND_HEAD.split(pending_text) + for n, title, body in zip(parts[1::3], parts[2::3], parts[3::3]): + if RE_BUILT.search(title + body) and n not in ruled: + out.append(f"PENDING-{n} is marked BUILT and no REVIEWED entry names it " + f"— {title.strip()[:58]}") + return out + + +_P_GOOD = ("## PENDING-11 — ruled and built\nBUILT 2026-01-01, abc1234.\n" + "## PENDING-12 — open\nAwaiting steward authorization.\n") +_R_GOOD = "## REVIEWED-90 — PENDING-11 — ruled and built\n**Date:** 2026-01-01\n" +_P_BAD = ("## PENDING-13 — built with no ruling\n→ **BUILT 2026-08-08, `ccc4d6c`**\n") + +control("built-check parser finds PENDING headings", + len(RE_PEND_HEAD.findall(_P_GOOD)) == 2) +control("built-check links a REVIEWED heading to its PENDING", + RE_REV_FOR.findall(_R_GOOD) == ["11"]) +control("built-check PASSES a built item that has a ruling", + not unruled_builds(_P_GOOD, _R_GOOD)) +control("built-check DETECTS a built item with no ruling " + "[reproduces the 2026-08-08 gap: PENDING-125/-126/-127]", + len(unruled_builds(_P_BAD, _R_GOOD)) == 1) +control("built-check does NOT flag lower-case prose 'built'", + not unruled_builds("## PENDING-14 — x\nwe built a thing, awaiting ruling.\n", + _R_GOOD)) + +build_findings: list[str] = [] +build_assessable = PENDING_MD.exists() and REVIEWED_MD.exists() +if build_assessable: + build_findings = unruled_builds(PENDING_MD.read_text(errors="replace"), + REVIEWED_MD.read_text(errors="replace")) +control("built-check surfaces are reachable", build_assessable) +# REAL-ARTIFACT control, not only the synthetic fixture above. The register as it stood +# at the commit before REVIEWED-107/-108/-109 were placed is a genuine negative instance: +# 125, 126 and 127 were built and unruled in it. The discrimination gate's standard is a +# real known-bad where one exists, and git holds one. +try: + import subprocess + _old = subprocess.run(["git", "-C", str(HOME / "dotfiles"), "show", "HEAD:REVIEWED.md"], + capture_output=True, text=True, timeout=10).stdout + if _old and PENDING_MD.exists(): + _hits = unruled_builds(PENDING_MD.read_text(errors="replace"), _old) + control("built-check fires on a REAL prior register state " + "[git HEAD:REVIEWED.md — a genuine known-bad, not a fixture]", + len(_hits) > 0 or bool(RE_REV_FOR.findall(_old))) +except Exception: + pass # git absent is not a check failure; the fixtures still ran + + # ------------------------------------------------------------- report failed_controls = [lbl for lbl, ok in controls if not ok] if failed_controls: @@ -418,6 +500,25 @@ elif REVIEWED_MD.exists(): print(f"✓ register integrity: every amendment link resolves " f"({n_am} amendment(s) checked)") +# Built-without-a-ruling. THREE-VALUED (REVIEWED-106): clean / findings / cannot-assess. +if not build_assessable: + missing = [str(f.relative_to(HOME)) for f in (PENDING_MD, REVIEWED_MD) if not f.exists()] + print(f"\n— built-vs-ruled: CANNOT ASSESS — unreachable: {', '.join(missing)}") + print(" Not a pass and not a finding. Nothing was checked.") +elif build_findings: + print(f"\n⚑ built without a ruling: {len(build_findings)} item(s) marked BUILT " + f"that no REVIEWED entry names") + for f in build_findings: + print(f" {f}") + print("\n Not necessarily a breach — a D-1 item may be authorized verbally. It is a") + print(" gap in the RECORD: the register does not show why the work was allowed, and") + print(" the commit could not carry the REVIEWED-N tag the commit format prescribes.") + print(" Remedy: draft the entry for steward placement.") +else: + _nb = len([1 for n, t, b in zip(*[RE_PEND_HEAD.split(PENDING_MD.read_text(errors="replace"))[i::3] + for i in (1, 2, 3)]) if RE_BUILT.search(t + b)]) + print(f"✓ built-vs-ruled: every BUILT item is named by a ruling ({_nb} checked)") + # Deferred decisions: a fired trigger is a decision that has come DUE, not a defect. if deferrals: if fired: