From 2fa1a3bde9ad9e1ad5bebf9ba477fbd77f22bbd3 Mon Sep 17 00:00:00 2001 From: Jonathan Borduas Date: Sat, 22 Aug 2026 02:30:03 +0100 Subject: [PATCH] tools: give the declared population a catcher -- it drifted 55 to 57 and the prose guarding it never ran tools/README.md declares an instrument count and publishes the command that produces it, under the sentence "Run it; do not trust the number below." Nothing ran it. At e66aeb4 the declaration read 55 and its own command returned 57, and all seven rows of that table had drifted in a single day: 55->57, 54->56, 109->112, 132->135, 74->76, 57->58, teamlead 23 unchanged. That sentence is principle-shaped. Principle-shaped rules bind 0 of 5 times in this repository; event-attached ones bind 3 of 3. So check-tools-index.py now extracts the command from the README's own fence, runs it, and exits 1 when the declaration and the command disagree -- in either direction, and also when the number beside the command and the number on the declared row disagree with each other, which is a different failure and is named separately. The command is extracted, never re-implemented. Re-implementing it would make this gate a seventh reading of a noun that already had six (#345), and the two would drift apart silently. Three defects of my own, in order, each left in the comments: Class D. The first draft printed "DRIFT ... says 55, its own command says 57" and exited 0, because the lines went into `out` and pop_rc was never read. The verdict contradicted its own report, inside the gate whose job is catching drift. Class A. The second draft returned VOID both when a README never declared a population and when the declaration had been deleted. Those are different propositions. It broke two existing self-tests that run check() over fixture trees which never declared one. Both absent is now N/A and joins the unchecked legs as PARTIAL; one absent is VOID. The case that leaves uncatchable -- deletion from the real README -- is asserted in selftest() instead, repo-anchored, where a repository-specific fact belongs. Ordering. The call was spliced 13 lines above where `unchecked` is declared and would have raised NameError on every run. Caught by asserting the two line numbers against each other before running it, not by running it. Controls, all against the real 1155-line gate: agreeing tree exits 0; row 55 vs tree 57 exits 1 and names both drifts; fence 99 vs row 57 exits 1 on the internal disagreement alone; a half-deleted declaration exits 2, never clean, and --selftest exits 3 on it. 33 ok lines, no FAIL. check-orientation, check-goal-conformance, check-tools-index and gate-selftests all exit 0. The table is re-measured and re-dated to 2026-08-22 at e66aeb4. Changing values under an unchanged date would have made the date a lie. Only the declared row has a catcher; the other six rows are still unguarded and the README now says so. Filed by ARCHITECT, session c83ecf77. Refs #345. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01RTX4A8KkC9cRRrt17yzjWQ --- scripts/check-tools-index.py | 104 ++++++++++++++++++++++++++++++++++- tools/README.md | 24 +++++--- 2 files changed, 119 insertions(+), 9 deletions(-) diff --git a/scripts/check-tools-index.py b/scripts/check-tools-index.py index f8b4614..7a8c070 100755 --- a/scripts/check-tools-index.py +++ b/scripts/check-tools-index.py @@ -363,6 +363,75 @@ def parse_count(tok): return WORDS.get(tok.lower()) +# ⛔ THE DECLARED POPULATION DECAYS, AND THE SENTENCE GUARDING IT IS PRINCIPLE-SHAPED. +# tools/README.md declares a number AND publishes the command that produces it, under the sentence +# "Run it; do not trust the number below." Nothing ran it. On 2026-08-22 the declaration read 55 +# and its own command returned 57 — and all seven rows of that table had drifted in one day. +# Principle-shaped rules bind 0 of 5 times in this repository; event-attached ones bind 3 of 3. +# This is the event. +# +# POPULATION the declared instrument count in tools/README.md +# PREDICATE equals what the command published beside it returns +# CHANNEL the working tree this gate was pointed at, via sh -c, cwd=root +# +# ⚠ The command is EXTRACTED from the README, never re-implemented here. Re-implementing it would +# make this gate a SEVENTH reading of a noun that already had six (#345), and the two would drift +# apart silently. Extracting it also catches the case a hand-written predicate would miss: editing +# the command without editing the number. +DECLARED_CMD = re.compile(r"^\s*(ls tools/[^\n]*?)\s+⇒\s*(\d+)\.", re.M) +DECLARED_ROW = re.compile(r"^(.*?)\s+(\d+)\s+⇐ THE DECLARED POPULATION\s*$", re.M) + + +def declared_population(root, text): + """(rc, lines). rc None = this tree declares no population, so the check does not apply.""" + cmd_m = DECLARED_CMD.search(text) + row_m = DECLARED_ROW.search(text) + + # ⛔ "NEVER DECLARED" AND "DECLARATION DELETED" ARE DIFFERENT PROPOSITIONS (class A, a collapsed + # pair). The first draft returned VOID for both and broke two existing self-tests, which run + # check() over minimal FIXTURE trees whose README never carried a declaration. A fixture that + # never declared a population is not drifting — the check does not apply to it. + # + # BOTH absent ⇒ N/A, joins the unchecked legs, reports PARTIAL, never 'clean'. + # ONE absent ⇒ VOID. A half-present declaration IS mutilated, and no fixture has that shape. + # + # ⚠ That leaves "someone deletes the declaration from the REAL README" uncatchable HERE, by + # construction. selftest() carries that one instead — a repository-specific fact asserted + # against the repository, not smuggled into a function meant to work on any tree. + if not cmd_m and not row_m: + return None, [" ---- tools/README.md declares no population — that leg NOT CHECKED", + " (a tree that never declared one is not drifting; see selftest)"] + if not cmd_m or not row_m: + missing = "the published command" if not cmd_m else "the ⇐ THE DECLARED POPULATION row" + return 2, [f" VOID a population IS declared but {missing} is absent — established nothing"] + + cmd, beside = cmd_m.group(1), int(cmd_m.group(2)) + label, declared = row_m.group(1).strip(), int(row_m.group(2)) + try: + r = subprocess.run(["sh", "-c", cmd + " | wc -l"], cwd=str(root), + capture_output=True, text=True, timeout=20) + got = int(r.stdout.strip()) + except (subprocess.TimeoutExpired, ValueError) as e: + return 2, [f" VOID the README's own command produced no count ({e!r}) — established", + f" nothing. cmd: {cmd}"] + + lines = [f" ---- declared population: {declared} ({label})", + f" ---- the README's own command returns: {got}", + f" {cmd}"] + bad = [] + if got != declared: + bad.append(f" DRIFT the ⇐ THE DECLARED POPULATION row says {declared}, its own command" + f" says {got}") + if beside != declared: + # ⚠ Two numbers inside one README disagreeing is a DIFFERENT failure from either + # disagreeing with the tree. Naming them apart is what lets a reader fix the right one. + bad.append(f" DRIFT the number beside the command ({beside}) and the declared row" + f" ({declared}) are not the same number") + if bad: + return 1, lines + bad + return 0, lines + [" ok declaration, published command, and tree all agree"] + + def check(root): """Return (exit_code, lines). Pure enough to test against a fixture tree.""" out = [] @@ -391,6 +460,14 @@ def check(root): failed = False unchecked = [] # legs that established NOTHING, so the summary cannot claim them + # ⚠ Placed AFTER `unchecked` is declared, not beside the other README parsing 13 lines up, + # where the first attempt put it and would have raised NameError on every run. Caught by + # asserting the two line numbers against each other rather than by running it. + pop_rc, pop_lines = declared_population(root, text) + out.extend(pop_lines) + if pop_rc is None: + unchecked.append("declared population") + held = set() # names impounded at the TOP level (for the `extra` leg only) held_paths = set() # every impounded path, tools/-relative — the ack file's population @@ -651,7 +728,13 @@ def impound(label, directory, names, rel=""): if unchecked and not failed: out.append(f" PARTIAL rows and prose verified; {len(unchecked)} leg(s) established" f" NOTHING: {', '.join(unchecked)} — not 'clean'") - return (1 if failed else 0), out, bool(unchecked) + # ⛔ pop_rc MUST REACH THE EXIT CODE. The first draft printed "DRIFT ... says 55, its own + # command says 57" and then exited 0, because the lines went into `out` and the code was never + # read. That is class D — the verdict contradicts its own report — committed inside the gate + # whose job is catching drift. A caller reading only the status saw a clean tree. + if pop_rc == 2: + return 2, out, bool(unchecked) + return (1 if (failed or pop_rc) else 0), out, bool(unchecked) def selftest(): @@ -1121,6 +1204,25 @@ def held_count(lines_): rc, _, _ = check(root) ok &= (rc == 2) print(f" {'ok ' if rc == 2 else 'FAIL'} void: an unparseable index exits 2, not 0 (got {rc})") + # ⛔ THE ONE CASE declared_population() CANNOT CATCH, ASSERTED WHERE IT BELONGS. + # It treats "no declaration at all" as N/A so it still works on fixture trees; the cost is that + # deleting the declaration from the REAL tools/README.md would go quiet. This is that catcher. + real = Path(__file__).resolve().parent.parent / "tools" / "README.md" + if not real.is_file(): + print(" FAIL tools/README.md unreadable from the checkout — cannot assert") + ok = False + else: + rt = real.read_text(encoding="utf-8") + hit_cmd, hit_row = bool(DECLARED_CMD.search(rt)), bool(DECLARED_ROW.search(rt)) + if hit_cmd and hit_row: + print(" ok the real tools/README.md still declares a population AND publishes" + " its command") + else: + print(" FAIL the real tools/README.md lost %s — the declaration is what this gate" + % ("its published command" if not hit_cmd else "its ⇐ THE DECLARED POPULATION row")) + print(" is aimed at, and removing it must not read as passing") + ok = False + return 0 if ok else 3 diff --git a/tools/README.md b/tools/README.md index ffb20a4..86c8737 100644 --- a/tools/README.md +++ b/tools/README.md @@ -10,7 +10,7 @@ below is taken over it.** ``` INSTRUMENT ≡ a non-test executable directly under tools/, EXCLUDING quarantined subdirectories - ls tools/*.py tools/*.sh | grep -v '/test_' ⇒ 55. Run it; do not trust the number below. + ls tools/*.py tools/*.sh | grep -v '/test_' ⇒ 57. Run it; do not trust the number below. ``` ⚠ **The first draft of this section declared 54 and published a command that returns 55** — off by the @@ -18,19 +18,27 @@ single `.sh`. ⛔ **In the section whose entire purpose is removing that ambigui RUNNING the command rather than trusting it.** ★ **`merge-watch.sh` is an instrument; a definition that excludes it because of its extension is drawing the population around a file suffix.** -**Measured 2026-08-21 at `c7b5a46`, one second, all six readings, so a reader meeting an older figure +**Measured 2026-08-22 at `e66aeb4`, one second, all six readings, so a reader meeting an older figure can place it:** ``` -top-level non-test executables (.py + .sh) 55 ⇐ THE DECLARED POPULATION - of which .py 54 ⚠ every count published on 2026-08-21 used THIS subset -top-level tools/*.py, including test_ 109 -ALL .py under tools/ recursively 132 -ALL .py under tools/ excluding test_ 74 -rows in the index table below 57 +top-level non-test executables (.py + .sh) 57 ⇐ THE DECLARED POPULATION + of which .py 56 ⚠ every count published on 2026-08-21 used THIS subset +top-level tools/*.py, including test_ 112 +ALL .py under tools/ recursively 135 +ALL .py under tools/ excluding test_ 76 +rows in the index table below 58 files under tools/teamlead/ 23 ⛔ QUARANTINED — belonging is an OPEN QUESTION ``` +⛔ **ALL SEVEN of these drifted in ONE DAY** — 55→57, 54→56, 109→112, 132→135, 74→76, 57→58 — **and +the sentence *"Run it; do not trust the number below"* did not catch any of them, because nothing ran +it.** ★ **That sentence is PRINCIPLE-shaped, and principle-shaped rules bind 0 of 5 times here; +event-attached ones bind 3 of 3.** ⇒ **`scripts/check-tools-index.py` now EXTRACTS the command from +the fence above and runs it, and exits 1 when the declaration and its own command disagree.** ⚠ **It +is a gate, not a promise: this table's other six rows are still unguarded, and only the declared row +has a catcher.** + ⚠ **SUBDIRECTORIES ARE OUT OF SCOPE AND THAT IS NOT AN OVERSIGHT.** `scripts/check-tools-index.py` holds them to a **weaker contract by design** — *named in their own README, not row + prose + count* — and `tools/teamlead/` is under quarantine, where **belonging is deliberately unresolved.** ⇒ ★ **The