Skip to content

docs(subsystem-index): the OPEN: block is read from the FROZEN MIRROR, so it can name already-RESOLVED bullets - #1450

Open
ZacxDev wants to merge 1 commit into
mainfrom
zach/subsystem-probe-reads-frozen-mirror
Open

ZacxDev wants to merge 1 commit into
mainfrom
zach/subsystem-probe-reads-frozen-mirror

Conversation

@ZacxDev

@ZacxDev ZacxDev commented Sep 9, 2026

Copy link
Copy Markdown
Member

The skill already says the local tree is a read-only mirror, and that writing there — or validating a path under it after a write — is wrong. It does not say that the probe's own reads come from it. Those reads are what the writer acts on.

subsystem_touch.py reads ~/.claude/analyze-service-index and prints that path on its store: line; cairn reads ~/.cache/subsystem-store. So the OPEN: block, the already there duplicate-check display, and the journal: N bullets, newest dated … line are all as stale as that mirror — and nothing in the output says the reading is second-hand. The store: line is the only tell, and it reads as provenance rather than a warning.

Measured 2026-09-09, while following this skill

The probe reported an entry had 2 bullets still declaring OPEN:, oldest unverified for 23 days. The live store already carried that exact bullet rewritten as RESOLVED <ref> (<date>) — closed five days earlier by another session.

mirror synced cache
that entry 162 lines 170 lines
newest file in scope 2026-09-02 2026-09-09

Following the block literally would have re-resolved an already-resolved bullet, or re-opened settled work. The remedy is one command: cairn sync, then read ~/.cache/subsystem-store/<scope>/<entry>.md before acting on any OPEN: row.

Also noted, because it cuts the other way: the same staleness makes the already there comparison miss bullets appended since the last refresh, so the duplicate-check can pass on content that is already recorded.

Instrument validated before trusting the suite

🔴 python3 <test>.py on these pytest modules collects nothing and exits 0 — a green from a command that never ran. Under pytest the protocol module runs 94 tests.

Negative control, aimed at the file the test actually forbids the string in (write-back.md — not the owner doc, which legitimately quotes the retired prompt):

  • injected append this to the index? (y/N)rc=1, 1 failed / 93 passed
  • restored → rc=0, 94 passed
  • test_subsystem_touch.py914 passed

Docs-only; no behaviour change.

…RROR, so it can name already-RESOLVED bullets

The skill already says the local tree is a read-only mirror and that WRITING
there, or validating a path under it after a write, is wrong. It does not say
that the probe's own READS come from it — and those reads are what the writer
acts on.

subsystem_touch.py reads ~/.claude/analyze-service-index and prints that path on
its store: line; cairn reads ~/.cache/subsystem-store. So every population in the
OPEN: block, the `already there` duplicate-check display, and the
`journal: N bullets, newest dated ...` line are as stale as that mirror, and
nothing in the output says the reading is second-hand. The store: line is the
only tell and it reads as provenance, not as a warning.

MEASURED 2026-09-09 while following this skill: the probe reported an entry had 2
bullets still declaring OPEN:, oldest unverified for 23 days. The live store
already carried that exact bullet rewritten as RESOLVED, closed five days earlier
by another session. The two copies of that one entry were 162 vs 170 lines, and
the mirror's newest file was a week behind the cache's. Following the block
literally would have re-resolved an already-resolved bullet, or re-opened settled
work.

The remedy is one command: cairn sync, then read
~/.cache/subsystem-store/<scope>/<entry>.md before acting on any OPEN: row.

Also noted, because it cuts the other way: the same staleness makes the
`already there` comparison miss bullets appended since the last mirror refresh,
so the duplicate-check can pass on content that IS already recorded.

Instrument validated before trusting the suite. `python3 <test>.py` on these
pytest modules collects NOTHING and exits 0 — a green from a command that never
ran; under pytest the protocol module runs 94 tests. Negative control, aimed at
the file the test actually forbids the string in (write-back.md, not the owner
doc, which legitimately quotes the retired prompt): injecting the y/N prompt
gives rc=1, 1 failed / 93 passed; restored gives rc=0, 94 passed. The
subsystem_touch suite is 914 passed.

Claude-Session-Id: b9707377-9b92-47c8-9548-e32c96a31786
ZacxDev added a commit that referenced this pull request Sep 11, 2026
…, and the cost is not "preflight"

Round-0 audit findings, all fixed.

1. The AST predicate was NARROWER THAN ITS OWN DOCSTRING — the defect class it
   exists to catch. It resolved `from subprocess import run as r` but NOT
   `import subprocess as sp; sp.run(...)`, a sibling spelling of the same
   aliasing the docstring claims to close; and it matched only `run`, so
   `Popen(...).communicate()` with no bound was invisible while the assertion
   beside it called exactly that "worse than the red it replaces". Both were
   measured SURVIVING. Now an enumerated `_SPAWNING_CALLABLES` plus resolution
   of module aliases and from-imports.

2. The constant's own comment said the 47s is "almost all the runner's fixed
   preflight rather than the tests". FALSE, and it is the load-bearing half: a
   `--targets` run still executes the hook-test and shell-test families, which
   `--targets` does not narrow. Of ~54s under `bash -x`, the SELECTED target's
   pytest is 2.9s and ~46s is those families. Discriminator, same box, same
   load ~52, minutes apart: `--targets <dir>` = 47s vs `--files <one file>` = 4s,
   because `--files` sets SCOPED_MODE and run-tests.sh:4518 drops the families.
   The comment now says so, names the root cause, and says this bound is the
   SYMPTOM fix.

3. The worst-case budget was asserted, not measured. Recorded: four real nested
   runs, so 4x600s = 40 min against a measured timeouts.tasks of 1h10m — 57% of
   the task budget, and a run that hits it posts NOTHING (checks pending
   forever). Stated as the trade this value makes.

4. Occurrence count was under-stated. #1450 is a third clean occurrence — a
   skills-doc-only diff red on a real-nested-run test in this file. #1429 is
   genuinely ambiguous (its diff touches run-tests.sh) and #1466 was a broken
   gate; both correctly excluded.

5. Dead code the previous commit propagated into new code: the `cwd` parameter
   no caller has ever passed, and an unused `import shutil`.

Mutation battery re-run on the widened predicate, control green (32 passed),
reading ONLY the `E ` lines:

  E  import subprocess as sp; sp.run(...)     KILLED  (SURVIVED before)
  F  Popen(...).communicate(), no bound       KILLED  (SURVIVED before)
  A  7th raw subprocess.run                   KILLED
  B  from subprocess import run as _r         KILLED
  C  bound respelled literal 120              KILLED  "timeout is Constant(value=120)"
  D  bound deleted entirely                   KILLED  "timeout is ABSENT"

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SCmdWjiRn4PtLGTJrgmiX1
Claude-Session-Id: 460124dc-bb3d-4afb-b9e5-0687bec64e44
ZacxDev added a commit that referenced this pull request Sep 11, 2026
…nnot select — the root cause, not the bound

Operator chose to fix the cause in this PR rather than file it.

THE CAUSE. `run-tests.sh` drops the hook-test and shell-test families on a
`--files` run, keyed on SCOPED_MODE — which only `--files` sets. A `--targets`
run therefore executed BOTH families in full, and no `--targets` value can name
either. Re-keyed on `SCOPE_STATE != FULL`, so any narrowed run drops them.

MEASURED, dev host, same box:

    --targets scripts/collector/i3/tests   47 s  (before, load ~52)
    --targets scripts/collector/i3/tests    9 s  (after,  load ~46)

Under `bash -x`, of ~54 s the SELECTED target's pytest was 2.9 s and ~46 s was
those families. That ~12x is what breached the nested-subprocess bound in
test_run_tests_targets.py under CI contention.

🔴 The skip is ANNOUNCED on the PARTIAL surface too. Widening the skip without
widening the announcement would be a run silently executing two families fewer
than its own SUMMARY claims — the #276 shape. Both moved together.

⚠ It does NOT bring the rest of SCOPED_MODE: a PARTIAL run still enforces every
per-target floor, GUARD 7 REQUIRED and GUARD 2 TOTAL, and still reports
SCOPE: PARTIAL. Asserted, because reusing SCOPED_MODE wholesale would have
passed the skip tests and silently suspended the floors.

Regression matrix for the new test: RED at origin/main's runner (AssertionError;
its output shows `PASS scripts/claude-hooks/... (script)` — the family running
inside a --targets run), GREEN at HEAD. The pre-existing
test_a_full_run_still_runs_the_hook_and_shell_families is the positive control
that a FULL run is unaffected.

ROUND-1 AUDIT FIXES:

1. The bound now lives in testlib/scoped_harness.py, which ALREADY had its own
   `timeout=600` default used by five test files. A constant private to
   test_run_tests_targets.py was a FOURTH copy while claiming "one rule, one
   place". Both read RUNNER_TIMEOUT_S now. Still unconsolidated and said so:
   test_run_tests_preconditions.py (300) and
   test_devshell_satisfies_required_tools.py (120 x2).
2. Value re-derived after the runner fix: 600 -> 300. 600 was sized against a
   47 s run that no longer exists.
3. The cost paragraph named the WRONG CAP and inverted the consequence. The
   binding cap is the gate TASK's `timeout: 60m`, below the pipeline's
   `timeouts.tasks: 70m`, so a slow test can never reach the latter. Per that
   pipeline's own measured probe: task-level timeout -> Failed, finally RAN ->
   a posted RED; timeouts.tasks -> finally NEVER RAN -> the unclearable pending
   the old comment wrongly warned about.
