#!/usr/bin/env python3 """What did this instrument kill? -- the re-open query for `docs/re/REFUTED.md`. tools/stale-instrument everything, grouped tools/stale-instrument render-vs-capture what one instrument killed tools/stale-instrument --ours only instruments that are ours tools/stale-instrument --check self-test; exits non-zero if broken Why this exists --------------- Rule R1. A refutation whose instrument is one of our renderers is not a refutation -- it is *"our renderer disagrees"*. The register carries that as a 🟑, but a colour alone does not re-open anything: the failure it is there to prevent was a real disc field sitting dead for weeks because **nothing re-opens a claim when the instrument that killed it improves.** So this is the query you run *after* you fix a renderer, a reader or the harness. It answers "what did I just invalidate?" in one command, instead of relying on someone remembering which of 222 entries rested on the thing they changed. The register is the only source. There is no second list to drift out of sync with it -- the tags live at the end of each entry, in the file people actually read, which is this corpus's own repeatedly-learned lesson about where a fact has to live to exist. """ from __future__ import annotations import os import re import sys from collections import OrderedDict REGISTER = os.environ.get("SYLPH_REGISTER", "docs/re/REFUTED.md") # Ours, in the sense R1 means: an instrument whose defects we cannot rule out, # because we built it. `our-tool` is ours too, but it is not disqualifying -- # see the note below and in the register's reading guide. OURS = {"render-vs-capture", "screen-render", "our-reader", "harness", "our-tool"} # Not a verdict. It means nobody recorded how the claim died. UNRECORDED = "unrecorded" TAG = re.compile(r"⟨([a-z][a-z-]*)⟩\s*$") def entries(text: str): """Every bullet, with its instrument tag and whether it is already 🟑. An entry is a `* ` bullet plus its continuation lines, ending at a blank line or a heading -- the same span rule the register's own enumeration uses. """ out, cur, start = [], None, 0 for i, line in enumerate(text.split("\n"), 1): if line.startswith("* "): if cur is not None: out.append((start, cur)) cur, start = [line], i elif line.startswith("#"): if cur is not None: out.append((start, cur)) cur = None elif cur is not None: if not line.strip(): out.append((start, cur)) cur = None else: cur.append(line) if cur is not None: out.append((start, cur)) for ln, body in out: joined = " ".join(x.strip() for x in body) m = TAG.search(body[-1].rstrip()) # πŸ”΄ THE VERDICT IS THE LEADING GLYPH, NOT ANY 🟑 IN THE ENTRY. Matching # anywhere counted four entries as re-opened that merely carry an inline # 🟑 caveat mid-sentence ("🟑 a third recorded run implies NEW GAME"). # An entry whose verdict is ❌ and whose *reach* is qualified is not an # open claim, and reporting it as one inflates exactly the number this # tool exists to make trustworthy. yield { "line": ln, "instrument": m.group(1) if m else None, "open": body[0].startswith("* 🟑"), "settled": body[0].startswith("* βœ…"), "text": re.sub(r"\s+", " ", joined), } def summary(text: str) -> str: """The claim, short enough to scan a list of them.""" t = re.sub(r"^\*\s*(🟑|❌|βœ…)?\s*", "", text) t = re.sub(r"[*`~]", "", t) return (t[:118] + "…") if len(t) > 118 else t def die(msg: str): """A HARNESS fault, exit 2 -- distinct from 'no such instrument', exit 1. πŸ”΄ `sys.exit("text")` exits **1**, not 2. Every one of these sites used it, so three faults that this file's own docstring calls exit-2 were reporting the same code as an ordinary miss -- a real failure wearing a wrong diagnosis, the exact shape the register catalogues elsewhere. Its own `--check` caught it on the first run, which is the argument for having one. """ print(msg, file=sys.stderr) raise SystemExit(2) def load(): if not os.path.exists(REGISTER): die( f"stale-instrument: {REGISTER} is not here.\n" " Run from the repository root, or set SYLPH_REGISTER.\n" " Exit 2: the harness is broken, not the corpus." ) rows = list(entries(open(REGISTER, encoding="utf-8").read())) # πŸ”΄ LIVENESS. A reader that parses nothing reports "no claims rest on that # instrument" -- which reads exactly like the good news you were hoping for. # A renamed file, a changed bullet shape or a lost tag all produce it. This # corpus has already produced one false zero from a reader invented minutes # earlier, so the empty case is a harness fault with its own exit code. if not rows: die("stale-instrument: parsed 0 entries from " f"{REGISTER} -- exit 2, the reader is broken.") if not any(r["instrument"] for r in rows): die(f"stale-instrument: {len(rows)} entries, none tagged βŸ¨β€¦βŸ© -- " "exit 2, the reader is broken or the register is untagged.") return rows def main(argv: list[str]) -> int: if "--check" in argv: return selftest() rows = load() only_ours = "--ours" in argv wanted = [a for a in argv if not a.startswith("-")] by = OrderedDict() for r in rows: by.setdefault(r["instrument"] or UNRECORDED, []).append(r) if wanted: name = wanted[0] if name not in by: print(f"no entry names ⟨{name}⟩. Known instruments:") for k in sorted(by): print(f" {k}") return 1 group = by[name] mine = " -- OURS, so these are re-openable" if name in OURS else "" print(f"⟨{name}⟩ killed {len(group)} claim(s){mine}\n") for r in group: mark = "🟑" if r["open"] else ("βœ…" if r["settled"] else "❌") print(f" {mark} L{r['line']:<4} {summary(r['text'])}") if name in OURS: print(f"\n If you have improved {name}, every ❌ above is a claim that" "\n died to an instrument that no longer exists in that form.") return 0 print(f"{len(rows)} entries in {REGISTER}\n") order = sorted(by, key=lambda k: (k not in OURS, k == UNRECORDED, -len(by[k]))) for k in order: g = by[k] tag = " ← OURS" if k in OURS else (" ← nobody wrote it down" if k == UNRECORDED else "") opened = sum(1 for r in g if r["open"]) note = f", {opened} already 🟑" if opened else "" print(f" {k:20s} {len(g):3d} claim(s){note}{tag}") ours = sum(len(by[k]) for k in by if k in OURS) unrec = len(by.get(UNRECORDED, [])) print(f"\n {ours} claim(s) died to an instrument WE BUILT.") print(f" {unrec} claim(s) record no instrument at all -- " "neither safe nor suspect,\n just unauditable. That is the backfill queue.") print("\n tools/stale-instrument to list one") return 0 def selftest() -> int: """Cases that must hold, executed -- not reasoned about. A control that does not run is not a control; this file's neighbours in `tools/` say so about other people's tools, so it is asserted here. """ import subprocess import tempfile me = os.path.abspath(__file__) ok = 0 def run(reg, args=()): env = dict(os.environ, SYLPH_REGISTER=reg) p = subprocess.run([sys.executable, me, *args], capture_output=True, text=True, env=env) return p.returncode, p.stdout + p.stderr with tempfile.TemporaryDirectory() as d: good = os.path.join(d, "good.md") open(good, "w", encoding="utf-8").write( '* "a" β†’ refuted. ⟨disc⟩\n\n* 🟑 "b" β€” our renderer disagrees. ⟨render-vs-capture⟩\n' ) cases = [ ("a tagged register parses", (good, ()), 0, "2 entries"), ("one instrument lists", (good, ("disc",)), 0, '"a"'), ("an OURS instrument says so", (good, ("render-vs-capture",)), 0, "re-openable"), ("an unknown instrument fails", (good, ("nope",)), 1, "no entry names"), ] for name, (reg, args), want_rc, want_txt in cases: rc, out = run(reg, args) good_rc = rc == want_rc good_tx = want_txt in out if good_rc and good_tx: print(f" {name:34s} exit {rc} βœ…") else: print(f" {name:34s} exit {rc} (wanted {want_rc}), " f"text {'found' if good_tx else 'MISSING'} πŸ”΄") ok = 1 # The two harness faults, which must be distinguishable from a clean run. empty = os.path.join(d, "empty.md") open(empty, "w").write("# nothing here\n") rc, out = run(empty) if rc == 2 and "reader is broken" in out: print(f" {'an empty register is a fault':34s} exit 2 βœ…") else: print(f" {'an empty register is a fault':34s} exit {rc}, wanted 2 πŸ”΄") ok = 1 untagged = os.path.join(d, "untagged.md") open(untagged, "w", encoding="utf-8").write('* "a" β†’ refuted.\n') rc, out = run(untagged) if rc == 2 and "none tagged" in out: print(f" {'an untagged register is a fault':34s} exit 2 βœ…") else: print(f" {'an untagged register is a fault':34s} exit {rc}, wanted 2 πŸ”΄") ok = 1 rc, _ = run(os.path.join(d, "does-not-exist.md")) if rc == 2: print(f" {'a missing register is a fault':34s} exit 2 βœ…") else: print(f" {'a missing register is a fault':34s} exit {rc}, wanted 2 πŸ”΄") ok = 1 print() print("the query fails when it must, and says so distinctly" if not ok else "πŸ”΄ the query's own control is broken") return ok if __name__ == "__main__": sys.exit(main(sys.argv[1:]))