@chrono-meta/fh-gate 1.4.96 → 1.4.97

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,425 @@
1
+ #!/usr/bin/env bash
2
+ # lane_runner_check.sh — a lane suite that nothing executes is prose, not an anchor.
3
+ #
4
+ # WHY (measured 2026-08-12, reship axis, card §🔱⑮ A):
5
+ # The card recorded three repairs as "지워도 레인 초록" — delete the repair, the lanes stay green,
6
+ # i.e. the anchor is decorative. Investigating those three found the class is much wider: of 43 lane
7
+ # and test suites under scripts/, **12 had no runner anywhere** — not selfcheck.sh, not the git
8
+ # hooks, not the CI workflows (all three surfaces enumerated, not assumed). 9 of the 12 ship to npm
9
+ # consumers (`npm pack --dry-run --json`, 263 files — a tarball measurement, not a files[] reading).
10
+ # ⚠️ This header said **11 / 8** until 2026-08-13 while the DEBT block below said 12, and the
11
+ # discrepancy is not a typo: 11 was the HAND-ROLLED probe's count, 12 is what this check found on
12
+ # its first run (the hand probe had read a pre-commit COMMENT as wiring). The corrected number went
13
+ # into the array and the prose above it was left alone, so the file carried both. Caught by
14
+ # cross-family review, not by re-reading. ★ A file that states its own measurement twice will
15
+ # eventually state it two different ways — and the stale copy is the one a reader meets first.
16
+ # The sharpest case was scripts/test_marker_floor_lanes.sh: pre-commit's
17
+ # validate_marker_floor() is live and blocks real commits, while its own known-pair calibration has
18
+ # never executed. A shipped gate whose calibration is dead is exactly the defect this repo spent the
19
+ # 2026-08-11/12 campaign closing, one file at a time.
20
+ #
21
+ # WHY A DERIVED CHECK AND NOT 12 MORE ANCHORS (the actual design decision):
22
+ # "Wire each one" closes today's 12 and is blind to the 13th — and the 13th is not hypothetical: the
23
+ # first run of THIS check found one (test_marker_crossfamily_lanes.sh) that the hand-rolled probe
24
+ # behind the original count had misread, because the hook names it only in a comment.
25
+ #
26
+ # ⚠️ THE JUSTIFICATION FOR BUILDING THIS IS THE MEASUREMENT, NOT A RECURRENCE COUNT — and the first
27
+ # draft of this header got that wrong. It claimed N=3 for "hardcoded list where a derived one is
28
+ # available" (citing selfcheck's `for _subj in compaction_probe judgment_circuit_lint
29
+ # novelty_claim_check`, test_selfcheck_state_lanes' four-name list, and the same-day ACCEPTED_ABSENT
30
+ # fix) and leaned on [[feedback_mechanize_at_repetition_prose_before]]'s N≥3 threshold. An
31
+ # independent scan refuted the count using this repo's own discriminator — "첫 발생 직후 고쳤다면
32
+ # 나머지가 막혔겠는가": the ACCEPTED_ABSENT fix is what CREATED the helper that exposed the second
33
+ # site, so those two are one discovery, not two recurrences; and the compaction_probe fix runs the
34
+ # OPPOSITE direction (no declaration existed there, so the correct fix was to start consulting the
35
+ # environment and assert both arms). Honest count for that class: **N=1–2, below threshold.**
36
+ #
37
+ # What actually justifies this file is a direct measurement, which needs no recurrence argument:
38
+ # **12 of 43 lane suites execute nowhere**, 8 of them shipped, including the calibrations for two
39
+ # gates that block real commits. That is a present hole, not a predicted one. Where the derived form
40
+ # is genuinely doing work is narrower and worth stating plainly: it is what makes the DEBT list
41
+ # shrink-only and what fails on the 13th — not a claim that hardcoded lists are a recurring disease
42
+ # here.
43
+ #
44
+ # THREE-VALUED, and the middle value is the whole point:
45
+ # WIRED — some runner invokes it (selfcheck · git hook · CI workflow · another script's dispatch)
46
+ # EXEMPT — declared below with a reason it must NOT be auto-run (cost, live CLI, network)
47
+ # DEBT — known-unwired, listed below, counted loudly on every run, does NOT block
48
+ # An undeclared unwired suite is none of these and FAILS. That is the regrowth this file exists to
49
+ # stop: today's 11 are debt, tomorrow's 12th is a failure.
50
+ #
51
+ # WHY DEBT DOESN'T BLOCK: 11 suites cannot be wired in one sitting, and a check that fails for weeks
52
+ # is a check that trains `--no-verify` — this repo has logged that trade explicitly
53
+ # ([[feedback_overblock_traded_for_failopen]]). The DEBT list is a decision surface, not a silencer:
54
+ # every entry is printed on every run, and the count is a number someone has to look at.
55
+ #
56
+ # Usage: bash scripts/lane_runner_check.sh [--list-debt]
57
+ # Exit: 0 = every suite is WIRED, EXEMPT, or declared DEBT · 1 = an undeclared suite has no runner,
58
+ # or the instrument itself broke (zero suites scanned / dead control).
59
+ set -uo pipefail
60
+
61
+ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
62
+ cd "$REPO_ROOT" || exit 1
63
+
64
+ # ── EXEMPT — declared reasons a suite must NOT be auto-run ────────────────────────────────────
65
+ # Same rule as package_coverage_check.sh's ACCEPTED_ABSENT: if you cannot write the sentence, the
66
+ # suite probably belongs in a runner instead of here.
67
+ #
68
+ # test_sessionstart_multihook_lanes.sh — spends live `claude` CLI calls (auth + tokens) on whoever
69
+ # runs it. selfcheck DOES reference it, via the SessionStart anchor-pair loop, and routes its
70
+ # exit 2 to NOT EXERCISED; it is listed here only so a future reader does not "fix" that loop
71
+ # into an unconditional run.
72
+ EXEMPT=(
73
+ "test_sessionstart_multihook_lanes.sh"
74
+ )
75
+
76
+ # ── DEBT — measured unwired on 2026-08-12, each awaiting a runner ─────────────────────────────
77
+ # This list is the todo, and it must only ever shrink. Adding a line here is a decision that needs a
78
+ # reason in the commit message; the check does not care, but the reviewer should.
79
+ # 🟥 WHAT `DEBT: 0` DOES NOT MEAN — read before quoting the number anywhere.
80
+ # The scope of this check is a NAME CONVENTION: `suites` is globbed as `test_*.sh` / `*_lanes.sh`
81
+ # (see the glob above). A lane suite that lives INSIDE its subject as a `--self-test` dispatcher has
82
+ # no such filename and is structurally invisible here. Measured 2026-08-13, after DEBT hit zero:
83
+ # scripts/chamber_witness.sh --self-test 16 lanes --self-test callers: 0
84
+ # scripts/capability_registry_check.sh --self-test 7 lanes --self-test callers: 0
85
+ # scripts/digest_landing_check.sh --self-test 10 lanes --self-test callers: 0
86
+ # scripts/directional_diff_gate.sh --self-test --self-test callers: 0
87
+ # 🟥 READ THAT COLUMN EXACTLY: it counts dispatches of `--self-test`, NOT callers of the script.
88
+ # The two come apart, and collapsing them does real damage in both directions. chamber_witness.sh
89
+ # has FOUR live production callers (chamber_run.sh:118·137·158·182) and zero self-test dispatches:
90
+ # writing that as "zero callers" would falsify the evidence sentence that ship_readiness_gate.md's
91
+ # identity-② promotion rests on ("wired into chamber_run.sh steps 2–5"), and ② would be overturned
92
+ # for a reason that is not true. The reverse shorthand is just as wrong: having production callers
93
+ # does NOT discharge the self-test debt — a witness generator whose known-pair never runs can break
94
+ # silently while every run that uses it still reports success, which is the fail-open the promotion
95
+ # criterion is supposed to exclude. (Both halves named by a peer session on 2026-08-13, after my
96
+ # own summary of this block used the collapsing shorthand.)
97
+ # (callers = grep over selfcheck.sh + .github/workflows/*.yml + templates/.git-hooks/*, the same
98
+ # three surfaces this check enumerates. All four SHIP.) selfcheck.sh wires exactly three embedded
99
+ # self-tests by name — compaction_probe · judgment_circuit_lint · novelty_claim_check — and these
100
+ # four are not among them. So the debt number below is true of the filename-convention population
101
+ # and false of "every lane suite in this repo executes". A second lens over `--self-test` dispatchers is
102
+ # the honest next step and is NOT built here; it is carried to the card as a measured residual so
103
+ # the number in this file cannot be read as a claim it does not make.
104
+ # (Raised by adversarial review against this delta, which called C1 "partially refuted" for exactly
105
+ # this reason. It was right: the denominator was mine to state and I had stated it as if it were the
106
+ # population.)
107
+ #
108
+ # ── 12 → 2 as of 2026-08-13 (PR: reship — lane-debt wiring) ──────────────────────────────────
109
+ # TEN of the twelve are discharged by the pair-loop in scripts/selfcheck.sh. TWO are back here,
110
+ # and the point of this block is that THE REASON CHANGED. Before today the entry meant "nothing
111
+ # runs this, and we do not know what happens if it does". Now it means "we ran it, and it fails
112
+ # outside this operator's machine" — which is a different, smaller, and actionable debt.
113
+ #
114
+ # What the twelve were: capability_entrypoint_shipping · chamber_run · destructive_pre_gate ·
115
+ # env_purity · field_canon · frontier_digest_retry · knowledge_seam · marker_crossfamily ·
116
+ # marker_floor · residency_closure · reviewer_capability_conformance · stale_clone_guard.
117
+ # Nine were in the published tarball, so consumers carried nine suites nothing called. The two
118
+ # that mattered most were the marker pair: test_marker_crossfamily_lanes.sh calibrates the
119
+ # `crossfamily:` enum that has hard-blocked commits since 2026-08-08 (the hook names it only in a
120
+ # COMMENT — templates/.git-hooks/pre-commit:1068 — which is what made a hand-rolled probe report
121
+ # 11 not 12), and test_marker_floor_lanes.sh calibrates pre-commit's live validate_marker_floor().
122
+ # Both are wired now.
123
+ #
124
+ # 🟥 THE TWO BELOW WERE WIRED, MEASURED RED IN CI, AND DELIBERATELY MOVED BACK. Making them green
125
+ # was available and is the wrong move: their failures are TRUE — the suites really do depend on
126
+ # things a CI runner does not have — so forcing a pass would be routing a real failure to green in
127
+ # the same delta that spent nine repairs closing exactly that. A suite whose preconditions are
128
+ # undeclared belongs in the todo list, not in the mandatory path.
129
+ DEBT=(
130
+ # PASS 19 · FAIL 0 locally; PASS 12 · FAIL 7 in CI. Reproduced locally by pointing HOME at an
131
+ # empty directory — 12/7, the same split — so the dependency is exact and not a guess:
132
+ # field_canon_preload.sh resolves `${HOME}/projects` and seven lanes assume the operator's
133
+ # mapped-project layout is there. Nothing in the suite declares that precondition, so on a
134
+ # machine without it the suite reports a REGRESSION rather than "not exercised here".
135
+ # Fix before re-wiring: the suite must detect the missing layout and exit NOT-EXERCISED (the
136
+ # shape test_sessionstart_multihook_lanes.sh already uses for its CLI dependency), or build its
137
+ # own fixture HOME for those seven the way it already does for the no-HOME lane.
138
+ "test_field_canon_lanes.sh"
139
+ # 17/17 locally; 2/17 in CI. And this one was PREDICTED: the adversarial review of this very
140
+ # delta flagged `elapsed < 10` as a wall-clock assertion that would go falsely red on a loaded CI
141
+ # runner, naming this file and that line. It was filed as a low-severity residual and not acted
142
+ # on; CI then produced exactly it. ★ The finding named a MECHANISM (a wall-clock assertion is now
143
+ # on a mandatory path), and a mechanism does not become less true for being labelled R.
144
+ # Fix before re-wiring: give the timing assertions headroom or gate them behind an explicit
145
+ # opt-in, so the default run measures logic and not the runner's load.
146
+ "test_stale_clone_guard_lanes.sh"
147
+ )
148
+
149
+ if [ "${1:-}" = "--list-debt" ]; then
150
+ # Same bash-3.2 empty-array guard as the scan call below. This site was MISSED when that one was
151
+ # fixed in the same commit — the fix stopped at the two sites the failing run happened to touch,
152
+ # and `--list-debt` (a documented interface, see Usage above) still died with `unbound variable`.
153
+ # Caught by adversarial review, not by me. [[feedback_half_fix_propagation_boundary]]: the
154
+ # question a repair must ask is "where else does this fact have to reach", and "the sites my
155
+ # repro exercised" is not an answer to it.
156
+ printf '%s\n' "${DEBT[@]+"${DEBT[@]}"}"
157
+ exit 0
158
+ fi
159
+
160
+ # ── SELF-REFERENCE: this checker is not a lane suite, so it does not appear in its own scanned set
161
+ # (the name pattern below matches `test_*.sh` / `*_lanes.sh`, and this is neither). That gap is the
162
+ # exact defect this file exists to detect, one level up: unwire it and nothing notices, because the
163
+ # only thing that would notice is itself. Measured by a revert probe on the day it was written —
164
+ # removing its call from selfcheck.sh left every check green, this one included.
165
+ # Guarded on selfcheck.sh's presence so a tree that legitimately lacks it does not fail here.
166
+ if [ -f scripts/selfcheck.sh ] && ! grep -qE '^[[:space:]]*(if !? ?)?bash scripts/lane_runner_check\.sh' scripts/selfcheck.sh; then
167
+ echo "FAIL lane-runner: scripts/selfcheck.sh no longer invokes this check — the checker that"
168
+ echo " detects unrun suites is itself unrun. Restore the call in selfcheck.sh."
169
+ exit 1
170
+ fi
171
+
172
+ # `"${ARR[@]+"${ARR[@]}"}"` and not the plain `"${ARR[@]}"`: on bash 3.2 (stock macOS) `set -u`
173
+ # treats an EMPTY array's expansion as an unbound variable and aborts. DEBT is now empty by design,
174
+ # which is exactly the state the plain form cannot survive — measured here 2026-08-13, and it
175
+ # aborted so early that the script still printed PASS (see the rc routing further down).
176
+ out=$(python3 - "${#EXEMPT[@]}" "${EXEMPT[@]+"${EXEMPT[@]}"}" "${DEBT[@]+"${DEBT[@]}"}" <<'PY'
177
+ import os, re, sys, glob, json
178
+
179
+ n_exempt = int(sys.argv[1])
180
+ exempt = set(sys.argv[2:2 + n_exempt])
181
+ debt = set(sys.argv[2 + n_exempt:])
182
+
183
+ # Suites: deduped by NAME. An earlier draft globbed `scripts/*_lanes.sh` and `scripts/test_*.sh`
184
+ # separately and summed them — every `test_*_lanes.sh` matched both, inflating 43 to 75. The count
185
+ # was never published; it was caught because a hand-check of one case disagreed with it
186
+ # (CLAUDE.md §Instrument-Calibration, hand-verify-one-sample). Keep this a set.
187
+ suites = sorted({os.path.basename(f) for f in glob.glob('scripts/*.sh')
188
+ if re.match(r'^(test_.*|.*_lanes)\.sh$', os.path.basename(f))})
189
+
190
+ # Runner surfaces, enumerated rather than assumed. All three were checked by hand when this file was
191
+ # written: CI runs `bash scripts/selfcheck.sh` + count_check + the plugin validators and nothing else.
192
+ SELF = os.path.basename(__file__) if '__file__' in dir() else 'lane_runner_check.sh'
193
+ runners = []
194
+ for pat in ('scripts/*.sh', 'templates/.git-hooks/*', '.github/workflows/*.yml'):
195
+ runners += [p for p in glob.glob(pat) if os.path.isfile(p)]
196
+ # This file names every DEBT and EXEMPT suite in its own arrays. Scanning itself would read each of
197
+ # those declarations as evidence the suite is run — the check would certify its own todo list as
198
+ # done. Caught by the known-negative control below on the first execution.
199
+ runners = [r for r in runners if os.path.basename(r) != 'lane_runner_check.sh']
200
+
201
+ # An invocation is `bash <path-ending-in-suite>`, not a mention of the name. The bounded `.{0,60}?`
202
+ # spans wrappers like `exec bash "$(dirname "$0")/x.sh"` (which contains a space inside the command
203
+ # substitution) without running to the end of an arbitrary line.
204
+ INVOKE = re.compile(r'\b(?:exec\s+)?(?:bash|sh)\s+.{0,60}?')
205
+
206
+ def has_runner(suite):
207
+ """A mention is not an invocation. Comments, package.json files[] entries and prose references in
208
+ tracks/ all name suites without running them — that conflation is what made the original count
209
+ wrong (75 vs the real 43).
210
+
211
+ Two measured failures shaped this predicate, both caught by the controls rather than by review:
212
+ · `\\b(bash|sh)\\b` matched the `sh` inside the `.sh` extension itself, so ANY line naming a
213
+ suite counted as running it — every suite read as WIRED, including the known-negative.
214
+ · An earlier quote-anchored regex missed `exec bash "$(dirname "$0")/x.sh"` and reported NONE
215
+ for a suite a hand-check had already confirmed was dispatched.
216
+ Both are the same class: the predicate was re-spelled instead of being pinned to a known pair."""
217
+ for r in runners:
218
+ if os.path.basename(r) == suite:
219
+ continue
220
+ try:
221
+ txt = open(r, encoding='utf-8', errors='replace').read()
222
+ except OSError:
223
+ continue
224
+ if runner_dispatches(suite, txt):
225
+ return True
226
+ return False
227
+
228
+ def runner_dispatches(suite, txt):
229
+ """The whole predicate, over ONE runner's text. Split out from has_runner so the controls below
230
+ can drive it with synthetic fixtures instead of with whatever the repo happens to look like
231
+ today — see the CONTROL block for why that mattered."""
232
+ direct = re.compile(r'\b(?:exec\s+)?(?:bash|sh)\s+.{0,60}?' + re.escape(suite))
233
+ lines = txt.split('\n')
234
+ # Does this runner invoke ANYTHING through a variable? If so, a suite named in one of its
235
+ # list constructs is plausibly dispatched by that loop.
236
+ indirect_dispatch = any(
237
+ re.search(r'\b(?:exec\s+)?(?:bash|sh)\s+"?\$', ln)
238
+ for ln in lines if not ln.strip().startswith('#'))
239
+ for line in lines:
240
+ if suite not in line or line.strip().startswith('#'):
241
+ continue
242
+ if direct.search(line):
243
+ return True
244
+ # for-list idiom, used twice in selfcheck.sh:
245
+ # for _anchor in scripts/a.sh scripts/b.sh; do ... bash "$_anchor" ... done
246
+ # for _pair in "subject|scripts/x.sh|mode" ...; do ... bash "$_anc" ... done
247
+ # The literal name is in the list; the invocation is through the variable, so the direct
248
+ # pattern above structurally cannot see it. Hand-verified 2026-08-12 for both
249
+ # test_session_close_lanes.sh and test_wizard_snippet_merge_lanes.sh — the strict
250
+ # detector called both UNWIRED and both are genuinely run.
251
+ # Gated on the file actually containing a variable dispatch, so a prose mention in a
252
+ # script that never runs anything indirectly does not get a free pass.
253
+ if indirect_dispatch and re.match(r'\s*(for\s+\w+\s+in\b|["\']?\S*\|)', line):
254
+ return True
255
+ return False
256
+
257
+ wired = {s for s in suites if has_runner(s)}
258
+
259
+ # ── CONTROL: the instrument must be able to see a suite known to be wired, and must NOT see one
260
+ # known to be dead. Without both arms a broken detector reports "all clean" or "all broken" and
261
+ # either reads as a verdict. [[feedback_absence_measurement_needs_control]]
262
+ CTL_POS = 'test_selfcheck_state_lanes.sh' # selfcheck.sh invokes this DIRECTLY
263
+ CTL_POS2 = 'test_session_close_lanes.sh' # selfcheck.sh:613 for-list + `bash "$_anchor"` — the
264
+ # INDIRECT arm. Pins the second detection branch: the
265
+ # strict detector called this UNWIRED and a hand-check
266
+ # showed it runs. Without this control that branch
267
+ # could silently rot back to blind.
268
+ # ── the known-NEGATIVE arm is now synthetic, and that is a repair, not a weakening ────────────
269
+ # It used to name a real suite (test_residency_closure_lanes.sh) that had zero callers. On
270
+ # 2026-08-13 the DEBT list was discharged, that suite gained a runner, and the control fired:
271
+ # "known-negative read as WIRED — the detector over-matches". The detector was fine. The CONTROL
272
+ # had been anchored to WORK NOT YET DONE, so doing the work broke the instrument. A negative
273
+ # control that lives on the todo list dies the moment the todo list is emptied — and it dies
274
+ # LOUDLY, as a false accusation against the thing it was guarding, which is the worst possible
275
+ # time to be debugging your own instrument.
276
+ # So the negative arm is driven with fixture text instead. The three fixtures are the three ways a
277
+ # name appears WITHOUT being run, all of them observed in this repo:
278
+ # · in a comment (every DEBT/EXEMPT declaration, and the pre-commit:1068 mention that fooled a
279
+ # hand-rolled probe into reporting 11 instead of 12)
280
+ # · in a manifest-style list with no dispatch anywhere in the file (package.json files[])
281
+ # · next to a `bash -n "$f"` syntax-check loop over a glob — flagged 2026-08-13 by a peer session
282
+ # as the way a DEBT count could be silently understated, since that loop touches every script
283
+ # in the tree. Pinned here so the answer stays measured rather than argued: syntax-checking a
284
+ # file is not running its lanes, and `bash -n` must never satisfy this predicate.
285
+ # 🟥 NAMED RESIDUAL — the indirect branch is a KNOWN BYPASS, and the claim is narrowed accordingly.
286
+ # Both an in-family adversarial review and an independent cross-family audit (codex/gpt-5.5) landed
287
+ # on this same hole on 2026-08-13, and the cross-family one reproduced it against the predicate
288
+ # itself: a file that contains `bash "$ANY_VAR"` ANYWHERE, plus a line of the shape
289
+ # `"x|scripts/test_future_lanes.sh|y"`, reads as WIRED even when nothing dispatches that list.
290
+ # So "an undeclared unwired suite cannot regrow" is true of a suite nobody MENTIONS and false of one
291
+ # added decoratively to an existing pair table. It is documented rather than closed because
292
+ # narrowing the branch to same-loop-body variable matching would put both real known-positives
293
+ # (selfcheck.sh:723 and :630) at risk, and this file sits exactly where "the repair is the main
294
+ # source of the next defect" has already been measured. The fixture below pins the CURRENT
295
+ # permissive behaviour so that a future narrowing is a visible, deliberate change rather than a
296
+ # silent one — it asserts what IS, not what should be.
297
+ CTL_BYPASS_FIXTURE = (
298
+ '#!/usr/bin/env bash\nbash "$UNRELATED"\n'
299
+ ' "subject|scripts/test_control_never_wired_lanes.sh|mode"\n')
300
+ CTL_NEG_NAME = 'test_control_never_wired_lanes.sh'
301
+ CTL_NEG_FIXTURES = [
302
+ ("comment-only mention",
303
+ '#!/usr/bin/env bash\nbash "$SOME_OTHER"\n# see test_control_never_wired_lanes.sh for detail\n'),
304
+ ("manifest-style list, no dispatch in file",
305
+ '{\n "files": [\n "scripts/test_control_never_wired_lanes.sh"\n ]\n}\n'),
306
+ ("bash -n glob loop + prose mention",
307
+ '#!/usr/bin/env bash\nfor f in scripts/*.sh; do bash -n "$f" || fail=1; done\n'
308
+ 'echo "coverage includes scripts/test_control_never_wired_lanes.sh"\n'),
309
+ ]
310
+ ctl = []
311
+ if CTL_POS in suites and CTL_POS not in wired:
312
+ ctl.append(f"known-positive {CTL_POS} read as UNWIRED — the direct detector is blind")
313
+ if CTL_POS2 in suites and CTL_POS2 not in wired:
314
+ ctl.append(f"known-positive {CTL_POS2} read as UNWIRED — the for-list/indirect branch is blind")
315
+ for _why, _txt in CTL_NEG_FIXTURES:
316
+ if runner_dispatches(CTL_NEG_NAME, _txt):
317
+ ctl.append(f"known-negative ({_why}) read as WIRED — the detector over-matches")
318
+ # The fixtures above drive `runner_dispatches` DIRECTLY, which leaves the layer between it and
319
+ # `has_runner` uncontrolled — runner enumeration, and in particular the self-exclusion filter that
320
+ # keeps this file out of its own scanned set. That filter is load-bearing: without it, every name
321
+ # declared in the arrays here reads as evidence that the suite runs, and the check certifies its own
322
+ # todo list as done. The old real-suite known-negative used to cover that path incidentally; the
323
+ # fixtures do not, so the loss is pinned explicitly rather than left as an unstated regression
324
+ # (raised by adversarial review, which was right that C4 was a strengthening AND a narrowing).
325
+ if any(os.path.basename(r) == 'lane_runner_check.sh' for r in runners):
326
+ ctl.append("self-exclusion filter is gone — this file is in its own runner set, so every name "
327
+ "in DEBT/EXEMPT would read as WIRED")
328
+ if not runners:
329
+ ctl.append("runner enumeration collapsed to zero — every suite would read as UNWIRED")
330
+ # And the positive arm on fixture text too, so the negative arm cannot pass by the predicate simply
331
+ # having gone blind — a detector that answers False to everything satisfies three negatives.
332
+ if not runner_dispatches(CTL_NEG_NAME, 'bash scripts/test_control_never_wired_lanes.sh\n'):
333
+ ctl.append("fixture known-positive read as UNWIRED — the predicate answers False to everything")
334
+ # Pins the documented bypass above. If this ever stops holding, someone narrowed the indirect branch
335
+ # — which may well be the right move, but it must be a decision, and both real known-positives above
336
+ # have to be re-confirmed at the same time. Update this line and the residual note together.
337
+ if not runner_dispatches(CTL_NEG_NAME, CTL_BYPASS_FIXTURE):
338
+ ctl.append("the documented indirect-branch bypass no longer reproduces — the predicate was "
339
+ "narrowed. That is not a failure, but the residual note above is now stale and the "
340
+ "two real for-list known-positives must be re-verified before removing this check.")
341
+ if ctl:
342
+ print("CONTROL_FAILED\t" + " · ".join(ctl))
343
+ raise SystemExit(2)
344
+
345
+ if not suites:
346
+ print("EXTRACTOR_BROKE\tzero lane suites found under scripts/")
347
+ raise SystemExit(2)
348
+
349
+ undeclared = sorted(s for s in suites if s not in wired and s not in exempt and s not in debt)
350
+ # A DEBT entry that has since gained a runner is resolved — say so, so the list shrinks by evidence
351
+ # rather than by someone remembering. Same shape as package_coverage_check.sh's STALE report.
352
+ resolved = sorted(s for s in debt if s in wired)
353
+ # A DEBT entry that no longer exists at all (renamed/deleted) is also stale.
354
+ gone = sorted(s for s in debt if s not in suites)
355
+
356
+ print(f"COUNTS\t{len(suites)}\t{len(wired)}\t{len(exempt)}\t{len(debt)}")
357
+ for s in undeclared:
358
+ print(f"UNDECLARED\t{s}")
359
+ for s in resolved:
360
+ print(f"RESOLVED\t{s}")
361
+ for s in gone:
362
+ print(f"GONE\t{s}")
363
+ PY
364
+ )
365
+ rc=$?
366
+
367
+ if [ "$rc" -eq 2 ]; then
368
+ echo "FAIL lane-runner: the instrument broke, it did not pass"
369
+ printf '%s\n' "$out" | sed 's/^/ /'
370
+ exit 1
371
+ fi
372
+
373
+ # ANY other non-zero is also the instrument failing, and it used to fall straight through to the
374
+ # PASS line below. Measured 2026-08-13, on this file, by the change that emptied DEBT: under
375
+ # `set -u` on bash 3.2 (stock macOS) an EMPTY array expanded with "${DEBT[@]}" is an UNBOUND
376
+ # VARIABLE, so the heredoc never ran, `out` was empty, every count parsed as 0 via the `${1:-0}`
377
+ # defaults — and the script printed `PASS lane-runner: 0 suites`. A dead instrument reported a
378
+ # clean tree. Only rc==2 was handled because only rc==2 had ever been produced deliberately;
379
+ # everything else was assumed impossible rather than routed. That assumption is the defect class
380
+ # this whole file exists to catch, reproduced inside the catcher.
381
+ if [ "$rc" -ne 0 ]; then
382
+ echo "FAIL lane-runner: the scan exited $rc — the instrument did not complete, so this run"
383
+ echo " measured nothing. It is not a pass. (An empty result set is NOT an empty todo list.)"
384
+ printf '%s\n' "$out" | head -5 | sed 's/^/ /'
385
+ exit 1
386
+ fi
387
+
388
+ COUNTS=$(printf '%s\n' "$out" | awk -F'\t' '$1=="COUNTS"{print $2" "$3" "$4" "$5}')
389
+ set -- $COUNTS
390
+ TOTAL="${1:-0}"; WIRED="${2:-0}"; N_EXEMPT="${3:-0}"; N_DEBT="${4:-0}"
391
+
392
+ UNDECLARED=$(printf '%s\n' "$out" | awk -F'\t' '$1=="UNDECLARED"{print $2}')
393
+ RESOLVED=$(printf '%s\n' "$out" | awk -F'\t' '$1=="RESOLVED"{print $2}')
394
+ GONE=$(printf '%s\n' "$out" | awk -F'\t' '$1=="GONE"{print $2}')
395
+
396
+ if [ -n "$UNDECLARED" ]; then
397
+ echo "FAIL lane-runner: lane suite(s) with no runner and no declaration:"
398
+ printf '%s\n' "$UNDECLARED" | sed 's/^/ /'
399
+ echo " A suite nothing executes is prose. Fix by ONE of:"
400
+ echo " · wire it into scripts/selfcheck.sh (the usual answer)"
401
+ echo " · add it to EXEMPT here WITH the reason it must not be auto-run"
402
+ echo " · add it to DEBT here if it is known-unwired work you are deferring"
403
+ exit 1
404
+ fi
405
+
406
+ # Advisory, never blocking — a stale DEBT entry is hygiene, and converting a clean run into a red
407
+ # gate over bookkeeping is how the check stops being read.
408
+ if [ -n "$RESOLVED" ]; then
409
+ echo "⚠️ lane-runner: DEBT entry(ies) now have a runner — remove them from DEBT:"
410
+ printf '%s\n' "$RESOLVED" | sed 's/^/ /'
411
+ fi
412
+ if [ -n "$GONE" ]; then
413
+ echo "⚠️ lane-runner: DEBT entry(ies) no longer exist (renamed/deleted?) — remove them:"
414
+ printf '%s\n' "$GONE" | sed 's/^/ /'
415
+ fi
416
+
417
+ if [ "$N_DEBT" -gt 0 ]; then
418
+ echo "⚠️ lane-runner: ${N_DEBT} suite(s) still have NO runner (declared debt — see DEBT for the"
419
+ echo " per-entry reason and the date each was measured; they are not all from one measurement)."
420
+ echo " They are shipped or present but never execute — see DEBT in this file. This number must"
421
+ echo " only go down; a new one fails the check rather than joining the list silently."
422
+ fi
423
+
424
+ echo "PASS lane-runner: ${TOTAL} suites — ${WIRED} wired · ${N_EXEMPT} exempt · ${N_DEBT} declared debt"
425
+ exit 0