4. The guard passed VACUOUSLY with no spawn call in the file — mutant M11
   (delegate _spawn's body to a testlib helper) SURVIVED. Now asserts >= 1
   bounded spawn inside _spawn, killed by its own message.
5. `_SPAWNING_CALLABLES` claimed "every subprocess entry point that can start a
   child" and was not. Added getoutput/getstatusoutput (the sharp case: no
   timeout parameter exists, so unbounded by construction) and replaced the
   claim with an enumeration naming what is deliberately NOT covered.
6. "all three merged" was wrong: #1450 is still OPEN.

Mutation battery, control green, each killed by the assertion that claims it:
  M13 getoutput          KILLED   M9  Popen().communicate()  KILLED
  M14 getstatusoutput    KILLED   M11 vacuous pass           KILLED (was SURVIVED)
  M2  import as sp       KILLED

87 passed across test_run_tests_targets + the five scoped_harness consumers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SCmdWjiRn4PtLGTJrgmiX1
Claude-Session-Id: 460124dc-bb3d-4afb-b9e5-0687bec64e44
ZacxDev added a commit that referenced this pull request Sep 11, 2026
…he gate on diffs that could not reach it (#1493)

* fix(tests): one bound, one place — the 120s subprocess cap reddened the gate on diffs that could not reach it

`test_run_tests_targets.py` spawns nested `run-tests.sh` runs under a hard
subprocess timeout. That bound was 120s, open-coded at SIX call sites, and it
has been failing PRs whose diff cannot touch this file — devrc#1458 and #1462
both merged with `tekton/devrc-pytests` RED because of it.

MEASURED, not inferred:

- Mechanism reproduced first-hand by lowering the constant: `subprocess.
  TimeoutExpired`, returncode -9, traceback ending in `_check_timeout`. It is
  NOT an assertion failure, which is exactly why it sent readers to debug
  their own diff.
- One nested run of `scripts/collector/i3/tests` — the SMALLEST target, 13
  tests — took 47s on the dev host at load ~52, i.e. 39% of the old bound,
  and almost all of it is the runner's fixed preflight rather than the tests.
  CI is documented at 27-50 concurrent full-suite runs on one node.

So the bound was breached by CONTENTION, not by anything a diff did.

Three changes:

1. `_RUNNER_TIMEOUT_S = 600` — one named constant carrying the measurement and
   the reasoning. ~12x the measured figure, still bounding a genuine hang well
   inside the gate's budget. Deliberately not env-overridable.
2. `_spawn()` — the single place a nested run is bounded. All six sites route
   through it. It TRANSLATES `TimeoutExpired` into a failure that states the
   classification ("this is not an assertion failure and probably not your
   diff"), because a bare TimeoutExpired names a command and a number and
   tells the reader nothing about which is wrong.
3. `test_no_call_site_open_codes_its_own_subprocess_bound` — pins the
   RELATIONSHIP, parsed via AST rather than grepped.

Guard mutation-tested; control green (32 passed), each hazard killed by the
assertion that claims it, naming line and function:

  A  7th raw subprocess.run in _run()          KILLED  "without going through `_spawn`"
  B  aliased `from subprocess import run as _r` KILLED  same (a regex walks past this)
  C  _spawn bound respelled literal 120        KILLED  "timeout is Constant(value=120)"
  D  _spawn bound deleted entirely             KILLED  "timeout is ABSENT"

D matters most: a check that only inspected the `timeout=` kwargs it FOUND
would pass hardest on the unbounded hang this bound exists to prevent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SCmdWjiRn4PtLGTJrgmiX1
Claude-Session-Id: 460124dc-bb3d-4afb-b9e5-0687bec64e44

* fix(tests): round-0 fixes — the guard was narrower than its docstring, and the cost is not "preflight"

Round-0 audit findings, all fixed.

1. The AST predicate was NARROWER THAN ITS OWN DOCSTRING — the defect class it
   exists to catch. It resolved `from subprocess import run as r` but NOT
   `import subprocess as sp; sp.run(...)`, a sibling spelling of the same
   aliasing the docstring claims to close; and it matched only `run`, so
   `Popen(...).communicate()` with no bound was invisible while the assertion
   beside it called exactly that "worse than the red it replaces". Both were
   measured SURVIVING. Now an enumerated `_SPAWNING_CALLABLES` plus resolution
   of module aliases and from-imports.

2. The constant's own comment said the 47s is "almost all the runner's fixed
   preflight rather than the tests". FALSE, and it is the load-bearing half: a
   `--targets` run still executes the hook-test and shell-test families, which
   `--targets` does not narrow. Of ~54s under `bash -x`, the SELECTED target's
   pytest is 2.9s and ~46s is those families. Discriminator, same box, same
   load ~52, minutes apart: `--targets <dir>` = 47s vs `--files <one file>` = 4s,
   because `--files` sets SCOPED_MODE and run-tests.sh:4518 drops the families.
   The comment now says so, names the root cause, and says this bound is the
   SYMPTOM fix.

3. The worst-case budget was asserted, not measured. Recorded: four real nested
   runs, so 4x600s = 40 min against a measured timeouts.tasks of 1h10m — 57% of
   the task budget, and a run that hits it posts NOTHING (checks pending
   forever). Stated as the trade this value makes.

4. Occurrence count was under-stated. #1450 is a third clean occurrence — a
   skills-doc-only diff red on a real-nested-run test in this file. #1429 is
   genuinely ambiguous (its diff touches run-tests.sh) and #1466 was a broken
   gate; both correctly excluded.

5. Dead code the previous commit propagated into new code: the `cwd` parameter
   no caller has ever passed, and an unused `import shutil`.

Mutation battery re-run on the widened predicate, control green (32 passed),
reading ONLY the `E ` lines:

  E  import subprocess as sp; sp.run(...)     KILLED  (SURVIVED before)
  F  Popen(...).communicate(), no bound       KILLED  (SURVIVED before)
  A  7th raw subprocess.run                   KILLED
  B  from subprocess import run as _r         KILLED
  C  bound respelled literal 120              KILLED  "timeout is Constant(value=120)"
  D  bound deleted entirely                   KILLED  "timeout is ABSENT"

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SCmdWjiRn4PtLGTJrgmiX1
Claude-Session-Id: 460124dc-bb3d-4afb-b9e5-0687bec64e44

* fix(run-tests): a --targets run no longer pays for two families it cannot select — the root cause, not the bound

Operator chose to fix the cause in this PR rather than file it.

THE CAUSE. `run-tests.sh` drops the hook-test and shell-test families on a
`--files` run, keyed on SCOPED_MODE — which only `--files` sets. A `--targets`
run therefore executed BOTH families in full, and no `--targets` value can name
either. Re-keyed on `SCOPE_STATE != FULL`, so any narrowed run drops them.

MEASURED, dev host, same box:

    --targets scripts/collector/i3/tests   47 s  (before, load ~52)
    --targets scripts/collector/i3/tests    9 s  (after,  load ~46)

Under `bash -x`, of ~54 s the SELECTED target's pytest was 2.9 s and ~46 s was
those families. That ~12x is what breached the nested-subprocess bound in
test_run_tests_targets.py under CI contention.

🔴 The skip is ANNOUNCED on the PARTIAL surface too. Widening the skip without
widening the announcement would be a run silently executing two families fewer
than its own SUMMARY claims — the #276 shape. Both moved together.

⚠ It does NOT bring the rest of SCOPED_MODE: a PARTIAL run still enforces every
per-target floor, GUARD 7 REQUIRED and GUARD 2 TOTAL, and still reports
SCOPE: PARTIAL. Asserted, because reusing SCOPED_MODE wholesale would have
passed the skip tests and silently suspended the floors.

Regression matrix for the new test: RED at origin/main's runner (AssertionError;
its output shows `PASS scripts/claude-hooks/... (script)` — the family running
inside a --targets run), GREEN at HEAD. The pre-existing
test_a_full_run_still_runs_the_hook_and_shell_families is the positive control
that a FULL run is unaffected.

ROUND-1 AUDIT FIXES:

1. The bound now lives in testlib/scoped_harness.py, which ALREADY had its own
   `timeout=600` default used by five test files. A constant private to
   test_run_tests_targets.py was a FOURTH copy while claiming "one rule, one
   place". Both read RUNNER_TIMEOUT_S now. Still unconsolidated and said so:
   test_run_tests_preconditions.py (300) and
   test_devshell_satisfies_required_tools.py (120 x2).
2. Value re-derived after the runner fix: 600 -> 300. 600 was sized against a
   47 s run that no longer exists.
3. The cost paragraph named the WRONG CAP and inverted the consequence. The
   binding cap is the gate TASK's `timeout: 60m`, below the pipeline's
   `timeouts.tasks: 70m`, so a slow test can never reach the latter. Per that
   pipeline's own measured probe: task-level timeout -> Failed, finally RAN ->
   a posted RED; timeouts.tasks -> finally NEVER RAN -> the unclearable pending
   the old comment wrongly warned about.
4. The guard passed VACUOUSLY with no spawn call in the file — mutant M11
   (delegate _spawn's body to a testlib helper) SURVIVED. Now asserts >= 1
   bounded spawn inside _spawn, killed by its own message.
5. `_SPAWNING_CALLABLES` claimed "every subprocess entry point that can start a
   child" and was not. Added getoutput/getstatusoutput (the sharp case: no
   timeout parameter exists, so unbounded by construction) and replaced the
   claim with an enumeration naming what is deliberately NOT covered.
6. "all three merged" was wrong: #1450 is still OPEN.

Mutation battery, control green, each killed by the assertion that claims it:
  M13 getoutput          KILLED   M9  Popen().communicate()  KILLED
  M14 getstatusoutput    KILLED   M11 vacuous pass           KILLED (was SURVIVED)
  M2  import as sp       KILLED

87 passed across test_run_tests_targets + the five scoped_harness consumers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SCmdWjiRn4PtLGTJrgmiX1
Claude-Session-Id: 460124dc-bb3d-4afb-b9e5-0687bec64e44

* fix(tests): revert the bound to 600 — re-sizing it narrowed five files on the evidence of a sixth

Round-2 delta audit, one finding, and it is a defect the round-1 fix introduced.

Moving the constant into testlib/scoped_harness.py was right; changing its VALUE
in the same edit was not. `RUNNER_TIMEOUT_S` is the default for `run()`, which
test_scoped_runs.py, test_scoped_mapper.py, test_scoped_scope_marker.py,
test_scoped_gate_contract.py and test_scoped_ledgers.py use across ~47 call
sites. Setting it to 300 halved the bound for all five, justified entirely by a
9s measurement taken in the sixth.

Their runs are not 9s. MEASURED on the dev host: 28.9s at load ~57 for
test_the_node_runner_reports_FULL_on_a_REAL_run; the audit observed 70.5s for a
sibling under different load. At 300 that is ~4-10x headroom, against the >2.55x
contention inflation that caused this PR — i.e. the narrowing would have
re-created here the exact failure class the runner fix removes elsewhere: a red
on tekton/devrc-pytests naming a test the diff cannot reach.

So 600 stands, unchanged from what this module already had. What the PR removes
is the six open-coded `timeout=120` copies, not the bound's value. The rejected
300 and the measurements that rejected it are recorded beside the constant so
nobody re-derives it from the 9s figure.

The worst-case-budget argument that motivated 300 is kept as information and
corrected: 4x600s = 40 min requires all four nested runs to HANG — after the
runner fix each measures 2-9s — and a genuine hang is a defect you want
surfaced, not absorbed.

Also from round 2's non-findings:
- `bounded_in_spawn` counted spawn calls sited in `_spawn`, not ones checked to
  carry a bound, while its message said "NO bounded spawn". Renamed to
  `spawns_in_spawn` and the message narrowed to what the expression measures.
- "a LEGIBLE RED" overstated it: the posted status is `error` carrying
  `COULD NOT RUN`, which CLAUDE.md tells readers is a broken gate rather than a
  bad change. The substantive claim (posts something vs pending forever) stands.
- Cited line numbers corrected: test_devshell_satisfies_required_tools.py is
  110,438 not 105,436. Re-verified all three cited sites by grep.

Mutation battery re-run, control green, M11 now killed by the renamed
assertion's own message. 105 passed across test_run_tests_targets, the five
scoped_harness consumers and test_run_tests_preconditions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SCmdWjiRn4PtLGTJrgmiX1
Claude-Session-Id: 460124dc-bb3d-4afb-b9e5-0687bec64e44

* docs(tests): four false claims the last two fix rounds wrote about themselves

Round-3 delta audit. Every finding is in prose THIS ladder's own fix commits
added — the recorded failure mode, and none of it is a code defect.

A. "What this PR removes is the six open-coded timeout=120 copies, NOT the
   bound's value" — false for the scope that matters. Those six sites go
   120 -> 600, a 5x widening, and that widening is the PR's headline fix. What is
   unchanged is the SHARED CONSTANT, for the five files already reading it. As
   written it would tell a maintainer no timeout was raised anywhere. Both
   copies of the sentence now name the scope.

B. The comment on `spawns_in_spawn` said the `unbounded` assertion "above …
   has already run". It is BELOW — line 355 against the comment at 337 and the
   assertion at 348. The comment's whole argument was "this needn't check
   boundedness, that is already proven", which would have justified deleting the
   check it leaned on. Corrected, and the two are now stated as independent: one
   refuses an EMPTY set, the other a BOUNDLESS call.

C. "an `error` carrying COULD NOT RUN" — wrong string. Traced the reporter's
   branch selection in devrc-ci-pipeline.yaml: the COULD NOT RUN arm is guarded
   by BUILD_STATUS = "Succeeded", which a timed-out task does not satisfy; a
   SIGKILLed step leaves no steprc and lands on `KILLED: <leg> — the gate pod
   died at or after step <phase> (preempted/evicted/OOM/timeout)`. The pipeline
   states it itself: "A task-level timeout: expiring while a step is EXECUTING
   also SIGKILLs it, so it reads as KILLED". The conclusion (error, not a code
   failure, posts something) survives; the citation did not.

