#!/usr/bin/env python3 """Which control gates are due for a keep-or-kill argument? (ADR-0006 D3) CB-WP-0009 T02. Six passes produced five standing control mechanisms and no way to retire any of them. Gates accumulate monotonically because each one was justified once, at the moment it was cheapest to justify. This reads `gates.toml` and reports two things: * gates past `review_by` — the date someone said they would argue for keeping it; * gates whose `caught` list is **empty** — which is not proof a gate is useless (it may be preventing rather than missing), but is the argument that has to be made out loud rather than never. It **reports**. It does not fail the build, for CB-RES-0005 §4's reason: a gate that blocks the remedy when the metric breaches is a trap. The only failure exit here is a broken registry — a file that cannot be read would otherwise report "0 gates, all healthy". Usage: python3 tools/gate-review.py python3 tools/gate-review.py --self-test """ import datetime import os import sys from repo import ROOT, enter_root REGISTRY = os.path.join(ROOT, "gates.toml") try: import tomllib except ModuleNotFoundError: # pragma: no cover - Python < 3.11 import tomli as tomllib class Fail(Exception): pass REQUIRED = ("id", "name", "checks", "added", "review_by", "retire_if") def load(path=REGISTRY): """Every gate, validated. A registry that parses but says nothing is the harness-does-nothing failure this project keeps finding.""" if not os.path.exists(path): raise Fail(f"{os.path.relpath(path, ROOT)} is missing") with open(path, "rb") as fh: data = tomllib.load(fh) gates = data.get("gate") or [] if not gates: raise Fail("registry parsed but contains no gates") for g in gates: missing = [k for k in REQUIRED if not g.get(k)] if missing: raise Fail(f"gate {g.get('id', '?')!r} is missing {missing}") for field in ("added", "review_by"): try: datetime.date.fromisoformat(g[field]) except ValueError as e: raise Fail(f"gate {g['id']!r} has a bad {field}: {e}") from e return gates def make_targets(path=None): """Targets declared in the Makefile, so an entry cannot name a command that does not exist.""" path = path or os.path.join(ROOT, "Makefile") targets = set() for line in open(path): if line and not line[0].isspace() and ":" in line and not line.startswith("."): name = line.split(":", 1)[0].strip() if name and " " not in name: targets.add(name) return targets def report(today=None): today = today or datetime.date.today() gates = load() targets = make_targets() overdue, quiet, broken = [], [], [] for g in gates: if datetime.date.fromisoformat(g["review_by"]) <= today: overdue.append(g) if not g.get("caught"): quiet.append(g) target = g.get("target") or "" if target and target not in targets: broken.append((g, target)) print("gate review — every gate is an experiment (ADR-0006 D3)") print(f" registry {len(gates)} gate(s), {REGISTRY.split('/')[-1]}") print(f" today {today.isoformat()}") print("\n gates") for g in gates: due = datetime.date.fromisoformat(g["review_by"]) days = (due - today).days mark = "DUE " if days <= 0 else ("soon" if days <= 30 else "ok ") print(f" [{mark}] {g['id']:<14} {g['name']:<28} " f"review {g['review_by']} ({days:+}d) caught {len(g.get('caught', []))}") if quiet: print("\n caught nothing yet — not a verdict, an argument that is owed") for g in quiet: print(f" {g['id']:<14} retire if: {g['retire_if']}") if overdue: print("\n DUE for a keep-or-kill argument") for g in overdue: print(f" {g['id']:<14} {g['retire_if']}") if broken: print("\n registry drift — entry names a target the Makefile lacks") for g, target in broken: print(f" {g['id']:<14} target {target!r}") print(f"\n {len(overdue)} due, {len(quiet)} silent, {len(broken)} drifted") print(" reporting only — never fails the build (CB-RES-0005 §4)") return 0 def self_test(): """Each check pins a way this tool could report a comfortable lie.""" results = [] def check(name, ok, detail=""): results.append((name, ok, detail)) gates = load() check("the real registry loads", len(gates) >= 5, f"{len(gates)} gate(s)") check("every gate names what would retire it", all(len(g["retire_if"]) > 20 for g in gates)) # Registry drift is the failure this exists to prevent: a gate added # to the Makefile with no entry, or an entry for a deleted target. targets = make_targets() named = [(g["id"], g["target"]) for g in gates if g.get("target")] check("every named target exists in the Makefile", all(t in targets for _i, t in named), ", ".join(f"{i}:{t}" for i, t in named if t not in targets) or "all present") check("Makefile targets were actually parsed", len(targets) >= 15, f"{len(targets)} target(s)") import tempfile def registry(text): fh = tempfile.NamedTemporaryFile("w", suffix=".toml", delete=False) fh.write(text) fh.close() return fh.name # A registry that parses to nothing must abort, not report "0 due". empty = registry("# no gates here\n") try: load(empty) check("an empty registry aborts", False, "reported a clean bill") except Fail: check("an empty registry aborts", True) finally: os.unlink(empty) # A missing required field must abort rather than be treated as absent # evidence — `caught` is optional, the rest are not. partial = registry('[[gate]]\nid = "X"\nname = "n"\nchecks = "c"\n' 'added = "2026-01-01"\nreview_by = "2026-02-01"\n') try: load(partial) check("a gate with no retire_if aborts", False, "accepted") except Fail: check("a gate with no retire_if aborts", True) finally: os.unlink(partial) # The overdue arithmetic must actually fire. import io from contextlib import redirect_stdout buf = io.StringIO() with redirect_stdout(buf): report(today=datetime.date(2099, 1, 1)) late = buf.getvalue() check("a far-future date marks every gate due", late.count("[DUE ]") == len(gates), f"{late.count('[DUE ]')} of {len(gates)}") buf = io.StringIO() with redirect_stdout(buf): report(today=datetime.date(2020, 1, 1)) early = buf.getvalue() check("a far-past date marks none due", "[DUE ]" not in early) check("silent gates are named either way", "caught nothing yet" in early) print("gate-review self-test (positive control)") ok = True for name, passed, det in results: print(f" [{'ok ' if passed else 'FAIL'}] {name}" + (f" — {det}" if det else "")) ok &= passed return 0 if ok else 1 def main(): enter_root() try: if "--self-test" in sys.argv: return self_test() return report() except Fail as e: print(f"gate-review: {e}", file=sys.stderr) return 1 if __name__ == "__main__": sys.exit(main())