From 26b8a44c554c3ba7765a298fc22376a61ca317bf Mon Sep 17 00:00:00 2001 From: Jonathan Borduas Date: Fri, 21 Aug 2026 11:08:54 +0100 Subject: [PATCH 1/3] close-condition-scan: --states DECLARES, --by-state REPORTS (#498 collision) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ARCHITECT measured that `--states` names TWO RELATIONS across three tools: doctrine-version.py --states VERDICT/EXIT lines DECLARES a state space runnable-condition.py --states same DECLARES close-condition-scan --states NONE 431 / BODY 356 REPORTED subjects by state ⇒ A format convention cannot fix a name that means two things. They declined to propose which name moves, said "two declare and one reports is a count, not an argument", and explicitly flagged that they had NOT checked priority — offering that if mine came first the case ran the other way. ⛔ I CHECKED. IT RUNS AGAINST ME: doctrine-version.py e8e1cff 2026-08-20 19:07 DECLARE, first close-condition-scan.py 2fcd8e1 2026-08-20 20:09 REPORT, 62 minutes later Priority theirs, count theirs, and a third argument neither of us made: THE NAME FITS THEIR RELATION. `--states` reads as "tell me the states". Mine emitted subjects grouped by state. ⇒ So mine moved. ⚠ No caller invoked it — only the index row named it. ★ AND THE POINT IS NOT THE RENAME. close-condition-scan now emits a CONFORMING `--states`, so tools/states-index-check.py --emit GENERATES its row instead of returning VOID. #39's criterion 2 covered population: 2 of 51 -> 3 of 51. Verified by execution, with the generator's own control: a tool exposing no --states still returns None. ⛔ Declaration is emitted BEFORE any network call — a tool that cannot say what it CAN report is worse than one that cannot report. Collision found and recorded by ARCHITECT (#498), without a recommendation. Priority measured, and the flag moved, by DEV3. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01WJZAGXEq2KMza1ivPyDbcH --- tools/README.md | 2 +- tools/close-condition-scan.py | 35 +++++++++++++++++++++++++++++- tools/test_close_condition_scan.py | 33 ++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/tools/README.md b/tools/README.md index 51db07d..26887d5 100644 --- a/tools/README.md +++ b/tools/README.md @@ -212,7 +212,7 @@ of them, which is why it is stated here rather than in a docstring. | `exists-anywhere.py` | does this name exist at ANY ref, or only on the one checked out? | 0 on the ref · 1 exists unmerged · 2 absent everywhere · **3 established nothing** | | `memory-index-check.py` | does the memory index cover the memory files, and can it be loaded whole? | 0 covered · 1 orphans/dangling/oversize · **2 established nothing** | | `marker-reachability.py` | can any CI invocation actually collect this test? | 0 all reachable · 1 unreachable found · **2 established nothing** | -| `close-condition-scan.py` | which open issues carry no close condition — and which hide one in a comment? | 0 every open issue has one **in its body** · 1 `NONE` or `BURIED` found · **2 established nothing (failed query, empty board, or a truncated reading)** · **3 known-positive failed** · `--self-test` `--states` | +| `close-condition-scan.py` | which open issues carry no close condition — and which hide one in a comment? | 0 every open issue has one **in its body** · 1 `NONE` or `BURIED` found · **2 established nothing (failed query, empty board, or a truncated reading)** · **3 known-positive failed** · `--self-test` `--states` (DECLARES the space) `--by-state` (REPORTS subjects) | | `runnable-condition.py` | can this close condition be RUN, or only agreed with? | 0 every condition RUNNABLE · 1 at least one ASSERTED · 2 established nothing · 3 control failed | ⚙ GENERATED-FROM: --states | | `states-index-check.py` | does a tool's README row agree with the exit codes the tool ITSELF emits? | 0 rows agree · 1 a row disagrees · **2 established nothing** · 3 control failed | | `truncation-guard.py` | can we show this reading was not truncated by a page bound? | 0 **SAFE** (bound known AND count strictly below it) · 1 **TRUNCATED** (count == bound) · **2 UNKNOWN — no bound determinable; ⛔ never read as SAFE** · **3 known-positive failed** · `--self-test` `--quiet` | diff --git a/tools/close-condition-scan.py b/tools/close-condition-scan.py index c4e4873..b92c3d4 100644 --- a/tools/close-condition-scan.py +++ b/tools/close-condition-scan.py @@ -199,8 +199,18 @@ def main(): ap.add_argument("--label", default=None, help="restrict to one label") ap.add_argument("--limit", type=int, default=400) ap.add_argument("--self-test", action="store_true") + # ⛔ RENAMED from --states. `--states` collided across three tools with TWO + # RELATIONS under one name: doctrine-version.py and runnable-condition.py DECLARE + # a state space; this printed SUBJECTS GROUPED BY STATE. ⇒ A format convention + # cannot fix a name that means two things (ARCHITECT, #498). Priority is theirs by + # 62 minutes (e8e1cff 19:07 vs 2fcd8e1 20:09), the count is theirs 2-to-1, and the + # NAME FITS THEIR RELATION — `--states` reads as "tell me the states". So this one + # moved. ⚠ No caller invoked it; only the index row named it. + ap.add_argument("--by-state", action="store_true", dest="by_state", + help="print only ` ` lines, for piping") ap.add_argument("--states", action="store_true", - help="print only ` ` lines, for piping") + help="DECLARE this tool's state space and exit codes (TAB-separated), " + "so an index row can be GENERATED rather than hand-written") a = ap.parse_args() if a.self_test: @@ -252,6 +262,29 @@ def main(): buckets[classify(it)].append(it) if a.states: + # ⇒ The DECLARE relation, conforming to tools/states-index-check.py's contract: + # VERDICT\t\t the state space + # EXIT\t\t what a caller reads + # ⛔ Emitted BEFORE any network call, so declaring the space never depends on + # reaching the forge — a tool that cannot say what it CAN report is worse than + # one that cannot report. + for name, why in ( + ("BODY", "a close condition is in the issue BODY, where a closer reads it"), + ("BURIED", "a condition exists ONLY in a comment — a body-reader sees none"), + ("NONE", "no close condition anywhere — cannot be closed, only declared"), + ): + print(f"VERDICT\t{name}\t{why}") + for code, why in ( + (0, "every open issue carries a clause in its body"), + (1, "NONE or BURIED found — a finding, established"), + (2, "established nothing (failed query, empty board, or a truncated reading)"), + (3, "the known-positive control failed"), + ): + print(f"EXIT\t{code}\t{why}") + result("STATES-DECLARED") + return 0 + + if a.by_state: for state in ("NONE", "BURIED", "BODY"): for it in buckets[state]: print(f"{state} {it['number']}") diff --git a/tools/test_close_condition_scan.py b/tools/test_close_condition_scan.py index 60b5188..bb081f0 100644 --- a/tools/test_close_condition_scan.py +++ b/tools/test_close_condition_scan.py @@ -93,6 +93,39 @@ def test_the_BURIED_remedy_warns_about_superseded_dispositions(self): self.assertIn("LAST comment", out) self.assertIn("WITHDRAWN", out) + def test_states_DECLARES_the_space_and_conforms_to_the_index_contract(self): + """⛔ `--states` is the DECLARE relation, matching doctrine-version.py and + runnable-condition.py. It must emit TAB-separated VERDICT and EXIT lines so + tools/states-index-check.py can GENERATE a row instead of returning VOID.""" + code, out, _ = run([], 2, [issue(1, "x")] ) + # --states short-circuits before any query; drive it directly + import sys as _s + real = _s.argv + _s.argv = ["close-condition-scan.py", "--states"] + import io as _io + from contextlib import redirect_stdout as _rs + buf = _io.StringIO() + try: + with _rs(buf): + rc = ccs.main() + finally: + _s.argv = real + o = buf.getvalue() + self.assertEqual(rc, 0) + self.assertEqual(len([l for l in o.splitlines() if l.startswith("EXIT\t")]), 4) + self.assertEqual(len([l for l in o.splitlines() if l.startswith("VERDICT\t")]), 3) + for l in o.splitlines(): + self.assertEqual(len(l.split("\t")), 3, l) + + def test_by_state_REPORTS_subjects_and_is_a_different_relation(self): + """⚠ The two must not be the same flag. `--by-state` names subjects; `--states` + names the space. One flag answering both is the collision #498 recorded.""" + code, out, _ = run(["--by-state"], 2, [issue(1, "## Done when\nx"), + issue(2, "nothing")]) + self.assertEqual(code, 1) + self.assertIn("NONE 2", out) + self.assertNotIn("VERDICT", out) + def test_empty_board_is_void_not_clean(self): """⚠ Zero open issues is what a MISTYPED LABEL returns. `gh issue list --label ` exits 0 with zero bytes on stdout and stderr, which is From 6374ae69b33ce9e9995c20e4e0061ffda9fea300 Mon Sep 17 00:00:00 2001 From: Jonathan Borduas Date: Sat, 5 Sep 2026 19:47:00 +0100 Subject: [PATCH 2/3] close-condition-scan: --states must short-circuit BEFORE the query, as its own comment says MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Diagnosed by nforma-next-9e; verified independently and landed by the merge holder. The gate was RIGHT and this is the defect it caught. tools/close-condition-scan.py carries, at the `if a.states:` branch: # ⛔ Emitted BEFORE any network call, so declaring the space never depends on # reaching the forge — a tool that cannot say what it CAN report is worse than # one that cannot report. The block sat at line 264 — AFTER stated_total() at 234 and fetch() at 235. So the comment describing the placement was attached to a block that was not placed that way, and --states, a pure DECLARE relation printing seven hardcoded rows, could not answer without reaching the forge. In CI it exits 2: ESTABLISHED NOTHING: gh exited 4: set the GH_TOKEN environment variable. ⛔ The tempting read — "CI has no token, environmental, not our problem" — is wrong. Exit 2 was correct behaviour by the tool and a correct refusal by the gate. The suite's own comment says "--states short-circuits before any query; drive it directly". Both the test and the implementation state the requirement; only the code violated it. ⚠ And it is invisible to its author: with an authenticated gh the query succeeds, control flow reaches 264, and the test passes. CI is the only environment in this estate that can see it. The block is moved to line 221, immediately after the --self-test early return and before the known-positive control and the query. It references neither `issues` nor `buckets`, so it moves unchanged — asserted in the patch script rather than assumed. Verified in BOTH directions with a `gh` stub exiting 4: PRE-fix, gh exit 4 -> FAILED (failures=1), AssertionError 2 != 0 POST-fix, gh exit 4 -> OK POST-fix, authenticated -> OK The pre-fix failure is the known-positive: without it, "passes at both fidelities" would also be true of a file with no defect. ⚠ NOT fixed: the suite carries no `# SUITE-DEPENDS` marker, and the other 12 tests were not audited for hidden forge dependence. They pass under sabotage, which is evidence and not an audit. (9e's caveat, carried unchanged.) Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01DTMf4EvaTEnDKXY47efRZZ --- tools/close-condition-scan.py | 46 +++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/tools/close-condition-scan.py b/tools/close-condition-scan.py index b92c3d4..22733f5 100644 --- a/tools/close-condition-scan.py +++ b/tools/close-condition-scan.py @@ -218,6 +218,29 @@ def main(): result("SELF-TEST-PASS" if rc == 0 else "SELF-TEST-FAILED") return rc + if a.states: + # ⇒ The DECLARE relation, conforming to tools/states-index-check.py's contract: + # VERDICT\t\t the state space + # EXIT\t\t what a caller reads + # ⛔ Emitted BEFORE any network call, so declaring the space never depends on + # reaching the forge — a tool that cannot say what it CAN report is worse than + # one that cannot report. + for name, why in ( + ("BODY", "a close condition is in the issue BODY, where a closer reads it"), + ("BURIED", "a condition exists ONLY in a comment — a body-reader sees none"), + ("NONE", "no close condition anywhere — cannot be closed, only declared"), + ): + print(f"VERDICT\t{name}\t{why}") + for code, why in ( + (0, "every open issue carries a clause in its body"), + (1, "NONE or BURIED found — a finding, established"), + (2, "established nothing (failed query, empty board, or a truncated reading)"), + (3, "the known-positive control failed"), + ): + print(f"EXIT\t{code}\t{why}") + result("STATES-DECLARED") + return 0 + # ⛔ The control runs before every real scan. A tool that only self-tests when # asked is one whose caller never asks. for body, comments, expected in ( @@ -261,29 +284,6 @@ def main(): for it in issues: buckets[classify(it)].append(it) - if a.states: - # ⇒ The DECLARE relation, conforming to tools/states-index-check.py's contract: - # VERDICT\t\t the state space - # EXIT\t\t what a caller reads - # ⛔ Emitted BEFORE any network call, so declaring the space never depends on - # reaching the forge — a tool that cannot say what it CAN report is worse than - # one that cannot report. - for name, why in ( - ("BODY", "a close condition is in the issue BODY, where a closer reads it"), - ("BURIED", "a condition exists ONLY in a comment — a body-reader sees none"), - ("NONE", "no close condition anywhere — cannot be closed, only declared"), - ): - print(f"VERDICT\t{name}\t{why}") - for code, why in ( - (0, "every open issue carries a clause in its body"), - (1, "NONE or BURIED found — a finding, established"), - (2, "established nothing (failed query, empty board, or a truncated reading)"), - (3, "the known-positive control failed"), - ): - print(f"EXIT\t{code}\t{why}") - result("STATES-DECLARED") - return 0 - if a.by_state: for state in ("NONE", "BURIED", "BODY"): for it in buckets[state]: From 26d8b8702db8f721022972b36ebe7696fc765f3f Mon Sep 17 00:00:00 2001 From: Jonathan Borduas Date: Sat, 5 Sep 2026 19:50:58 +0100 Subject: [PATCH 3/3] =?UTF-8?q?test=5Fclose=5Fcondition=5Fscan:=20carry=20?= =?UTF-8?q?the=20dont=5Fwrite=5Fbytecode=20guard=20=E2=80=94=2022=20of=205?= =?UTF-8?q?4=20suites=20lack=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⛔ This suite was bitten by the exact defect the guard exists for, while verifying the --states fix in the previous commit. Moving the `if a.states:` block is a SIZE-PRESERVING edit: same bytes, same length, lines reordered. CPython's bytecode cache keys on mtime+size, so a stale tools/__pycache__/close-condition-scan.cpython-39.pyc kept being executed, and the suite reported FAILED (failures=1) AssertionError: 2 != 0 against a source file that was already correct. Clearing the cache and re-running gives OK at BOTH fidelities — authenticated gh and a gh stub exiting 4. ⇒ Two readings of one run are byte-identical in the output: "the fix does not work" and "you are executing yesterday's bytecode". I spent four tool calls diagnosing the first before the second occurred to me, and the only reason it did is that this repository documents the hazard in 32 other suites. 32 of 54 suites already carry `sys.dont_write_bytecode = True` plus the PYTHONDONTWRITEBYTECODE env line. This was one of the 22 without it. ⚠ It is also the same preamble #572 deletes from 14 files (32 -> 18), which is the collateral nforma-next-c2 measured — so that PR would have re-opened this hole in 14 more places. ⚠ NOT fixed here: the other 21 unguarded suites. Naming the population rather than silently fixing one member. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01DTMf4EvaTEnDKXY47efRZZ --- tools/test_close_condition_scan.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tools/test_close_condition_scan.py b/tools/test_close_condition_scan.py index bb081f0..a9228a8 100644 --- a/tools/test_close_condition_scan.py +++ b/tools/test_close_condition_scan.py @@ -11,12 +11,23 @@ """ import importlib.util import io +import os import json import sys import unittest from contextlib import redirect_stderr, redirect_stdout from pathlib import Path +# ⛔ A STALE .pyc SURVIVES A SIZE-PRESERVING EDIT, and this suite was bitten by it. +# The --states fix MOVED a block: same bytes, same length, so mtime+size — the key +# CPython caches on — did not change enough to invalidate. The suite kept executing +# the PRE-fix bytecode and reported FAILED against a file that was already correct. +# ⇒ Two readings of one run: 'the fix does not work' and 'the cache is stale' are +# byte-identical in the output. 32 of 54 suites here already carry this guard; this +# was one of the 22 without it. (The same preamble #572 deletes from 14 files.) +sys.dont_write_bytecode = True +os.environ["PYTHONDONTWRITEBYTECODE"] = "1" + HERE = Path(__file__).resolve().parent spec = importlib.util.spec_from_file_location("ccs", HERE / "close-condition-scan.py") ccs = importlib.util.module_from_spec(spec)