D. A four-line HANG BOUND paragraph was duplicated verbatim inside the docstring
   whose thesis is "one rule, one place". Deduplicated.

E. The revert deleted the one sentence quantifying a cost of 600 while keeping
   every sentence quantifying a cost of 300 — sweeping the arguments against the
   rejected value harder than against the adopted one. 4x600s = 40 min against a
   ~37 min post-queue budget is now stated plainly as the strongest argument
   AGAINST 600, with why it is accepted anyway.

Also: "~47 call sites" re-counted by AST — 45, of which 44 take the default.

Verified rather than trusted: 600 is the module's pre-existing default at
b315cdd; the six 120s sites; the five importing files; 110/438 and
preconditions:68; the gate task's 60m vs timeouts.tasks 70m; the three-way probe
table; no stale 300 claims to be live anywhere.

Guard battery re-run, control green, all five mutants killed by their own
messages. 105 passed across the seven affected files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SCmdWjiRn4PtLGTJrgmiX1
Claude-Session-Id: 460124dc-bb3d-4afb-b9e5-0687bec64e44

* docs(tests): the last round's own two corrections — and the ladder STOPS HERE

Round-4 delta audit. Two findings, both prose, both defects the round-3 fix
introduced while fixing round 2's.

1. Round 3 replaced one false claim with its OPPOSITE. It had said the PR
   changes "WHERE the bound is written, not WHAT it is" (false for the six
   consolidated sites); the replacement called the 120 -> 600 rise "the PR's
   headline fix" / "the whole point of the change" — which oversells 600 as a
   measured value and is contradicted twenty lines below by "120 WAS NEVER THE
   REAL DEFECT EITHER", and by commit 409b1d3's own subject, "the root cause,
   not the bound". Both copies now say the rise is a CONSEQUENCE of the
   consolidation, accepted; the headline fix is the runner scoping; 600 remains
   an unpinned hang bound. The two dead drafts are recorded so a third is not
   derived.

2. Round 3's fix for finding E deleted the 70.5s observation — the single worst
   recorded, i.e. the one most hostile to 300 — while adding a smaller new one,
   so the stated headroom improved from ~4-10x to ~10-18x with no note that
   evidence had been removed. That is the selective-sweep mechanism finding E
   was about, performed inside the commit fixing E. The 70.5s is restored and
   LABELLED (second-hand, load unrecorded) rather than deleted, with the range
   stated honestly as ~4x to ~18x depending which observation you take.

🔴 THE LADDER STOPS HERE, ON THE ATTRIBUTION GATE, NOT ON A CLEAN ROUND.
Measured, not asserted: round 3's fix (beb227e) and this one both change
100% comment lines — every added/removed line matches `^[+-]\s*#`. Two
consecutive rounds whose fixes changed ZERO payload lines means the ladder has
left the PR: rounds 2, 3 and 4 each found only the previous round's own
sentences, while the executable payload has been untouched since round 1.

The code was settled four rounds ago: the runner fix is 14 non-comment lines in
run-tests.sh and 4 in scoped_harness.py, green in both sandbox tiers on the
merged tree, with the guard mutation-killable by two independent mutants each
dying on its own message.

Nothing is filed as follow-up — round 4's two findings are fixed here, and it
reported no others.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SCmdWjiRn4PtLGTJrgmiX1
Claude-Session-Id: 460124dc-bb3d-4afb-b9e5-0687bec64e44

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
ZacxDev pushed a commit that referenced this pull request Sep 12, 2026
… --help and the env seams (D1-D5)

The six deletion candidates were assessed against the BROKEN tool, so each was
re-measured after F2 rather than acted on. Decisions, with the evidence:

