@chrono-meta/fh-gate 1.4.96 → 1.4.98
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.
- package/.claude-plugin/marketplace.json +2 -2
- package/CATALOG.md +19 -0
- package/CHEATSHEET.md +9 -1
- package/CLAUDE.md +39 -6
- package/README.ja.md +10 -0
- package/README.ko.md +10 -0
- package/README.md +30 -0
- package/README.zh.md +10 -0
- package/docs/pillars.svg +3 -7
- package/knowledge/shared/harness-core/fh_ecosystem_positioning.md +2 -0
- package/knowledge/shared/harness-core/fh_global_positioning_and_distribution_roadmap.md +136 -0
- package/knowledge/shared/harness-core/fh_three_layer_canon.md +20 -0
- package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +215 -2
- package/knowledge/shared/harness-core/ship_readiness_gate.md +112 -0
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +125 -0
- package/package.json +9 -2
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-commons/skills/ko-tech-writer/SKILL.md +63 -12
- package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/CHANGELOG.md +65 -0
- package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +30 -0
- package/scripts/compaction_probe.sh +153 -10
- package/scripts/consent_registry_check.sh +63 -0
- package/scripts/degrade_direction_scan.sh +10 -1
- package/scripts/digest_landing_check.sh +20 -4
- package/scripts/fh_node_check.sh +60 -1
- package/scripts/fh_session_load.sh +22 -2
- package/scripts/frontier_digest_autopilot.sh +229 -0
- package/scripts/lane_runner_check.sh +637 -0
- package/scripts/package_coverage_check.sh +199 -35
- package/scripts/postinstall_notice.js +34 -0
- package/scripts/psa_scan_lib.sh +164 -0
- package/scripts/public_surface_scan_files.sh +30 -0
- package/scripts/selfcheck.sh +442 -33
- package/scripts/test_consent_registry.sh +54 -0
- package/scripts/test_degrade_scan_shell_probes.sh +75 -0
- package/scripts/test_field_canon_lanes.sh +29 -5
- package/scripts/test_lane_runner_lanes.sh +221 -0
- package/scripts/test_marker_crossfamily_lanes.sh +10 -1
- package/scripts/test_marker_floor_lanes.sh +25 -1
- package/scripts/test_psa_singlefile_lanes.sh +288 -0
- package/scripts/test_reviewer_capability_conformance.sh +10 -1
- package/scripts/test_selfcheck_state_lanes.sh +173 -0
- package/scripts/test_stale_clone_guard_lanes.sh +21 -7
- package/scripts/test_version_lockstep_lanes.sh +62 -0
- package/scripts/version_lockstep_check.sh +143 -1
- package/templates/.git-hooks/pre-commit +22 -1
- package/templates/degrade_direction_scan.sh +10 -1
- package/templates/goal-quench-settings-merged.json +30 -0
|
@@ -0,0 +1,637 @@
|
|
|
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 THAT WERE HERE ARE RE-WIRED (2026-08-14) — DEBT 2 → 0. Both entries below are
|
|
125
|
+
# HISTORICAL: they record why the suites were pulled out and what the fix had to be, kept because
|
|
126
|
+
# the reasoning (and the exact CI-reproduction numbers) are load-bearing evidence for the next
|
|
127
|
+
# person who touches either suite, not because either is still debt.
|
|
128
|
+
#
|
|
129
|
+
# test_field_canon_lanes.sh — was: PASS 19 · FAIL 0 locally; PASS 12 · FAIL 7 in CI. Reproduced
|
|
130
|
+
# locally by pointing HOME at an empty directory — 12/7, the same split — so the dependency was
|
|
131
|
+
# exact and not a guess: field_canon_preload.sh resolves `${HOME}/projects` and seven lanes assumed
|
|
132
|
+
# the operator's mapped-project layout was there. Nothing in the suite declared that precondition,
|
|
133
|
+
# so on a machine without it the suite reported a REGRESSION rather than "not exercised here". Fix
|
|
134
|
+
# (built the second option this entry named, not the first): the suite now carries its own fixture
|
|
135
|
+
# hub+project-root (mirroring the shape lane ⑭ already used) instead of depending on the operator's
|
|
136
|
+
# real filesystem — `run()`/`runD()` inject `CLAUDE_PROJECT_DIR`/`FIELD_CANON_PROJECT_ROOT` at a
|
|
137
|
+
# fixed fixture path. Verified both directions: PASS 19/19 under an empty HOME (the exact CI
|
|
138
|
+
# reproduction) AND a known-negative check — breaking the `-dev`-suffix repo-resolution fallback in
|
|
139
|
+
# field_canon_preload.sh reproduces the identical PASS 12 / FAIL 7 split, so the fixture measures
|
|
140
|
+
# real behavior, not a vacuously-green rewrite.
|
|
141
|
+
#
|
|
142
|
+
# test_stale_clone_guard_lanes.sh — was: 17/17 locally; 2/17 in CI. Predicted by an adversarial
|
|
143
|
+
# review of the delta that introduced `elapsed < 10` as a wall-clock assertion on a mandatory path;
|
|
144
|
+
# CI then produced exactly it. Fix (the first option this entry named): the wedge sleep widened
|
|
145
|
+
# 30s→60s and the assertion changed from a fixed `elapsed < 10` to a relative `elapsed <
|
|
146
|
+
# (wedge/2)=30s` — proving the guard's internal 0.5s budget bounded it (60x headroom) rather than an
|
|
147
|
+
# absolute constant tuned to one machine's speed. Known-negative check: neutering the budget's kill
|
|
148
|
+
# condition in stale_clone_guard.sh (so it waits out the full wedge instead of self-bounding)
|
|
149
|
+
# correctly fails exactly this one lane, confirming the widened threshold still discriminates.
|
|
150
|
+
#
|
|
151
|
+
# Re-wired into scripts/selfcheck.sh's pair-loop alongside the other ten (found→extend, same shape:
|
|
152
|
+
# "scripts/field_canon_preload.sh|scripts/test_field_canon_lanes.sh" and
|
|
153
|
+
# "scripts/stale_clone_guard.sh|scripts/test_stale_clone_guard_lanes.sh").
|
|
154
|
+
DEBT=()
|
|
155
|
+
|
|
156
|
+
if [ "${1:-}" = "--list-debt" ]; then
|
|
157
|
+
# Same bash-3.2 empty-array guard as the scan call below. This site was MISSED when that one was
|
|
158
|
+
# fixed in the same commit — the fix stopped at the two sites the failing run happened to touch,
|
|
159
|
+
# and `--list-debt` (a documented interface, see Usage above) still died with `unbound variable`.
|
|
160
|
+
# Caught by adversarial review, not by me. [[feedback_half_fix_propagation_boundary]]: the
|
|
161
|
+
# question a repair must ask is "where else does this fact have to reach", and "the sites my
|
|
162
|
+
# repro exercised" is not an answer to it.
|
|
163
|
+
printf '%s\n' "${DEBT[@]+"${DEBT[@]}"}"
|
|
164
|
+
exit 0
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# ── SELF-REFERENCE: this checker is not a lane suite, so it does not appear in its own scanned set
|
|
168
|
+
# (the name pattern below matches `test_*.sh` / `*_lanes.sh`, and this is neither). That gap is the
|
|
169
|
+
# exact defect this file exists to detect, one level up: unwire it and nothing notices, because the
|
|
170
|
+
# only thing that would notice is itself. Measured by a revert probe on the day it was written —
|
|
171
|
+
# removing its call from selfcheck.sh left every check green, this one included.
|
|
172
|
+
# Guarded on selfcheck.sh's presence so a tree that legitimately lacks it does not fail here.
|
|
173
|
+
if [ -f scripts/selfcheck.sh ] && ! grep -qE '^[[:space:]]*(if !? ?)?bash scripts/lane_runner_check\.sh' scripts/selfcheck.sh; then
|
|
174
|
+
echo "FAIL lane-runner: scripts/selfcheck.sh no longer invokes this check — the checker that"
|
|
175
|
+
echo " detects unrun suites is itself unrun. Restore the call in selfcheck.sh."
|
|
176
|
+
exit 1
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# ── ORG SEAM — a downstream org declares ITS OWN suites in ITS OWN file ───────────────────────
|
|
180
|
+
# Why this exists (requested 2026-08-13 by a downstream org fork of this harness, with evidence):
|
|
181
|
+
# EXEMPT and DEBT above are hardcoded arrays in a SHARED-LAYER file. A downstream fork carries
|
|
182
|
+
# suites this repo has never heard of (`test_rest_push_lanes.sh`, `test_company_delta_lanes.sh` …),
|
|
183
|
+
# and all three remedies this check offers land in files that fork does not own:
|
|
184
|
+
# wire into selfcheck.sh → shared layer · EXEMPT array → this file, shared layer
|
|
185
|
+
# DEBT array → this file, shared layer
|
|
186
|
+
# So the fork's only choices were "violate the sync boundary" or "live at rc=1 forever". Measured:
|
|
187
|
+
# 5 such suites on their first run of this check. That is not a preference — a permanently red
|
|
188
|
+
# check stops being read, and one of those five suites had just caught a real regression when it
|
|
189
|
+
# was run by hand.
|
|
190
|
+
#
|
|
191
|
+
# Shape: OPTIONAL file, absent by default. This repo ships none, so the no-op arm is the arm that
|
|
192
|
+
# runs here — verified by the known-pair in scripts/test_lane_runner_lanes.sh.
|
|
193
|
+
#
|
|
194
|
+
# 🟥 A MALFORMED FILE IS FAIL-CLOSED, a stale entry is advisory. Those are different failures:
|
|
195
|
+
# · cannot parse → the instrument did not look. Reporting "no declarations" would render
|
|
196
|
+
# UNMEASURED as ZERO, which is the exact family this repo keeps closing.
|
|
197
|
+
# · names a suite that no longer exists → hygiene, same as a stale DEBT entry above, so it warns
|
|
198
|
+
# with the same voice. Making the downstream rule STRICTER than the upstream
|
|
199
|
+
# one it mirrors would just train people to delete the file.
|
|
200
|
+
ORG_DECL="company/lane_declarations.yaml"
|
|
201
|
+
ORG_EXEMPT=(); ORG_DEBT=()
|
|
202
|
+
if [ -f "$ORG_DECL" ]; then
|
|
203
|
+
_org_out=$(python3 - "$ORG_DECL" <<'ORGPY'
|
|
204
|
+
import re, sys
|
|
205
|
+
path = sys.argv[1]
|
|
206
|
+
try:
|
|
207
|
+
txt = open(path, encoding='utf-8').read()
|
|
208
|
+
except OSError as e:
|
|
209
|
+
print(f"ERR\tcannot read {path}: {e}"); sys.exit(0)
|
|
210
|
+
sect = None; seen = {}
|
|
211
|
+
for i, ln in enumerate(txt.splitlines(), 1):
|
|
212
|
+
s = ln.split('#', 1)[0].rstrip()
|
|
213
|
+
if not s.strip():
|
|
214
|
+
continue
|
|
215
|
+
m = re.match(r'^([A-Za-z_]+):\s*$', s)
|
|
216
|
+
if m:
|
|
217
|
+
sect = m.group(1)
|
|
218
|
+
if sect not in ('exempt', 'debt'):
|
|
219
|
+
print(f"ERR\t{path}:{i}: unknown section '{sect}' (expected 'exempt' or 'debt')")
|
|
220
|
+
sys.exit(0)
|
|
221
|
+
continue
|
|
222
|
+
m = re.match(r'^\s+-\s+(\S+)\s*$', s)
|
|
223
|
+
if m:
|
|
224
|
+
if sect is None:
|
|
225
|
+
print(f"ERR\t{path}:{i}: entry before any section header"); sys.exit(0)
|
|
226
|
+
name = m.group(1)
|
|
227
|
+
if name in seen and seen[name] != sect:
|
|
228
|
+
print(f"ERR\t{path}:{i}: '{name}' declared both exempt and debt — ambiguous")
|
|
229
|
+
sys.exit(0)
|
|
230
|
+
seen[name] = sect
|
|
231
|
+
print(f"{sect}\t{name}")
|
|
232
|
+
continue
|
|
233
|
+
print(f"ERR\t{path}:{i}: unparseable line: {s.strip()[:60]}")
|
|
234
|
+
sys.exit(0)
|
|
235
|
+
ORGPY
|
|
236
|
+
)
|
|
237
|
+
# `grep -q '^ERR'` — NOT `'^ERR\t'`. In a POSIX BRE, GNU grep reads `\t` as a literal `t`
|
|
238
|
+
# (so the pattern becomes `^ERRt` and matches nothing) while BSD grep on macOS takes it as a tab.
|
|
239
|
+
# Measured 2026-08-13: this exact line passed all 11 lanes locally and failed L6/L7/L8 in CI —
|
|
240
|
+
# the fail-closed arm was silently dead on Linux, which is the platform that gates merges.
|
|
241
|
+
# Same family as the `"$DDSCAN" "` anchor divergence a sibling harness measured the same day:
|
|
242
|
+
# a regex that means different things on the two greps is an instrument, and an instrument that
|
|
243
|
+
# answers differently per platform has not measured anything.
|
|
244
|
+
if printf '%s' "$_org_out" | grep -q '^ERR'; then
|
|
245
|
+
echo "FAIL lane-runner: $ORG_DECL exists but could not be read as declarations —"
|
|
246
|
+
printf '%s' "$_org_out" | sed 's/^ERR[[:space:]]*/ /'
|
|
247
|
+
echo " An unreadable declaration file is UNMEASURED, not empty. Fix the file or delete it."
|
|
248
|
+
exit 2
|
|
249
|
+
fi
|
|
250
|
+
while IFS=$'\t' read -r _k _v; do
|
|
251
|
+
[ -z "${_v:-}" ] && continue
|
|
252
|
+
case "$_k" in
|
|
253
|
+
exempt) ORG_EXEMPT+=("$_v") ;;
|
|
254
|
+
debt) ORG_DEBT+=("$_v") ;;
|
|
255
|
+
esac
|
|
256
|
+
done <<< "$_org_out"
|
|
257
|
+
# Stale-entry warning, same voice as the DEBT hygiene warnings below.
|
|
258
|
+
for _n in "${ORG_EXEMPT[@]+"${ORG_EXEMPT[@]}"}" "${ORG_DEBT[@]+"${ORG_DEBT[@]}"}"; do
|
|
259
|
+
[ -f "scripts/$_n" ] || echo "⚠️ lane-runner: $ORG_DECL declares '$_n' but scripts/$_n does not exist (renamed/deleted?)"
|
|
260
|
+
done
|
|
261
|
+
# Visibility: an org-declared suite must never be indistinguishable from an upstream one. A
|
|
262
|
+
# reader who sees "0 debt" has to be able to tell whether that is this repo's zero or a fork's
|
|
263
|
+
# declaration absorbing its own list.
|
|
264
|
+
echo " lane-runner: $ORG_DECL — ${#ORG_EXEMPT[@]} exempt · ${#ORG_DEBT[@]} debt (org-declared)"
|
|
265
|
+
EXEMPT+=("${ORG_EXEMPT[@]+"${ORG_EXEMPT[@]}"}")
|
|
266
|
+
DEBT+=("${ORG_DEBT[@]+"${ORG_DEBT[@]}"}")
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
# `"${ARR[@]+"${ARR[@]}"}"` and not the plain `"${ARR[@]}"`: on bash 3.2 (stock macOS) `set -u`
|
|
270
|
+
# treats an EMPTY array's expansion as an unbound variable and aborts. DEBT is now empty by design,
|
|
271
|
+
# which is exactly the state the plain form cannot survive — measured here 2026-08-13, and it
|
|
272
|
+
# aborted so early that the script still printed PASS (see the rc routing further down).
|
|
273
|
+
out=$(python3 - "${#EXEMPT[@]}" "${EXEMPT[@]+"${EXEMPT[@]}"}" "${DEBT[@]+"${DEBT[@]}"}" <<'PY'
|
|
274
|
+
import os, re, sys, glob, json
|
|
275
|
+
|
|
276
|
+
n_exempt = int(sys.argv[1])
|
|
277
|
+
exempt = set(sys.argv[2:2 + n_exempt])
|
|
278
|
+
debt = set(sys.argv[2 + n_exempt:])
|
|
279
|
+
|
|
280
|
+
# Suites: deduped by NAME. An earlier draft globbed `scripts/*_lanes.sh` and `scripts/test_*.sh`
|
|
281
|
+
# separately and summed them — every `test_*_lanes.sh` matched both, inflating 43 to 75. The count
|
|
282
|
+
# was never published; it was caught because a hand-check of one case disagreed with it
|
|
283
|
+
# (CLAUDE.md §Instrument-Calibration, hand-verify-one-sample). Keep this a set.
|
|
284
|
+
suites = sorted({os.path.basename(f) for f in glob.glob('scripts/*.sh')
|
|
285
|
+
if re.match(r'^(test_.*|.*_lanes)\.sh$', os.path.basename(f))})
|
|
286
|
+
|
|
287
|
+
# Runner surfaces, enumerated rather than assumed. All three were checked by hand when this file was
|
|
288
|
+
# written: CI runs `bash scripts/selfcheck.sh` + count_check + the plugin validators and nothing else.
|
|
289
|
+
SELF = os.path.basename(__file__) if '__file__' in dir() else 'lane_runner_check.sh'
|
|
290
|
+
runners = []
|
|
291
|
+
for pat in ('scripts/*.sh', 'templates/.git-hooks/*', '.github/workflows/*.yml'):
|
|
292
|
+
runners += [p for p in glob.glob(pat) if os.path.isfile(p)]
|
|
293
|
+
# This file names every DEBT and EXEMPT suite in its own arrays. Scanning itself would read each of
|
|
294
|
+
# those declarations as evidence the suite is run — the check would certify its own todo list as
|
|
295
|
+
# done. Caught by the known-negative control below on the first execution.
|
|
296
|
+
runners = [r for r in runners if os.path.basename(r) != 'lane_runner_check.sh']
|
|
297
|
+
|
|
298
|
+
# An invocation is `bash <path-ending-in-suite>`, not a mention of the name. The bounded `.{0,60}?`
|
|
299
|
+
# spans wrappers like `exec bash "$(dirname "$0")/x.sh"` (which contains a space inside the command
|
|
300
|
+
# substitution) without running to the end of an arbitrary line.
|
|
301
|
+
INVOKE = re.compile(r'\b(?:exec\s+)?(?:bash|sh)\s+.{0,60}?')
|
|
302
|
+
|
|
303
|
+
def has_runner(suite):
|
|
304
|
+
"""A mention is not an invocation. Comments, package.json files[] entries and prose references in
|
|
305
|
+
tracks/ all name suites without running them — that conflation is what made the original count
|
|
306
|
+
wrong (75 vs the real 43).
|
|
307
|
+
|
|
308
|
+
Two measured failures shaped this predicate, both caught by the controls rather than by review:
|
|
309
|
+
· `\\b(bash|sh)\\b` matched the `sh` inside the `.sh` extension itself, so ANY line naming a
|
|
310
|
+
suite counted as running it — every suite read as WIRED, including the known-negative.
|
|
311
|
+
· An earlier quote-anchored regex missed `exec bash "$(dirname "$0")/x.sh"` and reported NONE
|
|
312
|
+
for a suite a hand-check had already confirmed was dispatched.
|
|
313
|
+
Both are the same class: the predicate was re-spelled instead of being pinned to a known pair."""
|
|
314
|
+
for r in runners:
|
|
315
|
+
if os.path.basename(r) == suite:
|
|
316
|
+
continue
|
|
317
|
+
try:
|
|
318
|
+
txt = open(r, encoding='utf-8', errors='replace').read()
|
|
319
|
+
except OSError:
|
|
320
|
+
continue
|
|
321
|
+
if runner_dispatches(suite, txt):
|
|
322
|
+
return True
|
|
323
|
+
return False
|
|
324
|
+
|
|
325
|
+
def runner_dispatches(suite, txt):
|
|
326
|
+
"""The whole predicate, over ONE runner's text. Split out from has_runner so the controls below
|
|
327
|
+
can drive it with synthetic fixtures instead of with whatever the repo happens to look like
|
|
328
|
+
today — see the CONTROL block for why that mattered."""
|
|
329
|
+
direct = re.compile(r'\b(?:exec\s+)?(?:bash|sh)\s+.{0,60}?' + re.escape(suite))
|
|
330
|
+
lines = txt.split('\n')
|
|
331
|
+
# Does this runner invoke ANYTHING through a variable? If so, a suite named in one of its
|
|
332
|
+
# list constructs is plausibly dispatched by that loop.
|
|
333
|
+
indirect_dispatch = any(
|
|
334
|
+
re.search(r'\b(?:exec\s+)?(?:bash|sh)\s+"?\$', ln)
|
|
335
|
+
for ln in lines if not ln.strip().startswith('#'))
|
|
336
|
+
for line in lines:
|
|
337
|
+
if suite not in line or line.strip().startswith('#'):
|
|
338
|
+
continue
|
|
339
|
+
if direct.search(line):
|
|
340
|
+
return True
|
|
341
|
+
# for-list idiom, used twice in selfcheck.sh:
|
|
342
|
+
# for _anchor in scripts/a.sh scripts/b.sh; do ... bash "$_anchor" ... done
|
|
343
|
+
# for _pair in "subject|scripts/x.sh|mode" ...; do ... bash "$_anc" ... done
|
|
344
|
+
# The literal name is in the list; the invocation is through the variable, so the direct
|
|
345
|
+
# pattern above structurally cannot see it. Hand-verified 2026-08-12 for both
|
|
346
|
+
# test_session_close_lanes.sh and test_wizard_snippet_merge_lanes.sh — the strict
|
|
347
|
+
# detector called both UNWIRED and both are genuinely run.
|
|
348
|
+
# Gated on the file actually containing a variable dispatch, so a prose mention in a
|
|
349
|
+
# script that never runs anything indirectly does not get a free pass.
|
|
350
|
+
if indirect_dispatch and re.match(r'\s*(for\s+\w+\s+in\b|["\']?\S*\|)', line):
|
|
351
|
+
return True
|
|
352
|
+
return False
|
|
353
|
+
|
|
354
|
+
wired = {s for s in suites if has_runner(s)}
|
|
355
|
+
|
|
356
|
+
# ── Embedded --self-test dispatchers — a class the name-pattern `suites` glob cannot see ──────
|
|
357
|
+
# Measured 2026-08-13, the header of this file, §WHAT DEBT:0 DOES NOT MEAN: a lane suite that
|
|
358
|
+
# lives INSIDE its subject as a `--self-test` flag — not a separate `test_*.sh`/`*_lanes.sh` file
|
|
359
|
+
# — is structurally invisible to the glob above. 4 scripts carry one — chamber_witness.sh ·
|
|
360
|
+
# capability_registry_check.sh · digest_landing_check.sh · directional_diff_gate.sh — and none
|
|
361
|
+
# showed up as WIRED or UNWIRED anywhere in this report; the wiring line for the 3 that ARE wired,
|
|
362
|
+
# the `for _subj in ...` loop at scripts/selfcheck.sh:478, could be deleted and nothing here would
|
|
363
|
+
# go red. found→extend, not a new file: same idiom as `suites`/`has_runner` above — discover
|
|
364
|
+
# subjects, detect dispatch, report undeclared — new predicates for the shape this pattern uses.
|
|
365
|
+
#
|
|
366
|
+
# 🟥 A PARENTHESIS TRAP LIVES IN THIS SPECIFIC HEREDOC, READ BEFORE ADDING A LINE HERE.
|
|
367
|
+
# Measured 2026-08-14: a single heredoc-body line whose own paren count was unbalanced — one more
|
|
368
|
+
# close-paren than open-paren, from a regex needing a literal close-paren character — broke bash's
|
|
369
|
+
# parse of every line after it, on THIS file only. The reason is that the heredoc below sits inside
|
|
370
|
+
# a command-substitution wrapper, and that wrapper's own close-paren-matching scan turned out not
|
|
371
|
+
# to be fully heredoc-blind in the bash build this repo has tested. `bash -n` then failed dozens of
|
|
372
|
+
# lines later with an unrelated-looking error, because by then the parser believed the heredoc had
|
|
373
|
+
# already closed. Reproduced in isolation: a bare regex assignment needing a literal close-paren,
|
|
374
|
+
# spliced into this file at this exact position, alone, with nothing else added. The fix is
|
|
375
|
+
# structural, not "be careful" — every line added inside this heredoc must carry a matched
|
|
376
|
+
# open-paren and close-paren count on that same line, and a matched pair spanning two lines is
|
|
377
|
+
# exactly the shape that tripped this the first time. Collapse it back to one line, or spell the
|
|
378
|
+
# literal paren out as an escape sequence instead of a bare character, rather than splitting it
|
|
379
|
+
# across lines.
|
|
380
|
+
# A bare substring match on --self-test would also catch prose that only DISCUSSES the flag
|
|
381
|
+
# (measured: scripts/selfcheck.sh:482 has a comment naming it as an example of what NOT to grep
|
|
382
|
+
# for, which is exactly the false positive this narrower check exists to avoid). Require one of
|
|
383
|
+
# the two real dispatcher shapes instead: `"--self-test"` in a quoted comparison, or `--self-test)`
|
|
384
|
+
# as a bare case-pattern. The close-paren is built via chr — see the paren-trap note above; a
|
|
385
|
+
# literal `)` character on this line, however it is spelled, throws this file's parser off.
|
|
386
|
+
_CP = chr(41)
|
|
387
|
+
SELFTEST_DISPATCH_FORMS = ('"--self-test"', '--self-test' + _CP)
|
|
388
|
+
|
|
389
|
+
def _read(path):
|
|
390
|
+
try:
|
|
391
|
+
return open(path, encoding='utf-8', errors='replace').read()
|
|
392
|
+
except OSError:
|
|
393
|
+
return ''
|
|
394
|
+
|
|
395
|
+
# One line on purpose — see the paren-trap note above the SELFTEST_PAT definition.
|
|
396
|
+
_st_candidates = [f for f in glob.glob('scripts/*.sh') if os.path.basename(f) not in suites and os.path.basename(f) != 'lane_runner_check.sh']
|
|
397
|
+
_st_names = [os.path.basename(f)[:-3] for f in _st_candidates if any(_form in _read(f) for _form in SELFTEST_DISPATCH_FORMS)]
|
|
398
|
+
selftest_subjects = sorted(set(_st_names))
|
|
399
|
+
|
|
400
|
+
def selftest_dispatched(bare_name, txt):
|
|
401
|
+
"""Two shapes, both real in this repo. Cross-family review (2026-08-14) caught the first draft
|
|
402
|
+
shipping only the second — it read scripts/selfcheck.sh:478's `_subj` for-loop but missed
|
|
403
|
+
:898/:933's direct `bash scripts/probe_scope_check.sh --self-test` / `bash scripts/
|
|
404
|
+
utterance_landing_check.sh --self-test`, so those two subjects were reported UNDECLARED while
|
|
405
|
+
selfcheck.sh runs them every time. This is the exact failure the header above names by cite —
|
|
406
|
+
a reader trusting the count over the source would have been told a false thing with confidence.
|
|
407
|
+
|
|
408
|
+
Shape 1 (direct): `bash scripts/<name>.sh ... --self-test` on one line — mirrors
|
|
409
|
+
runner_dispatches' direct branch, structurally simpler than the indirect case below.
|
|
410
|
+
Shape 2 (indirect): bare_name sits in a `for VAR in ... bare_name ...; do` loop whose body
|
|
411
|
+
dispatches $VAR with --self-test — mirrors the indirect-branch reasoning of runner_dispatches:
|
|
412
|
+
the literal name is in a list construct, the invocation runs through the loop variable, so a
|
|
413
|
+
direct-dispatch grep alone structurally cannot see it (scripts/selfcheck.sh:478)."""
|
|
414
|
+
if re.search(rf'\bbash\s+scripts/{re.escape(bare_name)}\.sh\b[^\n]*--self-test', txt):
|
|
415
|
+
return True
|
|
416
|
+
in_loop = False; loop_var = None; has_name = False
|
|
417
|
+
for ln in txt.split('\n'):
|
|
418
|
+
s = ln.strip()
|
|
419
|
+
m = re.match(r'for\s+(\w+)\s+in\b(.*)', s)
|
|
420
|
+
if m:
|
|
421
|
+
loop_var = m.group(1)
|
|
422
|
+
has_name = bool(re.search(rf'\b{re.escape(bare_name)}\b', m.group(2)))
|
|
423
|
+
in_loop = True
|
|
424
|
+
continue
|
|
425
|
+
if in_loop:
|
|
426
|
+
if has_name and '--self-test' in ln and re.search(rf'\${{?{re.escape(loop_var)}\b', ln):
|
|
427
|
+
return True
|
|
428
|
+
if s == 'done':
|
|
429
|
+
in_loop = False; loop_var = None; has_name = False
|
|
430
|
+
return False
|
|
431
|
+
|
|
432
|
+
def has_selftest_runner(bare_name):
|
|
433
|
+
return any(selftest_dispatched(bare_name, _read(r)) for r in runners)
|
|
434
|
+
|
|
435
|
+
selftest_wired = {s for s in selftest_subjects if has_selftest_runner(s)}
|
|
436
|
+
selftest_undeclared = sorted(s for s in selftest_subjects if s not in selftest_wired)
|
|
437
|
+
|
|
438
|
+
# Minimal known-pair — proportionate to the size of this addition, not the full CTL apparatus
|
|
439
|
+
# below, but a dead predicate must still be caught rather than trusted on read-through alone.
|
|
440
|
+
_ST_POS_FIXTURE = 'for _subj in alpha beta; do\n bash "scripts/$_subj.sh" --self-test\ndone\n'
|
|
441
|
+
_ST_NEG_FIXTURE = 'for _subj in alpha beta; do\n bash "scripts/$_subj.sh" --normal-run\ndone\n'
|
|
442
|
+
if not selftest_dispatched('alpha', _ST_POS_FIXTURE):
|
|
443
|
+
print("CONTROL_FAILED\tself-test known-positive fixture read as undispatched — detector is blind")
|
|
444
|
+
raise SystemExit(2)
|
|
445
|
+
if selftest_dispatched('alpha', _ST_NEG_FIXTURE):
|
|
446
|
+
print("CONTROL_FAILED\tself-test known-negative fixture with no --self-test flag read as dispatched")
|
|
447
|
+
raise SystemExit(2)
|
|
448
|
+
|
|
449
|
+
# ── CONTROL: the instrument must be able to see a suite known to be wired, and must NOT see one
|
|
450
|
+
# known to be dead. Without both arms a broken detector reports "all clean" or "all broken" and
|
|
451
|
+
# either reads as a verdict. [[feedback_absence_measurement_needs_control]]
|
|
452
|
+
CTL_POS = 'test_selfcheck_state_lanes.sh' # selfcheck.sh invokes this DIRECTLY
|
|
453
|
+
CTL_POS2 = 'test_session_close_lanes.sh' # selfcheck.sh:613 for-list + `bash "$_anchor"` — the
|
|
454
|
+
# INDIRECT arm. Pins the second detection branch: the
|
|
455
|
+
# strict detector called this UNWIRED and a hand-check
|
|
456
|
+
# showed it runs. Without this control that branch
|
|
457
|
+
# could silently rot back to blind.
|
|
458
|
+
# ── the known-NEGATIVE arm is now synthetic, and that is a repair, not a weakening ────────────
|
|
459
|
+
# It used to name a real suite (test_residency_closure_lanes.sh) that had zero callers. On
|
|
460
|
+
# 2026-08-13 the DEBT list was discharged, that suite gained a runner, and the control fired:
|
|
461
|
+
# "known-negative read as WIRED — the detector over-matches". The detector was fine. The CONTROL
|
|
462
|
+
# had been anchored to WORK NOT YET DONE, so doing the work broke the instrument. A negative
|
|
463
|
+
# control that lives on the todo list dies the moment the todo list is emptied — and it dies
|
|
464
|
+
# LOUDLY, as a false accusation against the thing it was guarding, which is the worst possible
|
|
465
|
+
# time to be debugging your own instrument.
|
|
466
|
+
# So the negative arm is driven with fixture text instead. The three fixtures are the three ways a
|
|
467
|
+
# name appears WITHOUT being run, all of them observed in this repo:
|
|
468
|
+
# · in a comment (every DEBT/EXEMPT declaration, and the pre-commit:1068 mention that fooled a
|
|
469
|
+
# hand-rolled probe into reporting 11 instead of 12)
|
|
470
|
+
# · in a manifest-style list with no dispatch anywhere in the file (package.json files[])
|
|
471
|
+
# · next to a `bash -n "$f"` syntax-check loop over a glob — flagged 2026-08-13 by a peer session
|
|
472
|
+
# as the way a DEBT count could be silently understated, since that loop touches every script
|
|
473
|
+
# in the tree. Pinned here so the answer stays measured rather than argued: syntax-checking a
|
|
474
|
+
# file is not running its lanes, and `bash -n` must never satisfy this predicate.
|
|
475
|
+
# 🟥 NAMED RESIDUAL — the indirect branch is a KNOWN BYPASS, and the claim is narrowed accordingly.
|
|
476
|
+
# Both an in-family adversarial review and an independent cross-family audit (codex/gpt-5.5) landed
|
|
477
|
+
# on this same hole on 2026-08-13, and the cross-family one reproduced it against the predicate
|
|
478
|
+
# itself: a file that contains `bash "$ANY_VAR"` ANYWHERE, plus a line of the shape
|
|
479
|
+
# `"x|scripts/test_future_lanes.sh|y"`, reads as WIRED even when nothing dispatches that list.
|
|
480
|
+
# So "an undeclared unwired suite cannot regrow" is true of a suite nobody MENTIONS and false of one
|
|
481
|
+
# added decoratively to an existing pair table. It is documented rather than closed because
|
|
482
|
+
# narrowing the branch to same-loop-body variable matching would put both real known-positives
|
|
483
|
+
# (selfcheck.sh:723 and :630) at risk, and this file sits exactly where "the repair is the main
|
|
484
|
+
# source of the next defect" has already been measured. The fixture below pins the CURRENT
|
|
485
|
+
# permissive behaviour so that a future narrowing is a visible, deliberate change rather than a
|
|
486
|
+
# silent one — it asserts what IS, not what should be.
|
|
487
|
+
CTL_BYPASS_FIXTURE = (
|
|
488
|
+
'#!/usr/bin/env bash\nbash "$UNRELATED"\n'
|
|
489
|
+
' "subject|scripts/test_control_never_wired_lanes.sh|mode"\n')
|
|
490
|
+
CTL_NEG_NAME = 'test_control_never_wired_lanes.sh'
|
|
491
|
+
CTL_NEG_FIXTURES = [
|
|
492
|
+
("comment-only mention",
|
|
493
|
+
'#!/usr/bin/env bash\nbash "$SOME_OTHER"\n# see test_control_never_wired_lanes.sh for detail\n'),
|
|
494
|
+
("manifest-style list, no dispatch in file",
|
|
495
|
+
'{\n "files": [\n "scripts/test_control_never_wired_lanes.sh"\n ]\n}\n'),
|
|
496
|
+
("bash -n glob loop + prose mention",
|
|
497
|
+
'#!/usr/bin/env bash\nfor f in scripts/*.sh; do bash -n "$f" || fail=1; done\n'
|
|
498
|
+
'echo "coverage includes scripts/test_control_never_wired_lanes.sh"\n'),
|
|
499
|
+
]
|
|
500
|
+
ctl = []
|
|
501
|
+
if CTL_POS in suites and CTL_POS not in wired:
|
|
502
|
+
ctl.append(f"known-positive {CTL_POS} read as UNWIRED — the direct detector is blind")
|
|
503
|
+
if CTL_POS2 in suites and CTL_POS2 not in wired:
|
|
504
|
+
ctl.append(f"known-positive {CTL_POS2} read as UNWIRED — the for-list/indirect branch is blind")
|
|
505
|
+
for _why, _txt in CTL_NEG_FIXTURES:
|
|
506
|
+
if runner_dispatches(CTL_NEG_NAME, _txt):
|
|
507
|
+
ctl.append(f"known-negative ({_why}) read as WIRED — the detector over-matches")
|
|
508
|
+
# The fixtures above drive `runner_dispatches` DIRECTLY, which leaves the layer between it and
|
|
509
|
+
# `has_runner` uncontrolled — runner enumeration, and in particular the self-exclusion filter that
|
|
510
|
+
# keeps this file out of its own scanned set. That filter is load-bearing: without it, every name
|
|
511
|
+
# declared in the arrays here reads as evidence that the suite runs, and the check certifies its own
|
|
512
|
+
# todo list as done. The old real-suite known-negative used to cover that path incidentally; the
|
|
513
|
+
# fixtures do not, so the loss is pinned explicitly rather than left as an unstated regression
|
|
514
|
+
# (raised by adversarial review, which was right that C4 was a strengthening AND a narrowing).
|
|
515
|
+
if any(os.path.basename(r) == 'lane_runner_check.sh' for r in runners):
|
|
516
|
+
ctl.append("self-exclusion filter is gone — this file is in its own runner set, so every name "
|
|
517
|
+
"in DEBT/EXEMPT would read as WIRED")
|
|
518
|
+
if not runners:
|
|
519
|
+
ctl.append("runner enumeration collapsed to zero — every suite would read as UNWIRED")
|
|
520
|
+
# And the positive arm on fixture text too, so the negative arm cannot pass by the predicate simply
|
|
521
|
+
# having gone blind — a detector that answers False to everything satisfies three negatives.
|
|
522
|
+
if not runner_dispatches(CTL_NEG_NAME, 'bash scripts/test_control_never_wired_lanes.sh\n'):
|
|
523
|
+
ctl.append("fixture known-positive read as UNWIRED — the predicate answers False to everything")
|
|
524
|
+
# Pins the documented bypass above. If this ever stops holding, someone narrowed the indirect branch
|
|
525
|
+
# — which may well be the right move, but it must be a decision, and both real known-positives above
|
|
526
|
+
# have to be re-confirmed at the same time. Update this line and the residual note together.
|
|
527
|
+
if not runner_dispatches(CTL_NEG_NAME, CTL_BYPASS_FIXTURE):
|
|
528
|
+
ctl.append("the documented indirect-branch bypass no longer reproduces — the predicate was "
|
|
529
|
+
"narrowed. That is not a failure, but the residual note above is now stale and the "
|
|
530
|
+
"two real for-list known-positives must be re-verified before removing this check.")
|
|
531
|
+
if ctl:
|
|
532
|
+
print("CONTROL_FAILED\t" + " · ".join(ctl))
|
|
533
|
+
raise SystemExit(2)
|
|
534
|
+
|
|
535
|
+
if not suites:
|
|
536
|
+
print("EXTRACTOR_BROKE\tzero lane suites found under scripts/")
|
|
537
|
+
raise SystemExit(2)
|
|
538
|
+
|
|
539
|
+
undeclared = sorted(s for s in suites if s not in wired and s not in exempt and s not in debt)
|
|
540
|
+
# A DEBT entry that has since gained a runner is resolved — say so, so the list shrinks by evidence
|
|
541
|
+
# rather than by someone remembering. Same shape as package_coverage_check.sh's STALE report.
|
|
542
|
+
resolved = sorted(s for s in debt if s in wired)
|
|
543
|
+
# A DEBT entry that no longer exists at all (renamed/deleted) is also stale.
|
|
544
|
+
gone = sorted(s for s in debt if s not in suites)
|
|
545
|
+
|
|
546
|
+
print(f"COUNTS\t{len(suites)}\t{len(wired)}\t{len(exempt)}\t{len(debt)}")
|
|
547
|
+
for s in undeclared:
|
|
548
|
+
print(f"UNDECLARED\t{s}")
|
|
549
|
+
for s in resolved:
|
|
550
|
+
print(f"RESOLVED\t{s}")
|
|
551
|
+
for s in gone:
|
|
552
|
+
print(f"GONE\t{s}")
|
|
553
|
+
print(f"SELFTEST_COUNTS\t{len(selftest_subjects)}\t{len(selftest_wired)}")
|
|
554
|
+
for s in selftest_undeclared:
|
|
555
|
+
print(f"SELFTEST_UNDECLARED\t{s}")
|
|
556
|
+
PY
|
|
557
|
+
)
|
|
558
|
+
rc=$?
|
|
559
|
+
|
|
560
|
+
if [ "$rc" -eq 2 ]; then
|
|
561
|
+
echo "FAIL lane-runner: the instrument broke, it did not pass"
|
|
562
|
+
printf '%s\n' "$out" | sed 's/^/ /'
|
|
563
|
+
exit 1
|
|
564
|
+
fi
|
|
565
|
+
|
|
566
|
+
# ANY other non-zero is also the instrument failing, and it used to fall straight through to the
|
|
567
|
+
# PASS line below. Measured 2026-08-13, on this file, by the change that emptied DEBT: under
|
|
568
|
+
# `set -u` on bash 3.2 (stock macOS) an EMPTY array expanded with "${DEBT[@]}" is an UNBOUND
|
|
569
|
+
# VARIABLE, so the heredoc never ran, `out` was empty, every count parsed as 0 via the `${1:-0}`
|
|
570
|
+
# defaults — and the script printed `PASS lane-runner: 0 suites`. A dead instrument reported a
|
|
571
|
+
# clean tree. Only rc==2 was handled because only rc==2 had ever been produced deliberately;
|
|
572
|
+
# everything else was assumed impossible rather than routed. That assumption is the defect class
|
|
573
|
+
# this whole file exists to catch, reproduced inside the catcher.
|
|
574
|
+
if [ "$rc" -ne 0 ]; then
|
|
575
|
+
echo "FAIL lane-runner: the scan exited $rc — the instrument did not complete, so this run"
|
|
576
|
+
echo " measured nothing. It is not a pass. (An empty result set is NOT an empty todo list.)"
|
|
577
|
+
printf '%s\n' "$out" | head -5 | sed 's/^/ /'
|
|
578
|
+
exit 1
|
|
579
|
+
fi
|
|
580
|
+
|
|
581
|
+
COUNTS=$(printf '%s\n' "$out" | awk -F'\t' '$1=="COUNTS"{print $2" "$3" "$4" "$5}')
|
|
582
|
+
set -- $COUNTS
|
|
583
|
+
TOTAL="${1:-0}"; WIRED="${2:-0}"; N_EXEMPT="${3:-0}"; N_DEBT="${4:-0}"
|
|
584
|
+
|
|
585
|
+
UNDECLARED=$(printf '%s\n' "$out" | awk -F'\t' '$1=="UNDECLARED"{print $2}')
|
|
586
|
+
RESOLVED=$(printf '%s\n' "$out" | awk -F'\t' '$1=="RESOLVED"{print $2}')
|
|
587
|
+
GONE=$(printf '%s\n' "$out" | awk -F'\t' '$1=="GONE"{print $2}')
|
|
588
|
+
|
|
589
|
+
if [ -n "$UNDECLARED" ]; then
|
|
590
|
+
echo "FAIL lane-runner: lane suite(s) with no runner and no declaration:"
|
|
591
|
+
printf '%s\n' "$UNDECLARED" | sed 's/^/ /'
|
|
592
|
+
echo " A suite nothing executes is prose. Fix by ONE of:"
|
|
593
|
+
echo " · wire it into scripts/selfcheck.sh (the usual answer)"
|
|
594
|
+
echo " · add it to EXEMPT here WITH the reason it must not be auto-run"
|
|
595
|
+
echo " · add it to DEBT here if it is known-unwired work you are deferring"
|
|
596
|
+
exit 1
|
|
597
|
+
fi
|
|
598
|
+
|
|
599
|
+
# Advisory, never blocking — a stale DEBT entry is hygiene, and converting a clean run into a red
|
|
600
|
+
# gate over bookkeeping is how the check stops being read.
|
|
601
|
+
if [ -n "$RESOLVED" ]; then
|
|
602
|
+
echo "⚠️ lane-runner: DEBT entry(ies) now have a runner — remove them from DEBT:"
|
|
603
|
+
printf '%s\n' "$RESOLVED" | sed 's/^/ /'
|
|
604
|
+
fi
|
|
605
|
+
if [ -n "$GONE" ]; then
|
|
606
|
+
echo "⚠️ lane-runner: DEBT entry(ies) no longer exist (renamed/deleted?) — remove them:"
|
|
607
|
+
printf '%s\n' "$GONE" | sed 's/^/ /'
|
|
608
|
+
fi
|
|
609
|
+
|
|
610
|
+
if [ "$N_DEBT" -gt 0 ]; then
|
|
611
|
+
echo "⚠️ lane-runner: ${N_DEBT} suite(s) still have NO runner (declared debt — see DEBT for the"
|
|
612
|
+
echo " per-entry reason and the date each was measured; they are not all from one measurement)."
|
|
613
|
+
echo " They are shipped or present but never execute — see DEBT in this file. This number must"
|
|
614
|
+
echo " only go down; a new one fails the check rather than joining the list silently."
|
|
615
|
+
fi
|
|
616
|
+
|
|
617
|
+
SELFTEST_COUNTS=$(printf '%s\n' "$out" | awk -F'\t' '$1=="SELFTEST_COUNTS"{print $2" "$3}')
|
|
618
|
+
set -- $SELFTEST_COUNTS
|
|
619
|
+
ST_TOTAL="${1:-0}"; ST_WIRED="${2:-0}"
|
|
620
|
+
SELFTEST_UNDECLARED=$(printf '%s\n' "$out" | awk -F'\t' '$1=="SELFTEST_UNDECLARED"{print $2}')
|
|
621
|
+
ST_UNDECLARED_N=0
|
|
622
|
+
if [ -n "$SELFTEST_UNDECLARED" ]; then
|
|
623
|
+
ST_UNDECLARED_N=$(printf '%s\n' "$SELFTEST_UNDECLARED" | wc -l | tr -d ' ')
|
|
624
|
+
# Advisory, not blocking — these are pre-existing (measured 2026-08-13, before this check saw
|
|
625
|
+
# them at all), not something introduced by whatever change is running this check right now.
|
|
626
|
+
# Same DEBT philosophy as above: loudly counted, never silent, must only go down from here.
|
|
627
|
+
echo "⚠️ lane-runner: ${ST_UNDECLARED_N} embedded --self-test subject(s) with no --self-test"
|
|
628
|
+
echo " dispatcher anywhere (self-test code exists, nothing calls it — see this file's own"
|
|
629
|
+
echo " §Embedded --self-test comment for why the suites glob above cannot see this class):"
|
|
630
|
+
printf '%s\n' "$SELFTEST_UNDECLARED" | sed 's/^/ /'
|
|
631
|
+
echo " Fix by wiring \`bash scripts/<name>.sh --self-test\` into scripts/selfcheck.sh's"
|
|
632
|
+
echo " _subj for-loop (scripts/selfcheck.sh:478), same shape as the 3 already there."
|
|
633
|
+
fi
|
|
634
|
+
|
|
635
|
+
echo "PASS lane-runner: ${TOTAL} suites — ${WIRED} wired · ${N_EXEMPT} exempt · ${N_DEBT} declared debt" \
|
|
636
|
+
"· self-test: ${ST_WIRED}/${ST_TOTAL} wired"
|
|
637
|
+
exit 0
|