DELETE `--fetch`. It was the only git subcommand in this file that WRITES, in a
tool whose stated safety property is that it writes nothing, and `refs/` lives
in the COMMON git dir — a worktree gives zero isolation there, so "namespaced
under refs/stale-base-triage/*" was a weaker claim than the one the header
makes. MEASURED: this repo's PRs are same-repo branches and `origin`'s refspec
is `+refs/heads/*:refs/remotes/origin/*`, so an ordinary `git fetch origin`
already brings every head — 58 of 58 open PR heads resolved in the base clone
with no `--fetch` anywhere. It bought nothing and cost the tool its absolute
safety claim. The UNMEASURED message now says `git fetch origin`.

The guard got STRONGER, not weaker: `test_only_refs_under_its_own_namespace_are_
ever_written` (a relative claim) is replaced by `test_no_git_subcommand_that_
WRITES_is_ever_invoked`, which enumerates the read-only subcommands as an
ALLOWLIST off the AST — so a subcommand nobody enumerated is a write by default,
and `remote` is admitted only in its `get-url` form.

DELETE `--json`. No consumer: a whole-repo search for `stale-base-triage` /
`STALE_BASE_TRIAGE` returns this script and its test file, nothing else — no
unit, no skill, no nix, no caller. F2 does not change that; it makes the
RENDERED sweep useful, not a machine payload. It cost a module-level
`_JSON_MODE`, a `say()` indirection on every human line in the file, and a
second exit path computing `n_inherited` its own way.

KEEP `--sweep`, and it is now the primary mode. Its rationale is exactly what F2
reversed: MEASURED read-only over 58 open PRs, the sweep surfaces 7 provably
complete reds where it surfaced 4, and the three it gained (#1518, #1515, #1450)
are 9-94 commits behind and actively worked — not the 259/357/508-behind
abandoned ones the old gate happened to let through.

KEEP the self-parsing `--help`. What it prints is the `#` header — the env
ledger, the exit codes, the measured rationale — which argparse cannot see. The
alternative is copying the ledger into an epilog, i.e. reintroducing the
duplication the previous commit just removed, and the ledger is two-way pinned
against the code that reads it.

KEEP all four env seams and the ledger test. `STALE_BASE_TRIAGE_GH` and
`_REPO` are the seams the test harness points at a stub — they are why no test
in this file can reach real GitHub. `_BUDGET` is the only way to drive the
budget guard to zero. `_COMMENT_MODE` is the arming seam and FAILS CLOSED
across seven spellings. Each has a concrete in-repo consumer; none is
speculative.

`COMMENT_MODE_DEFAULT` is untouched: still the literal `"off"`, still pinned
both ways (value and source text). Nothing was posted to GitHub; every live run
in this work was read-only with mode=off.

VERIFIED AGAINST THE ORIGINAL SYMPTOM, on live data, not inferred from the code:
  old code (5ad08e5), `--pr 1518`:  VERDICT: COULD NOT MEASURE,  rc 0
  this tree,           `--pr 1518`:  VERDICT: INHERITED,          rc 10
and a four-PR read-only sweep returns INHERITED for #1518/#1515/#1450 with the
fix commits named, while #1177 (derived 39 against one name) stays COULD NOT
MEASURE with the HINT line.

⚠ The deleted-flag guard was SPELLED on its first draft and failed on the header
paragraph that explains the deletion — the same trap `_git_subcommands` is
AST-based to avoid. It now reads `add_argument` literals, module-level
assignments and string constants off the AST, so prose can neither satisfy nor
break it.

Suite: 71 tests, all green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQx16RMr8YQYBfsXaBEsSm
Claude-Session-Id: a7f5b63b-6de5-4fdc-9814-c8a8fc7b2ba0
ZacxDev pushed a commit that referenced this pull request Sep 12, 2026
… --help and the env seams (D1-D5)

The six deletion candidates were assessed against the BROKEN tool, so each was
re-measured after F2 rather than acted on. Decisions, with the evidence:

DELETE `--fetch`. It was the only git subcommand in this file that WRITES, in a
tool whose stated safety property is that it writes nothing, and `refs/` lives
in the COMMON git dir — a worktree gives zero isolation there, so "namespaced
under refs/stale-base-triage/*" was a weaker claim than the one the header
makes. MEASURED: this repo's PRs are same-repo branches and `origin`'s refspec
is `+refs/heads/*:refs/remotes/origin/*`, so an ordinary `git fetch origin`
already brings every head — 58 of 58 open PR heads resolved in the base clone
with no `--fetch` anywhere. It bought nothing and cost the tool its absolute
safety claim. The UNMEASURED message now says `git fetch origin`.

The guard got STRONGER, not weaker: `test_only_refs_under_its_own_namespace_are_
ever_written` (a relative claim) is replaced by `test_no_git_subcommand_that_
WRITES_is_ever_invoked`, which enumerates the read-only subcommands as an
ALLOWLIST off the AST — so a subcommand nobody enumerated is a write by default,
and `remote` is admitted only in its `get-url` form.

DELETE `--json`. No consumer: a whole-repo search for `stale-base-triage` /
`STALE_BASE_TRIAGE` returns this script and its test file, nothing else — no
unit, no skill, no nix, no caller. F2 does not change that; it makes the
RENDERED sweep useful, not a machine payload. It cost a module-level
`_JSON_MODE`, a `say()` indirection on every human line in the file, and a
second exit path computing `n_inherited` its own way.

KEEP `--sweep`, and it is now the primary mode. Its rationale is exactly what F2
reversed: MEASURED read-only over 58 open PRs, the sweep surfaces 7 provably
complete reds where it surfaced 4, and the three it gained (#1518, #1515, #1450)
are 9-94 commits behind and actively worked — not the 259/357/508-behind
abandoned ones the old gate happened to let through.

KEEP the self-parsing `--help`. What it prints is the `#` header — the env
ledger, the exit codes, the measured rationale — which argparse cannot see. The
alternative is copying the ledger into an epilog, i.e. reintroducing the
duplication the previous commit just removed, and the ledger is two-way pinned
against the code that reads it.

KEEP all four env seams and the ledger test. `STALE_BASE_TRIAGE_GH` and
`_REPO` are the seams the test harness points at a stub — they are why no test
in this file can reach real GitHub. `_BUDGET` is the only way to drive the
budget guard to zero. `_COMMENT_MODE` is the arming seam and FAILS CLOSED
across seven spellings. Each has a concrete in-repo consumer; none is
speculative.

`COMMENT_MODE_DEFAULT` is untouched: still the literal `"off"`, still pinned
both ways (value and source text). Nothing was posted to GitHub; every live run
in this work was read-only with mode=off.

VERIFIED AGAINST THE ORIGINAL SYMPTOM, on live data, not inferred from the code:
  old code (5ad08e5), `--pr 1518`:  VERDICT: COULD NOT MEASURE,  rc 0
  this tree,           `--pr 1518`:  VERDICT: INHERITED,          rc 10
and a four-PR read-only sweep returns INHERITED for #1518/#1515/#1450 with the
fix commits named, while #1177 (derived 39 against one name) stays COULD NOT
MEASURE with the HINT line.

⚠ The deleted-flag guard was SPELLED on its first draft and failed on the header
paragraph that explains the deletion — the same trap `_git_subcommands` is
AST-based to avoid. It now reads `add_argument` literals, module-level
assignments and string constants off the AST, so prose can neither satisfy nor
break it.

Suite: 71 tests, all green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQx16RMr8YQYBfsXaBEsSm
Claude-Session-Id: a7f5b63b-6de5-4fdc-9814-c8a8fc7b2ba0
@ZacxDev

ZacxDev commented Sep 12, 2026

Copy link
Copy Markdown
Member Author

Independent confirmation, 3 days later and larger — and the drift has grown.

Hit this while following the same step, without having seen this PR (my duplicate-sweep ran too late; closing #1580 in favour of this one).

Measured 2026-09-12, homelab-talos scope:

mirror pod
bullets in the entry 11 29
newest bullet 2026-08-31 2026-09-11
entry files in scope 11 36

So 12 days and 18 bullets of drift, against the 7 days / 8 lines measured here on 09-09.

Two additions to what this PR already documents:

  1. NEAR-MISS is affected too, not just OPEN: and already there. The probe flagged a marker as non-parsing; the live copy already reads RESOLVED <sha>: (also …) and parses fine. So all three displays rendered from the mirror are suspect, and the fix-the-LINE instruction on a NEAR-MISS row can send you to rewrite prose that is already correct.
  2. Both flagged items were already fixed upstream in my run — the NEAR-MISS above, and an OPEN: bullet the pod carried as RESOLVED 841d6fc4: plus a later bullet explicitly closing its open half. I verified that one on its own terms before believing the mirror was wrong (PR merged, and the deployed Pipeline/TriggerTemplate read clawgate-ci 40m0s, envelope tasks: 45m / pipeline: 60m), which is what made the staleness unambiguous rather than a guess.

Worth noting the failure mode this closes is self-inflicted by the protocol: the already there comparison exists to stop a writer re-recording a lesson the entry already carries, and a stale input makes it do the opposite.

🤖 Generated with Claude Code

ZacxDev pushed a commit that referenced this pull request Sep 12, 2026
… --help and the env seams (D1-D5)

The six deletion candidates were assessed against the BROKEN tool, so each was
re-measured after F2 rather than acted on. Decisions, with the evidence:

DELETE `--fetch`. It was the only git subcommand in this file that WRITES, in a
tool whose stated safety property is that it writes nothing, and `refs/` lives
in the COMMON git dir — a worktree gives zero isolation there, so "namespaced
under refs/stale-base-triage/*" was a weaker claim than the one the header
makes. MEASURED: this repo's PRs are same-repo branches and `origin`'s refspec
is `+refs/heads/*:refs/remotes/origin/*`, so an ordinary `git fetch origin`
already brings every head — 58 of 58 open PR heads resolved in the base clone
with no `--fetch` anywhere. It bought nothing and cost the tool its absolute
safety claim. The UNMEASURED message now says `git fetch origin`.

The guard got STRONGER, not weaker: `test_only_refs_under_its_own_namespace_are_
ever_written` (a relative claim) is replaced by `test_no_git_subcommand_that_
WRITES_is_ever_invoked`, which enumerates the read-only subcommands as an
ALLOWLIST off the AST — so a subcommand nobody enumerated is a write by default,
and `remote` is admitted only in its `get-url` form.

DELETE `--json`. No consumer: a whole-repo search for `stale-base-triage` /
`STALE_BASE_TRIAGE` returns this script and its test file, nothing else — no
unit, no skill, no nix, no caller. F2 does not change that; it makes the
RENDERED sweep useful, not a machine payload. It cost a module-level
`_JSON_MODE`, a `say()` indirection on every human line in the file, and a
second exit path computing `n_inherited` its own way.

KEEP `--sweep`, and it is now the primary mode. Its rationale is exactly what F2
reversed: MEASURED read-only over 58 open PRs, the sweep surfaces 7 provably
complete reds where it surfaced 4, and the three it gained (#1518, #1515, #1450)
are 9-94 commits behind and actively worked — not the 259/357/508-behind
abandoned ones the old gate happened to let through.

KEEP the self-parsing `--help`. What it prints is the `#` header — the env
ledger, the exit codes, the measured rationale — which argparse cannot see. The
alternative is copying the ledger into an epilog, i.e. reintroducing the
duplication the previous commit just removed, and the ledger is two-way pinned
against the code that reads it.

KEEP all four env seams and the ledger test. `STALE_BASE_TRIAGE_GH` and
`_REPO` are the seams the test harness points at a stub — they are why no test
in this file can reach real GitHub. `_BUDGET` is the only way to drive the
budget guard to zero. `_COMMENT_MODE` is the arming seam and FAILS CLOSED
across seven spellings. Each has a concrete in-repo consumer; none is
speculative.

`COMMENT_MODE_DEFAULT` is untouched: still the literal `"off"`, still pinned
both ways (value and source text). Nothing was posted to GitHub; every live run
in this work was read-only with mode=off.

VERIFIED AGAINST THE ORIGINAL SYMPTOM, on live data, not inferred from the code:
  old code (5ad08e5), `--pr 1518`:  VERDICT: COULD NOT MEASURE,  rc 0
  this tree,           `--pr 1518`:  VERDICT: INHERITED,          rc 10
and a four-PR read-only sweep returns INHERITED for #1518/#1515/#1450 with the
fix commits named, while #1177 (derived 39 against one name) stays COULD NOT
MEASURE with the HINT line.

⚠ The deleted-flag guard was SPELLED on its first draft and failed on the header
paragraph that explains the deletion — the same trap `_git_subcommands` is
AST-based to avoid. It now reads `add_argument` literals, module-level
assignments and string constants off the AST, so prose can neither satisfy nor
break it.

Suite: 71 tests, all green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQx16RMr8YQYBfsXaBEsSm
Claude-Session-Id: a7f5b63b-6de5-4fdc-9814-c8a8fc7b2ba0
ZacxDev added a commit that referenced this pull request Sep 12, 2026
…red (#1524)

* feat(ci-signal): name the commit that already fixed a PR's inherited red

MEASURED, and it is the reason this exists: of the SIX genuine
`tekton/devrc-pytests` failures in the whole post-#1458 window, THREE — half —
were the same test on PRs 17, 40 and 40 commits behind `main`, all since merged,
and that test passes on current `main`. An earlier sample had 8 of 8 failing
open PRs 5-42 commits behind, with a rebase curing 4 outright. Nothing blocks a
merge here, so the only value the gate has is whether a human believes a red —
and half the genuine reds are already-fixed failures a human then debugs against
a diff that cannot reach them.

`scripts/stale-base-triage.py` answers that deterministically, running no tests:
it reads the NEWEST `tekton/devrc-pytests` status per context, maps each failing
test named in the description to the ONE file defining it, and emits the evidence
pair — the PR head's copy of that file is byte-identical to the merge-base's (so
this change never touched it) while `main` HAS moved it, in commits absent from
the head. Verdict per PR: INHERITED / NOT EXPLAINED BY STALENESS / COULD NOT
MEASURE.

Measured live, read-only, over all 51 open PRs: 20 red heads, 3 INHERITED
(#1286, #1194, #1038), 2 NOT EXPLAINED, 15 COULD NOT MEASURE, 4 broken-gate
`error` rows counted on their own line. That non-zero 3 is the positive control
for the count: a zero here would otherwise be indistinguishable from an
instrument wired to nothing.

🔴 REPORT ONLY BY DEFAULT. `COMMENT_MODE_DEFAULT` is the literal `"off"`, pinned
by `test_the_comment_mode_default_is_the_LITERAL_off` both as a value and as
source text, so arming the PR-comment writer costs a visible line in the arming
commit. Nothing was posted to GitHub at any point.

Traps this repo has already paid for, each handled and each pinned by a test:
* `/commits/{sha}/status` (SINGULAR) maps `error` onto `failure` — never read.
  Only the plural list endpoint, which carries no roll-up field at all.
* the list is NEWEST-FIRST — folded on `max(created_at)`, order-independent.
* `error` is a broken gate, not a code failure — its own summary line, never in
  the red total.
* a SQUASH merge never makes a head an ancestor of its base; ancestry is asked
  only about the PR head, and every "this landed" claim is made by blob OID.
* `git diff --quiet <ref> -- <path>` exits 0 when the path exists on NEITHER
  side; existence is proved with `git cat-file -e` first, pinned by an AST scan
  of the git invocations rather than a string search that its own comment would
  satisfy.
* the 140-char description cap means a description can prove "at least one test
  failed and here is its name" but never "these are all of them" — so an
  INHERITED verdict requires `failed=N` to have survived AND to equal the number
  of names. That is conservative on purpose: dismissing a real red is the one
  error this tool must not make.
* the test-name -> file mapping is searched, never guessed, with a distinct
  outcome for not-found, ambiguous, class-qualified, parametrised and
  cap-truncated names.

Testing. 56 tests; RED at base `60194765` (the module cannot collect — the
script does not exist there), GREEN at HEAD. Three enumerated mutation rounds
under PYTHONDONTWRITEBYTECODE=1, mutants derived from the AST plus targeted
textual ones: round 1 74 mutants / 15 survivors AND THE POSITIVE CONTROL
SURVIVED, which is what found that `CONTEXT` was pinned nowhere; round 2 82 / 3
with the control killed; round 3 81 mutants, 78 killed, 0 survived, control
killed. Round 1's survivors also found a real defect — the `on_main` half of the
evidence pair was a guard that can never run (`commits_touching` already walks
`merge-base..main`), so it is now measured and printed for verification rather
than gated on, and `not in_head` moved into `prove_candidates` where a test can
reach it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQx16RMr8YQYBfsXaBEsSm
Claude-Session-Id: a7f5b63b-6de5-4fdc-9814-c8a8fc7b2ba0

* fix(stale-base-triage): the verdict was a function of the failing test's NAME LENGTH (F2)

`names_provably_complete` could only prove completeness by reading `failed=N`
out of the status description. `failed=` is the LAST field in the gate's banner
and GitHub caps a description at 140 BYTES, so whether it survives is decided by
how long the failing test's name is — and on the three PRs this tool's
requirement was measured on (#1454, #1462, #1499) it did not survive. All three
resolved to COULD NOT MEASURE with the correct answer already in the row. The
tool fired on 0 of the 3 cases that justified it.

The fix is a SECOND route, and it is a proof rather than a heuristic.
`scripts/run-tests.sh` GUARD 4 defines

    collected = passed + skipped + failed + errors + xfailed + xpassed
    TOT_FAILED += failed + errors

so `collected - passed - skipped == failed + xfailed + xpassed >= failed`, and
names are a subset of the failures, so `len(names) <= failed <= derived`. When
`derived == len(names)` the inequality is squeezed shut and completeness is
PROVEN. `collected=`, `passed=` and `skipped=` all precede `failed=`, so they
are exactly the fields that survive the cut. The direct `failed=N` route is kept
and still tried first, because it is the stronger claim.

🔴 THE BOUND IS AN OVER-COUNT, NEVER AN UNDER-COUNT, and that direction is the
whole safety argument: an under-count would certify a completeness the row does
not have and dismiss a real red. `xfailed`/`xpassed` can only inflate it, and
`_TOTALS_RE` demands the three fields ADJACENT AND IN ORDER so that a number the
cap cut SHORT cannot shrink it — `collected=` and `passed=` are each proven
intact by having matched banner text after them, and `skipped=`, the only one
that can be short, subtracts LESS and so errs toward withholding.

MEASURED, read-only, 2026-09-11 (provably-complete rows / red heads):

  population                                   old      new
  100 most-recent closed PRs                   5/28    18/28
  - the three justifying PRs #1454 #1462 #1499  0/3      3/3
  58 open PRs                                  4/26     7/26
  - #1518, #1515 (9 commits behind, worked)     0/2      2/2

SOUNDNESS CONTROL on the same real rows: 17 carried BOTH a visible `failed=N`
and a derivable bound; the two agreed EXACTLY on all 17, with zero under-counts.
Genuine multi-failure reds stay withheld — #1177 derives 39 against one name,
#1280 failed=5, #1440 failed=3, all still COULD NOT MEASURE.

The coupling to `run-tests.sh` is pinned mechanically rather than asserted:
`test_the_run_tests_collected_arithmetic_this_derivation_rests_on_is_pinned`
reads the `collected=$((…))` and `TOT_FAILED=$((…))` expressions out of the
shell source and checks the CONTAINMENT relation, so renaming the shell locals
is fine and changing WHAT IS SUMMED is not. What it cannot check — that
`xfailed`/`xpassed` are non-negative — is stated in the test, not asserted.

Prior art is now cited in the header: `main-status-watch.py`'s
`screen_all_known_flakes` is the same completeness gate in 15 lines, and its
"would have fired ZERO times on 100 commits" is the same `failed=N` route being
eaten by the same cap. This file's contribution is the derived route and the
blob/commit evidence half; the gate itself is prior art and says so.

Red/green matrix: 13 of the 14 new tests fail at 5ad08e5 and pass at HEAD; the
14th is labelled in its own docstring as an invariant guard, not regression
coverage. Suite: 56 -> 70 tests, all green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQx16RMr8YQYBfsXaBEsSm
Claude-Session-Id: a7f5b63b-6de5-4fdc-9814-c8a8fc7b2ba0

* test(stale-base-triage): close the three gaps the F2 mutation sweep found

Round 1 of the sweep on the new completeness logic killed 10 of 15. Four of the
five survivors were real gaps in the tests, not in the code:

* M14 — reordering `passed=`/`skipped=` in `run-tests.sh`'s TOTAL banner
  SURVIVED, because the pin searched the whole FILE and `run-tests.sh` prints
  TWO banners (a full-run one and a SCOPED one). Mutating one was satisfied by
  the other. The pin now requires EVERY banner line to carry the ordered
  fields, and asserts it found at least two.
* M11 — spelling the `skipped` group `(\d*)` SURVIVED: no fixture fed a row cut
  exactly on the `=`, which is what a cap landing one character early produces
  and which makes `int("")` RAISE on the reporting path. Added as a fixture.
* M10 — replacing the underivable-row refusal with a bound of 0 SURVIVED,
  because `names` is non-empty by then so 0 can never equal it and the VERDICT
  is identical. Only the operator-facing reason distinguishes them, and the two
  refusals call for different next steps, so both reasons are now pinned as
  whole strings.
* M5b — the naive three-separate-searches spelling of the totals regex is now
  driven explicitly and is killed by the existing out-of-order fixture.

Two survivors are argued EQUIVALENT rather than fixed, and are labelled as such
in the source:

* M5 — matching the three fields with `.*` between them instead of adjacently.
  `.*` still requires the ORDER, and a single-banner description contains
  exactly one of each field, so no input this parser can receive distinguishes
  them. The adjacency is kept for the truncation argument it encodes.
* M13 — `run-tests.sh` SHRINKING what it sums into `failed=` keeps the derived
  value an UPPER bound, so the derivation stays sound. The pin checks
  CONTAINMENT on purpose; the added comment says so, and M15 (a term LEAVING
  `collected` — the change that does break it) is killed by the same pin.

Round 2 over the fixed tree: 6/8 killed, the two above surviving as argued, no
new finding — so the ladder ends there. Both controls held in both rounds
(unmutated tree green; `COMMENT_MODE_DEFAULT = "on"` killed by four tests).
Run under PYTHONDONTWRITEBYTECODE=1, each edit verified to have changed the
file, verdicts read from the runner's own result lines.

Suite: 70 -> 71 tests, all green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQx16RMr8YQYBfsXaBEsSm
Claude-Session-Id: a7f5b63b-6de5-4fdc-9814-c8a8fc7b2ba0

* refactor(ci-signal): one rule, one place — and the two copies had already diverged (D6)

`classify` in `stale-base-triage.py` was BYTE-IDENTICAL to the one in
`main-status-watch.py`. `_FAILING_RE`, `_FAILED_COUNT_RE`, `parse_failing_names`
and `parse_failed_count` were duplicated AND already divergent — only the newer
copy stripped the `TOTAL` truncation fragment, and only the newer copy folded
statuses by timestamp instead of by array position. The disagreement is the
finding; consolidating is what made it audible.

All five now live in `scripts/lib/ci_status.py`, with
`derived_failure_upper_bound` beside them. Both files import it by path. What
stays in each consumer is POLICY — which context may speak, which names are
known flakes, what completeness means, what to do about a red — because the two
answer those differently.

🔴 `main-status-watch.py` IS LIVE ON BOTH HOSTS, every 10 minutes, so each
divergence was reconciled deliberately rather than by taking whichever copy was
newer:

* THE FOLD. The live copy kept the FIRST row per context, which is correct only
  because GitHub returns the array newest-first — an assumption about someone
  else's response ordering, load-bearing, unstated and untested. (⚠ It is NOT
  the "yields the OLDEST post" bug: first-wins over a newest-first array yields
  the NEWEST. The defect is order-DEPENDENCE, not a wrong row today.) It now
  folds on `max(created_at)`, which agrees with first-wins on GitHub's current
  ordering and stays right if that changes. With no `created_at` anywhere every
  stamp compares equal and the FIRST row wins — the old rule, as the fallback.
  `test_newest_per_context_is_ORDER_INDEPENDENT_which_first_wins_was_not`
  reimplements the old rule and shows it returning the `pending` row on the
  order it does not expect, beside the new one right on both.
* THE CONTEXT FILTER moved out of the fold and into `commit_verdict`, where the
  policy belongs, and BEFORE the emptiness check — a commit carrying only a
  foreign pipeline's rows must have NO verdict, never green, or an unrelated
  green would close an open red episode. Pinned by a new unit test beside the
  existing end-to-end one.
* THE isinstance SKIP. The live copy deliberately had none, arguing the arm was
  unreachable (its walk validates every row is a dict and names the commit if
  not) and that silently dropping a row could drop the red. The shared function
  keeps the triage tool's skip: still unreachable for the watcher, unchanged
  behaviour for the triage tool. Neither contract moves; the module says why.
* THE FRAGMENT STRIP is now shared, and is provably INERT for the watcher: a row
  cut at `| TOTA` was necessarily cut before `failed=N` too, so its screen —
  which also demands the count — refuses either way.
* THE DERIVED COMPLETENESS ROUTE is deliberately NOT adopted by the watcher, and
  the file now says why: there, proving completeness makes the tool SPEAK; here
  it makes it stay SILENT about a red. Widening a silence over main's only
  automated detector is a change to make on purpose with its own measurement,
  not a free win inherited from a sibling.

Duplication is now refused mechanically:
`test_the_shared_predicates_are_NOT_re_declared_in_either_consumer` fails on a
local `def` of any of the five, in either file, and carries a positive control
so the scan cannot pass vacuously.

TWO PRE-EXISTING TESTS BROKE, both for the right reason, and both were fixed
rather than weakened: one used `def classify(...)` as its positive control for
the docstring-stripper (the function is simply elsewhere now — it uses
`commit_verdict` instead), and one copies the script to a tmp dir and runs it,
which has no sibling `lib/` (it now passes PYTHONPATH, and says why).

DEPLOYMENT. The unit runs the script straight out of the checkout
(`ExecStart=… %h/workspace/devrc/scripts/main-status-watch.py`), so a `git pull`
delivers both files and no home-manager switch is involved. A run landing in the
instant between the two files arriving fails to import; the unit is a
timer-driven oneshot with no `OnFailure=` toast and the next poll is 10 minutes
away, so that window costs one skipped poll. ⚠ `X-Restart-Triggers` in
`nix/home.nix` still names only `main-status-watch.py` and was deliberately NOT
edited — for a timer-driven oneshot that re-reads the file every run it is
cosmetic, and touching `home.nix` here would widen this change's blast radius
for no behavioural gain.

`test_main_status_watch.py`: 96 -> 99 tests, all green.
`test_stale_base_triage.py`: 71 tests, all green. Both suites: 170 green.
No live-system change was made: the deployed copy is the base clone on `main`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQx16RMr8YQYBfsXaBEsSm
Claude-Session-Id: a7f5b63b-6de5-4fdc-9814-c8a8fc7b2ba0

* refactor(stale-base-triage): delete --fetch and --json; keep --sweep, --help and the env seams (D1-D5)

The six deletion candidates were assessed against the BROKEN tool, so each was
re-measured after F2 rather than acted on. Decisions, with the evidence:

DELETE `--fetch`. It was the only git subcommand in this file that WRITES, in a
tool whose stated safety property is that it writes nothing, and `refs/` lives
in the COMMON git dir — a worktree gives zero isolation there, so "namespaced
under refs/stale-base-triage/*" was a weaker claim than the one the header
makes. MEASURED: this repo's PRs are same-repo branches and `origin`'s refspec
is `+refs/heads/*:refs/remotes/origin/*`, so an ordinary `git fetch origin`
already brings every head — 58 of 58 open PR heads resolved in the base clone
with no `--fetch` anywhere. It bought nothing and cost the tool its absolute
safety claim. The UNMEASURED message now says `git fetch origin`.

The guard got STRONGER, not weaker: `test_only_refs_under_its_own_namespace_are_
ever_written` (a relative claim) is replaced by `test_no_git_subcommand_that_
WRITES_is_ever_invoked`, which enumerates the read-only subcommands as an
ALLOWLIST off the AST — so a subcommand nobody enumerated is a write by default,
and `remote` is admitted only in its `get-url` form.

DELETE `--json`. No consumer: a whole-repo search for `stale-base-triage` /
`STALE_BASE_TRIAGE` returns this script and its test file, nothing else — no
unit, no skill, no nix, no caller. F2 does not change that; it makes the
RENDERED sweep useful, not a machine payload. It cost a module-level
`_JSON_MODE`, a `say()` indirection on every human line in the file, and a
second exit path computing `n_inherited` its own way.

KEEP `--sweep`, and it is now the primary mode. Its rationale is exactly what F2
reversed: MEASURED read-only over 58 open PRs, the sweep surfaces 7 provably
complete reds where it surfaced 4, and the three it gained (#1518, #1515, #1450)
are 9-94 commits behind and actively worked — not the 259/357/508-behind
abandoned ones the old gate happened to let through.

KEEP the self-parsing `--help`. What it prints is the `#` header — the env
ledger, the exit codes, the measured rationale — which argparse cannot see. The
alternative is copying the ledger into an epilog, i.e. reintroducing the
duplication the previous commit just removed, and the ledger is two-way pinned
against the code that reads it.

KEEP all four env seams and the ledger test. `STALE_BASE_TRIAGE_GH` and
`_REPO` are the seams the test harness points at a stub — they are why no test
in this file can reach real GitHub. `_BUDGET` is the only way to drive the
budget guard to zero. `_COMMENT_MODE` is the arming seam and FAILS CLOSED
across seven spellings. Each has a concrete in-repo consumer; none is
speculative.

`COMMENT_MODE_DEFAULT` is untouched: still the literal `"off"`, still pinned
both ways (value and source text). Nothing was posted to GitHub; every live run
in this work was read-only with mode=off.

VERIFIED AGAINST THE ORIGINAL SYMPTOM, on live data, not inferred from the code:
  old code (5ad08e5), `--pr 1518`:  VERDICT: COULD NOT MEASURE,  rc 0
  this tree,           `--pr 1518`:  VERDICT: INHERITED,          rc 10
and a four-PR read-only sweep returns INHERITED for #1518/#1515/#1450 with the
fix commits named, while #1177 (derived 39 against one name) stays COULD NOT
MEASURE with the HINT line.

⚠ The deleted-flag guard was SPELLED on its first draft and failed on the header
paragraph that explains the deletion — the same trap `_git_subcommands` is
AST-based to avoid. It now reads `add_argument` literals, module-level
assignments and string constants off the AST, so prose can neither satisfy nor
break it.

Suite: 71 tests, all green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQx16RMr8YQYBfsXaBEsSm
Claude-Session-Id: a7f5b63b-6de5-4fdc-9814-c8a8fc7b2ba0

* docs(stale-base-triage): the cap is 140 BYTES, and the two files disagreed about it

`scripts/lib/ci_status.py` said BYTES and `stale-base-triage.py` said
CHARACTERS, in prose describing the same boundary. Bytes is what was measured:
every real row sampled on this repo is 138 CHARACTERS, which is 140 bytes once
the `—` in `FAILED: pytests —` is counted as the three UTF-8 bytes it is. A
140-CHARACTER cap would have produced 138-byte, 140-character rows, and none
was seen.

⚠ WHAT THAT MEASUREMENT DOES NOT SETTLE, now said in the file rather than
implied: whether the byte cut is GitHub's or the posting pipeline's own
truncation. Every row sampled carries exactly one multi-byte character, so the
two hypotheses were never separated — a row with two em-dashes would do it, and
none exists. The boundary is measured; its owner is not.

It matters because a fixture built on a character cut lands two bytes late and
quietly leaves `failed=` readable — i.e. it would exercise the DIRECT route
while claiming to test the derived one, which is a green proving nothing.
`cut_like_github` in the test file cuts on bytes for exactly that reason and
`test_CONTROL_the_synthetic_rows_land_on_the_real_140_BYTE_boundary` asserts
both lengths.

No behaviour change. Suite: 71 tests, all green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQx16RMr8YQYBfsXaBEsSm
Claude-Session-Id: a7f5b63b-6de5-4fdc-9814-c8a8fc7b2ba0

* test(no-real-launchers): the consolidation made main-status-watch.py a `home-manager` hit — acknowledged and PINNED

`test_every_hazardous_binary_the_scripts_reach_is_stubbed_or_acknowledged` went
red on the D6 commit, and it was right to. `launcher_scan.hazard_hits` is a TEXT
scan over `scripts/` and says so in its own docstring — a prose mention counts,
deliberately and fail-closed. The shared-module import comment explains that the
systemd unit runs this file straight out of the checkout, so a `git pull`
delivers both files and no home-manager switch is involved, and that sentence is
exactly why importing a sibling module is safe here. It is the FIFTH file of
this shape.

🔴 RE-JUSTIFIED, NOT REWORDED, which is this repo's stated convention and is
written into the entry above the table — rewording to dodge the scanner removes
the sentence and keeps the risk.

🔴 AND PINNED, because that table entry says in its own words that an
acknowledgement "would otherwise blind the guard" — and it has been blinded
before: adding a real `home-manager switch` to a script whose name was already
acknowledged for other reasons produced a real launch with 54 guard tests green.
So two new tests carry this row:

  test_main_status_watch_SPAWNS_these_argv0_AND_NOTHING_ELSE — an AST walk
  asserting the spawn argv[0] set is exactly {git, <computed>, <not-a-list>},
  GROWS-OR-SHRINKS. Both opaque entries are named rather than waved at:
  `<computed>` is `[gh, "api", path]` behind the MAIN_STATUS_WATCH_GH stub seam,
  `<not-a-list>` is `trigger_deadman`'s `cmd`, whose production literal is
  pinned exactly — and refused a `--force` — by an existing test. The comment
  says outright that this pin is not the whole story on its own.

  test_home_manager_is_MENTIONED_but_never_SPAWNED — both halves of the
  acknowledgement's claim: the mention must still EXIST (or the row has outlived
  its sentence) and it must remain a MENTION, asserted against the argv[0] set
  AND against `_code_only()`, which strips comments and docstrings by AST.

BOTH CONTROLS WATCHED, in both directions:
  * injecting `subprocess.run(["home-manager", "switch"])` fails BOTH new tests,
    each with its own message — not a different guard's;
  * rewording the mention to "no HM switch" fails the mention-must-exist half
    AND the ledger's own file-set pin, because the row then over-claims.

⚠ FOUND BY RUNNING THE WHOLE `scripts/tests` TARGET, not by the change-scoped
mapping: `scoped-tests.sh` selected only the two suites named in the diff, and
this guard reads every file under `scripts/` regardless of what changed. A
change that only adds a COMMENT can go red here.

scripts/tests/{test_no_real_launchers,test_main_status_watch,test_stale_base_
triage}.py together: 252 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQx16RMr8YQYBfsXaBEsSm
Claude-Session-Id: a7f5b63b-6de5-4fdc-9814-c8a8fc7b2ba0

* fix(ci-signal): the consolidation moved code out from under the citation guard, and the first citation written into the new module dangled

ROUND 1 audit findings on #1524. Six filed, six addressed, plus one the same
fixture exposed in the sibling parser.

F1 — `scripts/lib/ci_status.py` cited
`test_newest_per_context_skips_a_malformed_row_rather_than_raising`, which
existed nowhere, and the arm it claimed was guarded was unguarded: deleting
`if not isinstance(row, dict): continue` SURVIVED all 172 tests. The deeper
cause is the guard, not the citation — both copies of
`test_every_test_this_script_names_actually_exists` are scoped to ONE script
file, so neither scanned the shared module the consolidation created. The
stale-base-triage copy now FOLLOWS its script's `scripts/lib` imports (derived,
not enumerated, so a second shared module is covered the day it appears) with a
positive control that fails if the import-following reaches nothing; the
main-status-watch copy names the owner rather than implying coverage it lacks.
The cited test is now written, and it kills the mutant.

F2 — `_git_subcommands` silently DROPPED what it could not constant-fold, so
its stated rule ("an unknown git subcommand is a write by default") was wider
than what it enforced: a computed head was ABSENT, not DENIED. It now emits
`<computed>` / `<not-a-list>` / `<empty-argv>` / `<no-argv>` sentinels — the
shape `test_main_status_watch.py`'s `_spawn_argv0_literals` already used 200
lines away in this same PR — reads argv from the SECOND POSITIONAL rather than
"whichever argument is a list", and the allowlist is a predicate a test drives
over synthetic source. The bare-`subprocess.run(["git", "push"])` hole the
finding also names is closed separately: every git spawn must be lexically
inside the one `_git` helper, and the spawn argv0 set is pinned
GROWS-OR-SHRINKS.

F3 — the `gh` write path was pinned by the literal word POST, which
`gh api -X PATCH`, `-X PUT`, `--method DELETE`, `gh pr comment` and
`gh api -f` (a POST with no method flag at all) each walk straight past. It now
asserts the STATE: an AST ledger of the `gh` verbs the source can reach
(`("api",)` and the one `("api", "-X", "POST")`), plus a receipt classifier that
decides read-vs-write from the method and the field flags. The harness stub also
had to start recording ONE LINE PER INVOCATION — a comment body contains
newlines, so `calls()` was returning prose fragments as if they were argvs,
which was invisible while the only question asked of a line was whether it
contained POST.

F4 — `TOTALS_RE` was unanchored and `re.search` takes the FIRST match, so a
description carrying an earlier per-target row derives a bound of 2 where the
run's own `failed=` is 27. That is the UNDER-count direction, the one error the
module explicitly forbids, and `run-tests.sh` really does print such rows
(`FAIL <dir> (collected=… passed=… skipped=… failed=… errors=…)`) before the
TOTAL banner. Both parsers are now anchored on `TOTAL` — `FAILED_COUNT_RE` had
the identical defect and is reachable from the same string, which the audit did
not file; fixing one and not the other would have left the same certification
hazard on the direct route. Both anchors err toward REFUSING. No reachable
exploit is claimed: what assembles the description is the posting pipeline in
homelab-talos and cannot be pinned from here.

F5 — the "140 BYTES" reconciliation (02bc6f8) had not finished;
`main-status-watch.py` said "140 characters" nineteen lines above its own
"THE 140-BYTE CAP". Swept all five sites including the test NAME, and replaced
the prose with a control: the two real truncated rows are 138 chars / 140 bytes,
asserted. The honest unknown is kept intact — whether the cut is GitHub's or the
pipeline's is still undetermined, because every sampled row carries exactly one
multi-byte character.

F6 — `sys.path.insert(0, …/scripts/lib)` prepended the whole shared-module
directory ahead of the standard library for every later import, including the
lazy `import traceback` on the unattended-crash path. `append` removes the class.

M2 (deleting `if not ctx: continue`) is NOT filed: an `""` key cannot match
either caller's context, so it is an equivalent mutant.

Evidence.
  Suites: test_stale_base_triage 71 -> 78, test_main_status_watch 101 -> 102.
  395 passed across the four affected files (the only files in the repo that
  reference any changed path; nothing else imports `ci_status`).
  ⚠ scoped-tests.sh is NOT evidence here and its PASS is withdrawn: it printed
  RESULT: PASS against the PRE-REBASE base, and on current main it correctly
  REFUSES (exit 4) because the diff touches `scripts/lib/**` — scoping a shared
  module selects the files that NAME it and drops every target reaching it by
  import. `scripts/gate.sh --tier both` is what covers that gap.
  RED at pr1524-head: the F4 test fails with derived=2 against the real 27.
  Mutation: 19 mutants, 18 KILLED, 1 SURVIVED — and the survivor is M4
  (first-match -> last-match), which is EQUIVALENT once anchored because
  `TOTAL collected=` occurs at most once on any banner this repo can post. That
  is asserted in the test rather than left unexplained, and reachability was
  confirmed separately: breaking the same statement kills 39 tests. Positive
  control KILLED in the batch; swept under PYTHONDONTWRITEBYTECODE=1 with
  __pycache__ removed between mutants.

COMMENT_MODE_DEFAULT is unchanged ("off"), both pins intact, nothing posted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQx16RMr8YQYBfsXaBEsSm
Claude-Session-Id: a7f5b63b-6de5-4fdc-9814-c8a8fc7b2ba0

* test(stale-base-triage): the write screen read a comment BODY as flags, and three guards were never exercised

ROUND 2's five findings. No payload change — every one of these is a weakness in
a guard this PR itself wrote.

F-R2-1 `gh_write_calls` parsed argument VALUES as flags, last-wins. Measured on
the previous version, same call shape as the real `post_comment`:

    -X POST /…/comments -f body=hello                       -> WRITE (control)
    -X POST /…/comments -f body=switch to --method GET now  -> read  (WRONG)
    -X POST /…/comments -f body=use -X GET here             -> read  (WRONG)
    /repos/o/r/x --input=payload.json                       -> read  (WRONG)
    /repos/o/r/x -fbody=hello                               -> read  (WRONG)

All five now classify as writes. The method is FIRST-wins and is scanned only up
to the first field flag — everything after one is body text — while field flags
are scanned to the end, in both the separated and the ATTACHED spelling `gh`
accepts. It does NOT stop at the first `/`-prefixed positional the way
`_gh_argv_shapes` does: `gh api /repos/o/r/x -X POST` is legal and would read as
clean, which is the one direction that matters. Reachable, so it is pinned end to
end as well: `comment_body` interpolates a `main` commit SUBJECT, and the new
test drives a hostile one through the real script.

F-R2-2 the spawn pin folded every computed argv0 into one set member, so
`binary = "git"; subprocess.run([binary, "push"])` appended to the real source
passed all four guards unchanged. Computed spawn heads are now asserted by NAME
(every one is the `gh` seam) and by COUNT (cross-checked against
`_gh_argv_shapes`), and the violating source is driven over synthetically.

F-R2-3 `_spawn_argv0`'s `<not-a-list>` arm — the net for `os.system("git push")`
— was never exercised; the mutant replacing it with `pass` SURVIVED. Driven over
synthetic source now, like `_git_subcommands`' sentinels.

F-R2-4 both scripts' `sys.path.append` carried a 🔴 comment and no guard;
reverting either to `sys.path.insert(0, …)` SURVIVED. Pinned in each file's own
test, structurally.

F-R2-5 five sites named GitHub as the cutter of the 140-byte description cap
while the new control says the measurement cannot distinguish GitHub's cut from
the pipeline's. The measured half (BYTES, not characters) is unchanged
everywhere; only the attribution is dropped, and each site now points at
`test_CONTROL_the_real_truncated_rows_land_on_the_BYTE_cap_not_the_CHAR_cap`.
`GITHUB_DESCRIPTION_BYTE_CAP`/`cut_like_github` renamed to `DESCRIPTION_BYTE_CAP`
/`cut_at_the_byte_cap` for the same reason. The unknown is NOT resolved in either
direction.

MUTATION TABLE (isolated `cp -a` copy, `PYTHONDONTWRITEBYTECODE=1`,
`__pycache__` cleared per mutant, verdicts counted from pytest's own result
lines; baseline in the copy 187 passed / 0 failed):

  mutant                                    verdict  killed by
  CONTEXT literal -> devrc-NOPE (control)   KILLED   test_the_context_is_PINNED_…
  method last-wins again                    KILLED   …reads_an_ARGUMENT_VALUE_as_DATA…
  body-stop removed                         KILLED   …reads_an_ARGUMENT_VALUE_as_DATA…
  `--input=` leaves the prefix tuple        KILLED   test_EVERY_field_flag_…
  attached short forms leave the tuple      KILLED   test_EVERY_field_flag_…
  M5 `<not-a-list>` arm -> pass             KILLED   …ARGV_IS_NOT_A_LIST_is_a_SENTINEL…
  M6 sbt sys.path.insert(0, …)              KILLED   …APPENDED_to_sys_path… (sbt)
  M8 msw sys.path.insert(0, …)              KILLED   …APPENDED_to_sys_path… (msw)
  second computed spawn in the real file    KILLED   …SECOND_computed_spawn… + the pin
  non-Name head folded into "gh"            KILLED   …SECOND_computed_spawn…

Each died on its own guard's assertion, naming the row or expression mutated —
not on a neighbouring guard's error. `method last-wins` SURVIVED on the first
sweep: with the body-stop in place the two rules only differ on a value carried
by a NON-body flag, so a `-H` row was added and it dies there.

Suites: `test_stale_base_triage.py` 84 + `test_main_status_watch.py` 103 = 187
passed (180 at 3aec90b, +7 new tests). `test_no_real_launchers.py` +
`test_audit_dispatch.py`, the other two files naming these scripts, 215 passed.
`COMMENT_MODE_DEFAULT = "off"` and both pins untouched; nothing posted anywhere.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session-Id: a7f5b63b-6de5-4fdc-9814-c8a8fc7b2ba0

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Zachary Lowden <dev@vetr.com>
ZacxDev added a commit that referenced this pull request Sep 13, 2026
…reen them out (#1629)

MEASURED, on this tool's own first live sweep. It named five PRs
`INHERITED — likely cured by rebase`. That is a falsifiable claim, so each was
tested by rebuilding the merged tree (PR head + current origin/main) and running
ONLY the named failing test:

    #1450  1 passed          -> INHERITED was RIGHT
    #1286  1 passed          -> INHERITED was RIGHT
    #1603  1 failed          -> INHERITED was FALSE
    #1194  1 failed          -> INHERITED was FALSE
    #1038  merge conflict    -> untestable, excluded from the rate

2 of 4 testable verdicts were FALSE. `#1600`'s own unit block calls ONE false
INHERITED disqualifying for arming, so this blocks arming until it is fixed.

THE MECHANISM. The INHERITED test is "the failing test's own FILE is
byte-identical at the head and the merge-base, and main has moved it". That is
sound for a test which exercises code it NAMES, and systematically wrong for a
repo-wide census/scanner guard, which inspects OTHER files: there an unchanged
guard file is the NORMAL state of a genuine, self-inflicted breakage, because
the PR never touches the guard — it adds a file the guard scans. Both false
verdicts were the same census guard, and in both the offender was a NEW FILE the
PR itself adds (#1194's was its own scripts/tests/test_break_glass_merge.py,
confirmed absent from main, so a rebase would have carried the offender along
with the red).

THE FIX reuses the derivation `ledger-check.sh` already runs (14daa42) rather
than growing a second one: `testlib.census_scan.analyze()` computes, from the
AST, every test whose verdict depends on the repo's FILE SET. A hand-kept list
of "the census guards" would itself be a ledger and would rot exactly the way
the ledgers it describes did. Membership => demote to NOT EXPLAINED BY
STALENESS, which is the conservative arm this tool already has.

FAIL-SAFE DIRECTION, and the bug my own test found. `census_scan.analyze()` on a
mis-rooted path RETURNS AN EMPTY RESULT rather than raising. The first version
trusted that and would have answered "not a census guard" for every test in the
repo — failing OPEN into precisely the bug being fixed. The trip is
`parsed == 0`; an unbuildable index RAISES and the caller records COULD NOT
MEASURE. A count floor was tried first and was wrong: it turned every
end-to-end fixture repo into COULD NOT MEASURE, because a small repo with no
census guards is a TRUE answer, not a broken scan. The production-strength
claim lives in the suite instead, as a positive control.

Placed LAST, after the evidence is computed, so a demoted verdict still prints
the candidates that WOULD have justified INHERITED — that evidence is what made
the original false verdicts falsifiable in one command. It can only ever demote;
it can never manufacture an INHERITED.

COST. The derivation is ~28s against a sweep that ran in 35s, so the index is
built on FIRST ASK and reused: a sweep that reaches no INHERITED branch pays
nothing. Pinned by a test that asserts the screen is not consulted for a test
the blob comparison already rejected.

Tests: 106 passed (was 93 before this change). The new block is red at
origin/main — 9 failed there, all on the absent symbol. The BEHAVIOURAL delta is
pinned on both sides with the identical fixture: base asserts ALPHA is
INHERITED (test_an_INHERITED_red_names_the_commit_that_already_fixed_it, still
green here), and the new test asserts the same fixture demotes when the oracle
says census guard — the two differ ONLY in the oracle's answer, so the screen is
isolated and nothing else moves.

Also widens the sys.path guard from "exactly one append" to "every mutation is
an append, and there are two". A count was the wrong pin: it goes red on an
addition that OBEYS the rule while saying nothing about one that breaks it.


Claude-Session: https://claude.ai/code/session_01298sLVSsAeZJpGNJTftdWv
Claude-Session-Id: 9ca6b87d-3d18-4866-9247-6ea38a486603

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant