@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.
@@ -9,12 +9,36 @@
9
9
  # Usage: bash scripts/test_marker_floor_lanes.sh Exit: 0 = all fixtures behave; 1 = regression.
10
10
 
11
11
  set -uo pipefail
12
- REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
12
+ # Script-relative, NOT `git rev-parse --show-toplevel`. Measured 2026-08-13 in a vendored tree
13
+ # (npm install, then `git init` at a level above — a monorepo committing node_modules is the
14
+ # same shape): rev-parse answers with the OUTER repo's root, so this suite looked for the
15
+ # package's own files inside somebody else's checkout, found nothing, and reported
16
+ # HARNESS-ERROR. The consumer sees a red `npm test` caused entirely by where their .git is.
17
+ # The subject of these lanes ships INSIDE this package, so the package root is the only root
18
+ # that can be right. Same form as test_capability_entrypoint_shipping.sh:29.
19
+ # The exposure is new: before these suites were wired into selfcheck.sh they ran nowhere, so
20
+ # the wrong root never cost anything. Wiring a dead lane surfaces every assumption it made.
21
+ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
13
22
  HOOK="$REPO_ROOT/templates/.git-hooks/pre-commit"
14
23
  T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
15
24
 
16
25
  sed -n '/^marker_recreate_hint()/,/^}/p;/^validate_marker_floor()/,/^}/p' "$HOOK" > "$T/fn.sh"
17
26
 
27
+ # EXTRACTION CALIBRATION — the sibling suite (test_marker_crossfamily_lanes.sh) has had this and
28
+ # this file did not. Without it, ANY failure to extract renders as a REGRESSION rather than as a
29
+ # broken instrument: an empty fn.sh means `run()` sources nothing, validate_marker_floor is
30
+ # undefined, `bash -c` exits non-zero, every fixture reads as BLOCK, and the three lanes that
31
+ # expect PASS "fail" — a red suite pointing at a gate that is perfectly fine. Measured 2026-08-13
32
+ # in a vendored git tree, where the old `git rev-parse` root sent $HOOK into another repo entirely.
33
+ # The root is fixed above; this guard is what makes the NEXT extraction failure legible instead of
34
+ # a false accusation, whatever causes it (a renamed function, a reformatted hook, a truncated file).
35
+ # exit 10, matching the harness-error contract selfcheck.sh's pair-loop routes to HARNESS ERROR.
36
+ if ! grep -q '^validate_marker_floor()' "$T/fn.sh"; then
37
+ echo "🟥 HARNESS-ERROR: validate_marker_floor() did not extract from $HOOK"
38
+ echo " (this suite measured NOTHING — it is not a regression verdict)"
39
+ exit 10
40
+ fi
41
+
18
42
  run() { bash -c "source '$T/fn.sh'; validate_marker_floor '$1'" >/dev/null 2>&1; }
19
43
 
20
44
  FAIL=0
@@ -0,0 +1,288 @@
1
+ #!/usr/bin/env bash
2
+ # test_psa_singlefile_lanes.sh — known-pair lanes for the public-surface scanner's SINGLE-FILE and
3
+ # MISUSE paths.
4
+ #
5
+ # What these lanes exist to hold (all measured 2026-08-12, all found by a control and not by review):
6
+ # L1 `public_surface_scan_files.sh <path>` used to IGNORE the argument and print its normal green,
7
+ # so a caller asking "is THIS file clean?" got a PASS about a file set that never contained it.
8
+ # L3/L4 `psa_scan_tagged` piped from a shell without `cat` died on its first line and returned 0 —
9
+ # a known-positive scanned as 0 hits. "The scanner ran" and "the scanner said clean" are
10
+ # different claims and were indistinguishable.
11
+ # L7 a missing file returned 0 hits, i.e. `not found` rendered as `0`.
12
+ # L9 a display that greps only ❌ renders an ALLOWLISTED token (⚪) as "no match" — that is how an
13
+ # existing operator allowlist decision got misread as an absent pattern.
14
+ #
15
+ # HERMETIC BY CONSTRUCTION: every lane builds its own pattern/allowlist files under a temp dir. It
16
+ # must NOT read `.claude/rules/.public-surface-patterns` (gitignored, operator-private) — a lane that
17
+ # depends on an operator-local file is unrunnable on a fresh clone and in CI, and would silently
18
+ # degrade to "0 lanes ran" there, which is the same not-measured-is-not-zero shape these lanes guard.
19
+ #
20
+ # Verdict is the exit code. Never parse a summary line for it.
21
+
22
+ set -uo pipefail
23
+
24
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
25
+ LIB="$REPO_ROOT/scripts/psa_scan_lib.sh"
26
+ PUB="$REPO_ROOT/scripts/public_surface_scan_files.sh"
27
+ TMP=$(mktemp -d) || exit 9
28
+ trap 'rm -rf "$TMP"' EXIT
29
+
30
+ # HERMETIC BINDING (cross-family round 1 refuted the original claim of hermeticity): lanes that did
31
+ # not set PSA_ALLOWLIST fell through to the library default, which is the operator's GITIGNORED
32
+ # allowlist. A lane whose verdict depends on a file that does not exist on a fresh clone is not
33
+ # hermetic — it is silently a different test there. Bound once, for every lane.
34
+ export PSA_ALLOWLIST="$TMP/allowlist"
35
+ # Environment hermeticity (round 2): PUBLIC_SURFACE_OK is an operator override that converts the
36
+ # publish scanner's fail-closed exit into a proceed. Inherited from the caller's shell it silently
37
+ # flips L2's expected verdict — a lane whose result depends on an ambient env var is measuring the
38
+ # environment, not the code.
39
+ unset PUBLIC_SURFACE_OK
40
+
41
+ pass=0; fail=0
42
+ ok() { pass=$((pass+1)); printf ' ✅ %s\n' "$1"; }
43
+ bad() { fail=$((fail+1)); printf ' ❌ %s\n' "$1"; }
44
+ # check <name> <expected_rc> <actual_rc> [substring_that_must_appear] [output]
45
+ check() {
46
+ local name="$1" want="$2" got="$3" need="${4:-}" out="${5:-}"
47
+ if [ "$got" != "$want" ]; then bad "$name — rc want=$want got=$got"; return; fi
48
+ if [ -n "$need" ]; then
49
+ case "$out" in *"$need"*) ;; *) bad "$name — rc ok but output lacks '$need'"; return ;; esac
50
+ fi
51
+ ok "$name"
52
+ }
53
+
54
+ # ── fixtures ────────────────────────────────────────────────────────────────────────────────────
55
+ printf 'HIGH\tPSA_LANE_SECRET\nHIGH\tPSA_LANE_ALLOWED\n' > "$TMP/defaults"
56
+ printf '# empty operator override is still "present + non-empty" for these lanes\nLOW\tPSA_LANE_LOW\n' > "$TMP/override"
57
+ printf 'fixtures/allowed.md\tPSA_LANE_ALLOWED\n' > "$TMP/allowlist"
58
+ mkdir -p "$TMP/fixtures"
59
+ printf 'harmless line\ncontact PSA_LANE_SECRET here\n' > "$TMP/fixtures/positive.md"
60
+ printf 'nothing to see\njust prose\n' > "$TMP/fixtures/negative.md"
61
+ printf 'this names PSA_LANE_ALLOWED on purpose\n' > "$TMP/fixtures/allowed.md"
62
+
63
+ echo "[psa single-file lanes]"
64
+
65
+ # ── L1/L2 : misuse of the publish scanner fails CLOSED, and the no-arg path is untouched ────────
66
+ out=$("$PUB" /nonexistent/definitely_not_a_file_zzz.md 2>&1); rc=$?
67
+ check "L1 publish-scanner: positional arg → rc=2 refuse" 2 "$rc" "USAGE ERROR" "$out"
68
+
69
+ # Control for L1: prove the guard did NOT swallow the normal no-arg path. A deliberately unresolvable
70
+ # pattern source makes the no-arg run exit at the instrument check (rc=1) BEFORE `npm pack`, so this
71
+ # stays fast and still proves the run got past the argument guard.
72
+ # Cross-family round 1: the first version only rejected rc=2, so a scanner that wrongly exited 0 PASS
73
+ # on an unresolvable pattern source would still have shown green. A control that cannot fail in the
74
+ # direction you care about is not a control. It now demands the specific fail-closed outcome.
75
+ out=$(PSA_PATTERNS=/nonexistent/nope "$PUB" 2>&1); rc=$?
76
+ check "L2 publish-scanner: no-arg path reaches the instrument check and fails closed" 1 "$rc" "incomplete confidentiality instrument" "$out"
77
+
78
+ # ── L3/L4 : the liveness self-test separates "ran and found nothing" from "never ran" ────────────
79
+ out=$(
80
+ . "$LIB"; psa_load "$TMP/defaults" "$TMP/override" >/dev/null 2>&1
81
+ psa_require_live 2>&1
82
+ ); rc=$?
83
+ check "L3 psa_require_live: healthy shell → alive" 0 "$rc"
84
+
85
+ # The reproduction: a shell whose PATH has no `cat`. psa_scan_tagged dies on its first line and would
86
+ # otherwise return 0 — which is what made a known-positive read as clean.
87
+ out=$(/usr/bin/env -i PATH=/nonexistent/bin /bin/bash -c "
88
+ . '$LIB'; psa_load '$TMP/defaults' '$TMP/override' >/dev/null 2>&1
89
+ psa_require_live
90
+ " 2>&1); rc=$?
91
+ check "L4 psa_require_live: PATH without cat → DEAD, not clean" 1 "$rc" "INSTRUMENT DEAD" "$out"
92
+
93
+ # ── L5/L6 : ordinary single-file verdicts ───────────────────────────────────────────────────────
94
+ out=$(
95
+ . "$LIB"; psa_load "$TMP/defaults" "$TMP/override" >/dev/null 2>&1
96
+ PSA_ALLOWLIST="$TMP/allowlist" psa_scan_file "$TMP/fixtures/positive.md" 2>&1
97
+ ); rc=$?
98
+ check "L5 psa_scan_file: known-positive → rc=1 + reports the token" 1 "$rc" "PSA_LANE_SECRET" "$out"
99
+
100
+ out=$(
101
+ . "$LIB"; psa_load "$TMP/defaults" "$TMP/override" >/dev/null 2>&1
102
+ PSA_ALLOWLIST="$TMP/allowlist" psa_scan_file "$TMP/fixtures/negative.md" 2>&1
103
+ ); rc=$?
104
+ check "L6 psa_scan_file: known-negative → rc=0" 0 "$rc"
105
+
106
+ # ── L7/L8 : unmeasured is its own value, never 0 ────────────────────────────────────────────────
107
+ out=$(
108
+ . "$LIB"; psa_load "$TMP/defaults" "$TMP/override" >/dev/null 2>&1
109
+ psa_scan_file "$TMP/fixtures/does_not_exist.md" 2>&1
110
+ ); rc=$?
111
+ check "L7 psa_scan_file: missing file → rc=3 NOT SCANNED (not 0)" 3 "$rc" "NOT SCANNED" "$out"
112
+
113
+ out=$(
114
+ . "$LIB"; psa_load "$TMP/defaults" "$TMP/override" >/dev/null 2>&1
115
+ psa_scan_file 2>&1
116
+ ); rc=$?
117
+ check "L8 psa_scan_file: no argument → rc=3 usage (not 0)" 3 "$rc" "USAGE" "$out"
118
+
119
+ # ── L9 : allowlisted (⚪) is a THIRD outcome and must remain visible ─────────────────────────────
120
+ # This is the regression anchor for the display-collapse: rc is 0 like a clean file, so a caller that
121
+ # only inspects rc — or greps only ❌ — cannot tell "an operator decided to permit this token here"
122
+ # from "this pattern never matched". The ⚪ line is the only thing that separates them.
123
+ out=$(
124
+ cd "$TMP" || exit 9
125
+ . "$LIB"; psa_load "$TMP/defaults" "$TMP/override" >/dev/null 2>&1
126
+ PSA_ALLOWLIST="$TMP/allowlist" psa_scan_file "fixtures/allowed.md" 2>&1
127
+ ); rc=$?
128
+ check "L9 psa_scan_file: allowlisted token → rc=0 AND an explicit ⚪ line" 0 "$rc" "⚪ allowlisted" "$out"
129
+
130
+ # L9b asserts the ABSENCE of a ❌ — and an absence assertion passes for free when nothing ran at all.
131
+ # Caught by the revert probe on 2026-08-12: against the pre-fix tree `psa_scan_file` did not exist,
132
+ # the output was a shell "command not found", there was no ❌ in it, and L9b went green. A lane that
133
+ # is green because the subject never executed is decorative — the same `not found ≠ 0` shape these
134
+ # lanes were written to hold, reproduced inside the lane file. So the absence claim is gated on
135
+ # positive evidence that the scan actually produced its allowlist verdict.
136
+ case "$out" in
137
+ *"⚪ allowlisted"*)
138
+ case "$out" in
139
+ *"❌"*) bad "L9b allowlisted token must not also report ❌" ;;
140
+ *) ok "L9b allowlisted token reports no ❌ (and the ⚪ verdict proves the scan ran)" ;;
141
+ esac ;;
142
+ *) bad "L9b UNMEASURED — no ⚪ verdict in the output, so 'no ❌' proves nothing" ;;
143
+ esac
144
+
145
+ # ── L10 : patterns not loaded is NOT clean ──────────────────────────────────────────────────────
146
+ # Reproduced from this repo's own advertised usage line, which omitted psa_load. An empty pattern
147
+ # stream matches nothing and reports nothing — identical output to a clean file.
148
+ out=$(
149
+ . "$LIB"
150
+ psa_scan_file "$TMP/fixtures/positive.md" 2>&1
151
+ ); rc=$?
152
+ check "L10 psa_scan_file: patterns never loaded → rc=3 NOT SCANNED (not clean)" 3 "$rc" "PATTERNS NOT LOADED" "$out"
153
+
154
+ # ── L11 : liveness covers the FILE path, not just stdin (missing awk) ────────────────────────────
155
+ # The tagging step needs awk; the first liveness draft only exercised the stdin path, so "alive"
156
+ # certified a pipeline the file scan does not use.
157
+ mkdir -p "$TMP/bin"
158
+ for b in cat grep mktemp rm printf sed; do
159
+ src=$(command -v "$b" 2>/dev/null) && ln -sf "$src" "$TMP/bin/$b" 2>/dev/null
160
+ done
161
+ out=$(/usr/bin/env -i PATH="$TMP/bin" /bin/bash -c "
162
+ . '$LIB'
163
+ PSA_STREAM=\$(printf 'HIGH\tPSA_LANE_SECRET')
164
+ PSA_ALLOWLIST=/dev/null
165
+ psa_require_live
166
+ " 2>&1); rc=$?
167
+ check "L11 psa_require_live: PATH without awk → DEAD (file path is covered)" 1 "$rc" "INSTRUMENT DEAD" "$out"
168
+
169
+ # ── L12 : errexit safety — the library must not kill its caller ─────────────────────────────────
170
+ # The library's documented contract is that it REPORTS state and never exits. Capturing a nonzero
171
+ # status outside an `if` broke that under `set -e`.
172
+ out=$(bash -c "
173
+ . '$LIB'; psa_load '$TMP/defaults' '$TMP/override' >/dev/null 2>&1
174
+ set -e
175
+ psa_require_live
176
+ echo SURVIVED
177
+ " 2>&1); rc=$?
178
+ check "L12 psa_require_live: safe under set -e (caller survives)" 0 "$rc" "SURVIVED" "$out"
179
+
180
+ # ── L13 : a NON-EMPTY pattern stream is not a COMPLETE one ──────────────────────────────────────
181
+ printf 'HIGH\tPSA_ONLY_IN_DEFAULTS\n' > "$TMP/defaults2"
182
+ printf 'default-only token PSA_ONLY_IN_DEFAULTS here\n' > "$TMP/fixtures/partial.md"
183
+ out=$(
184
+ . "$LIB"; psa_load "$TMP/missing_defaults_file" "$TMP/override" >/dev/null 2>&1
185
+ psa_scan_file "$TMP/fixtures/partial.md" 2>&1
186
+ ); rc=$?
187
+ check "L13 psa_scan_file: defaults failed to load → rc=3 (partial instrument is not clean)" 3 "$rc" "INCOMPLETE PATTERN INSTRUMENT" "$out"
188
+
189
+ # ── L14 : missing operator override is NOT a clean ───────────────────────────────────────────────
190
+ # Flipped in round 4. It used to warn and return 0; the override is where the HIGH company/companion
191
+ # literals live, so without it the highest-severity class was never looked at. "Warned but clean" is
192
+ # the false-clean shape with a comment attached.
193
+ out=$(
194
+ . "$LIB"; psa_load "$TMP/defaults" "$TMP/missing_override_file" >/dev/null 2>&1
195
+ psa_scan_file "$TMP/fixtures/negative.md" 2>&1
196
+ ); rc=$?
197
+ check "L14 psa_scan_file: override absent → rc=3 (HIGH literals unscanned is not clean)" 3 "$rc" "override_absent" "$out"
198
+
199
+ # ── L15 : a GARBAGE guard value must not walk past the guard ────────────────────────────────────
200
+ # Round 3: `[ "$PSA_DEFAULTS_OK" -ne 1 ]` with a non-numeric value returns 2, the `if` reads that as
201
+ # false, and the guard falls through — measured rc=0 CLEAN on a file whose token was not even in the
202
+ # loaded stream. The shell printed "integer expression expected" and nothing consumed it.
203
+ out=$(
204
+ . "$LIB"
205
+ PSA_STREAM=$(printf 'HIGH\tSOMETHING_ELSE'); PSA_DEFAULTS_OK=x; PSA_BAD_ROWS=0; PSA_OVERRIDE_PRESENT=1
206
+ psa_scan_file "$TMP/fixtures/positive.md" 2>&1
207
+ ); rc=$?
208
+ check "L15 psa_scan_file: garbage PSA_DEFAULTS_OK → rc=3 (guard is not walked past)" 3 "$rc" "INCOMPLETE PATTERN INSTRUMENT" "$out"
209
+
210
+ # ── L16 : an incomplete instrument still REPORTS what it saw ─────────────────────────────────────
211
+ # Round 3 caught the round-2 fix suppressing real hits: the guard returned 3 and emitted nothing, so a
212
+ # token the loaded patterns DID match was lost. "I cannot certify this" must not become "I saw nothing".
213
+ out=$(
214
+ . "$LIB"
215
+ PSA_STREAM=$(printf 'HIGH\tPSA_LANE_SECRET'); PSA_DEFAULTS_OK=0; PSA_BAD_ROWS=0; PSA_OVERRIDE_PRESENT=1
216
+ psa_scan_file "$TMP/fixtures/positive.md" 2>&1
217
+ ); rc=$?
218
+ check "L16 incomplete instrument still reports the hit it saw (verdict 3, evidence kept)" 3 "$rc" "PSA_LANE_SECRET" "$out"
219
+
220
+ # ── L17 : readonly caller vars → DEAD, not a corrupted abort ─────────────────────────────────────
221
+ # Round 4 refuted the round-3 `|| :`: bash aborts on assignment to a readonly variable before `||` is
222
+ # considered. The function now refuses BEFORE mutating anything.
223
+ # Scope note, stated because the round-3 claim over-reached: under `set -e` a bare call to a function
224
+ # that RETURNS nonzero still exits the caller — that is correct shell semantics, not a defect. What is
225
+ # fixed is dying mid-mutation with the caller's state half-swapped and no diagnostic. So the lane
226
+ # checks the guarded call, which is how a `set -e` caller is supposed to invoke a fallible function.
227
+ out=$(bash -c "
228
+ . '$LIB'
229
+ PSA_STREAM=x; readonly PSA_STREAM
230
+ set -e
231
+ if psa_require_live; then echo UNEXPECTED_ALIVE; else echo REFUSED_CLEANLY; fi
232
+ echo SURVIVED
233
+ " 2>&1); rc=$?
234
+ check "L17 readonly PSA_STREAM → refuses without mutating; guarded set -e caller survives" 0 "$rc" "SURVIVED" "$out"
235
+ case "$out" in *"INSTRUMENT DEAD"*) ok "L17b the refusal is diagnosed, not silent" ;; *) bad "L17b refusal produced no INSTRUMENT DEAD line" ;; esac
236
+
237
+ # ── L18/L19/L21 : the assign guard, in BOTH directions + the ATTRIBUTE case ─────────────────────────────────────────────
238
+ # L17's fixture used `PSA_STREAM=x; readonly PSA_STREAM` — the one spelling the round-4 glob happened
239
+ # to handle. Round 5 found the other: `readonly PSA_STREAM` with no value prints `declare -r PSA_STREAM`
240
+ # (no `=`), the glob missed it, and the caller died with a bare "readonly variable". A control that
241
+ # only exercises the passing spelling measures the fixture, not the guard.
242
+ out=$(bash -c "
243
+ . '$LIB'
244
+ readonly PSA_STREAM
245
+ set -e
246
+ if psa_require_live >/dev/null 2>&1; then echo ALIVE; else echo REFUSED; fi
247
+ echo SURVIVED
248
+ " 2>&1); rc=$?
249
+ check "L18 VALUELESS readonly is detected too (guard tests the property, not the declaration)" 0 "$rc" "REFUSED" "$out"
250
+ case "$out" in *SURVIVED*) ok "L18b caller survives the valueless case" ;; *) bad "L18b caller died on valueless readonly" ;; esac
251
+
252
+ # The other direction: the round-4 glob also matched an unrelated readonly variable whose VALUE
253
+ # contained the string. Over-blocking here would make every such shell report a healthy scanner dead.
254
+ out=$(bash -c "
255
+ . '$LIB'; psa_load '$TMP/defaults' '$TMP/override' >/dev/null 2>&1
256
+ readonly PSA_UNRELATED='PSA_STREAM=junk PSA_ALLOWLIST=junk'
257
+ if psa_require_live >/dev/null 2>&1; then echo ALIVE; else echo REFUSED; fi
258
+ " 2>&1); rc=$?
259
+ check "L19 unrelated readonly whose VALUE names the vars → no false positive" 0 "$rc" "ALIVE" "$out"
260
+
261
+ # ── L20 : the readonly helper is not a shell-injection surface ──────────────────────────────────
262
+ # Round 6. Reachable only via a non-literal argument, which no current call site passes — but the
263
+ # function is in the instrument that decides what may be published, and the guard is one line.
264
+ rm -f "$TMP/pwned"
265
+ out=$(bash -c ". '$LIB'; _psa_can_assign 'X; touch $TMP/pwned; #' v; echo rc=\$?" 2>&1); rc=$?
266
+ if [ -f "$TMP/pwned" ]; then bad "L20 eval injection — the payload executed"
267
+ else ok "L20 non-identifier argument is refused before eval (no injection)"; fi
268
+ case "$out" in *"refusing non-identifier"*) ok "L20b the refusal is diagnosed" ;; *) bad "L20b refusal was silent" ;; esac
269
+
270
+ # ── L21 : an ATTRIBUTE that rejects the value, not just readonly ─────────────────────────────────
271
+ # Round 7. The R5 probe assigned the variable's OWN CURRENT VALUE while the caller then assigns
272
+ # something else. Measured: `declare -i PSA_ALLOWLIST` → self-assignment succeeds, `=/dev/null` fails.
273
+ # The probe said writable, the real assignment killed the caller, and on the psa_scan_file path a hit
274
+ # psa_scan_tagged WOULD have reported went with it. The probe now takes the value it will assign.
275
+ out=$(bash -c "
276
+ . '$LIB'
277
+ declare -i PSA_ALLOWLIST
278
+ set -e
279
+ if psa_require_live >/dev/null 2>&1; then echo ALIVE; else echo REFUSED; fi
280
+ echo SURVIVED
281
+ " 2>&1); rc=$?
282
+ check "L21 declare -i (attribute rejects the value) → REFUSED, caller survives" 0 "$rc" "REFUSED" "$out"
283
+ case "$out" in *SURVIVED*) ok "L21b caller survives the attribute case" ;; *) bad "L21b caller died on the attribute case" ;; esac
284
+
285
+ echo "[psa single-file lanes] pass=$pass fail=$fail"
286
+ [ "$fail" -eq 0 ] || exit 1
287
+ [ "$pass" -ge 26 ] || { echo " ❌ INSTRUMENT ERROR — only $pass lanes ran; expected >=26"; exit 3; }
288
+ exit 0
@@ -40,7 +40,16 @@
40
40
  # Usage: bash scripts/test_reviewer_capability_conformance.sh Exit: 0 = conforms; 1 = drift.
41
41
 
42
42
  set -uo pipefail
43
- REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
43
+ # Script-relative, NOT `git rev-parse --show-toplevel`. Measured 2026-08-13 in a vendored tree
44
+ # (npm install, then `git init` at a level above — a monorepo committing node_modules is the
45
+ # same shape): rev-parse answers with the OUTER repo's root, so this suite looked for the
46
+ # package's own files inside somebody else's checkout, found nothing, and reported
47
+ # HARNESS-ERROR. The consumer sees a red `npm test` caused entirely by where their .git is.
48
+ # The subject of these lanes ships INSIDE this package, so the package root is the only root
49
+ # that can be right. Same form as test_capability_entrypoint_shipping.sh:29.
50
+ # The exposure is new: before these suites were wired into selfcheck.sh they ran nowhere, so
51
+ # the wrong root never cost anything. Wiring a dead lane surfaces every assumption it made.
52
+ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
44
53
  CORPUS="${CORPUS:-$REPO_ROOT/scripts/reviewer_capability_corpus.tsv}"
45
54
  HOOK="$REPO_ROOT/templates/.git-hooks/pre-commit"
46
55
  LENIENT="${ALLOW_UNDECIDABLE_AS_INCAPABLE:-0}"
@@ -184,6 +184,179 @@ _OUT=$(_show_failure "$_MANY")
184
184
  [ "$(printf '%s\n' "$_OUT" | grep -c '════ lanes: 9 passed')" -ge 1 ]
185
185
  chk $? "the summary banner still prints when a truncated-away line quotes it"
186
186
 
187
+ # ── ref-path + SessionStart-pair: `.git` presence is not "is this absence declared OK" ─────────
188
+ # WHY (2026-08-12, cross-family review, card §🔱⑮ G): both blocks used to key their package-mode
189
+ # SKIP on `[ -e .git ]`. A git-TRACKED tree that vendors this package (a monorepo committing
190
+ # node_modules, or a consumer who runs `git init` after install) has `.git` present and is still a
191
+ # package consumer — the predicate answered "source checkout" for it anyway, ran the full check, and
192
+ # reproduced the exact FAIL both blocks exist to prevent. The fix routes through a single declaration
193
+ # (`_pkg_accepted_absent`, backed by package_coverage_check.sh's ACCEPTED_ABSENT) instead. Anchored
194
+ # here because this file is the state-lanes suite and an adversarial read is what caught the first
195
+ # version of this same fix landing without a regression lane.
196
+ _PA_FN=$(grep -n '^_pkg_accepted_absent()' "$SELFCHECK")
197
+ if [ -z "$_PA_FN" ]; then
198
+ echo "FAIL _pkg_accepted_absent() is no longer defined in selfcheck.sh — this lane cannot verify"
199
+ echo " what it claims to. Update the lane WITH the subject."
200
+ exit 1
201
+ fi
202
+ echo "── discriminator located: selfcheck.sh:${_PA_FN%%:*}"
203
+
204
+ # route_refpath(git_present, path_exists, is_accepted_absent) mirrors the real per-path decision
205
+ # inside the `if [ -e ".git" ]` block: no .git → the whole scan is skipped regardless of any single
206
+ # path's state (package mode); .git present → PASS if the path exists, else consult the declaration.
207
+ route_refpath() {
208
+ local git="$1" exists="$2" accepted="$3"
209
+ if [ "$git" = 0 ]; then echo SKIP-package; return; fi
210
+ if [ "$exists" = 1 ]; then echo PASS; return; fi
211
+ if [ "$accepted" = 1 ]; then echo SKIP-accepted; return; fi
212
+ echo FAIL
213
+ }
214
+ # route_refpath_OLD is the reverted predicate: package-mode SKIP gated on .git alone, with no
215
+ # fallback to the declaration when .git is present and the path is genuinely missing.
216
+ route_refpath_OLD() {
217
+ local git="$1" exists="$2"
218
+ if [ "$git" = 0 ]; then echo SKIP-package; return; fi
219
+ if [ "$exists" = 1 ]; then echo PASS; return; fi
220
+ echo FAIL
221
+ }
222
+ [ "$(route_refpath 0 0 1)" = SKIP-package ] ; chk $? "no .git → SKIP regardless of the individual path"
223
+ [ "$(route_refpath 1 1 0)" = PASS ] ; chk $? "source tree, path exists → PASS"
224
+ [ "$(route_refpath 1 0 0)" = FAIL ] ; chk $? "source tree, path genuinely missing, not declared → FAIL (a real defect still fails)"
225
+ [ "$(route_refpath 1 0 1)" = SKIP-accepted ] ; chk $? "known-POSITIVE (the bug this fix closes): .git present (vendored/tracked package) + path missing + DECLARED accepted-absent → SKIP, not FAIL"
226
+ [ "$(route_refpath_OLD 1 0)" = FAIL ] ; chk $? "known-POSITIVE control: the reverted .git-only predicate DOES mis-route that same state to FAIL (the bug is real, not theoretical)"
227
+
228
+ # The two paths this fix was written for must actually be present in the declaration it now consults
229
+ # — otherwise the lanes above prove the routing logic works while the real inputs still fall through it.
230
+ _PA_LIST=$(bash "$SCRIPT_DIR/package_coverage_check.sh" --list-accepted 2>/dev/null)
231
+ printf '%s\n' "$_PA_LIST" | grep -qxF ".claude/regression/ablation_verdicts.md"
232
+ chk $? "premise holds: .claude/regression/ablation_verdicts.md is in the live ACCEPTED_ABSENT list"
233
+ printf '%s\n' "$_PA_LIST" | grep -qxF "scripts/probe_scope_check.sh"
234
+ chk $? "premise holds: scripts/probe_scope_check.sh is in the live ACCEPTED_ABSENT list"
235
+ printf '%s\n' "$_PA_LIST" | grep -qxF "scripts/test_sessionstart_multihook_lanes.sh"
236
+ chk $? "premise holds: scripts/test_sessionstart_multihook_lanes.sh is in the live ACCEPTED_ABSENT list"
237
+
238
+ # The SessionStart-pair loop's mode field: unrecognized values must FAIL closed, not fall through to
239
+ # whichever string-match branch happens to miss (codex cross-family finding, 2026-08-12).
240
+ _MODE_CASE=$(grep -n 'package-optional|always-shipped) ;;' "$SELFCHECK")
241
+ if [ -z "$_MODE_CASE" ]; then
242
+ echo "FAIL the pair-mode validation case is no longer in selfcheck.sh — this lane cannot verify"
243
+ echo " what it claims to. Update the lane WITH the subject."
244
+ exit 1
245
+ fi
246
+ route_pair_mode() {
247
+ case "$1" in
248
+ package-optional|always-shipped) echo VALID ;;
249
+ *) echo FAIL-badmode ;;
250
+ esac
251
+ }
252
+ [ "$(route_pair_mode package-optional)" = VALID ] ; chk $? "known mode 'package-optional' validates"
253
+ [ "$(route_pair_mode always-shipped)" = VALID ] ; chk $? "known mode 'always-shipped' validates"
254
+ [ "$(route_pair_mode alway-shipped)" = FAIL-badmode ] ; chk $? "known-POSITIVE: a typo'd mode is rejected, not silently treated as either known state"
255
+
256
+ # ── 삭제→SKIP: the fourth face. A declared-shipped subject's absence is not "package mode" ─────
257
+ # WHY (measured 2026-08-12, card §🔱⑮ A2): **18 blocks** in selfcheck.sh rendered a green SKIP when
258
+ # their subject was missing, and all 18 subjects are in package.json files[] AND in the real tarball
259
+ # (`npm pack --dry-run --json`, 20/20 — so routing them to FAIL cannot over-block a real consumer).
260
+ # For an always-shipped subject, "absent" can only mean deletion or a broken install. Two of the 18
261
+ # read as already-handled and were not: the gate_pathspec block printed "— not-checked, NOT a pass"
262
+ # without setting fail, and the --self-test loop's comment claims "never a silent pass" directly above
263
+ # the arm that was one. A comment asserting a property is not the property; both were hand-verified.
264
+ # LIFTED, not re-spelled — same reason as the discriminators above.
265
+ _ASV=$(sed -n '/^_absent_subject_verdict()/,/^}/p' "$SELFCHECK")
266
+ _SPF=$(sed -n '/^_ships_per_files()/,/^}/p' "$SELFCHECK")
267
+ if [ -z "$_ASV" ] || [ -z "$_SPF" ]; then
268
+ echo "FAIL _absent_subject_verdict / _ships_per_files no longer defined in selfcheck.sh —"
269
+ echo " this lane cannot verify what it claims. Update the lane WITH the subject."
270
+ exit 1
271
+ fi
272
+ echo "── 삭제→SKIP discriminator located (both helpers present)"
273
+
274
+ _asv() { # $1=subject → prints verdict, returns its rc
275
+ ( cd "$SCRIPT_DIR/.." && bash -c "
276
+ $_SPF
277
+ $_ASV
278
+ _absent_subject_verdict 'lane' '$1'" 2>&1 )
279
+ }
280
+ _asv_rc() { _asv "$1" >/dev/null 2>&1; echo $?; }
281
+
282
+ # known-POSITIVE: a declared-shipped subject going missing must FAIL, not SKIP. Three different
283
+ # shapes (a script, a hook under a directory files[] entry, an anchor's subject).
284
+ [ "$(_asv_rc scripts/count_check.sh)" = 1 ]
285
+ chk $? "known-POSITIVE: declared-shipped script absent → FAIL (was a green SKIP for 18 blocks)"
286
+ [ "$(_asv_rc templates/.git-hooks/pre-commit)" = 1 ]
287
+ chk $? "known-POSITIVE: hook covered by a DIRECTORY files[] entry → FAIL (prefix match, not equality)"
288
+ # ⚠️ Capture, THEN grep. `_asv` deliberately returns 1 here, and this file runs under `pipefail`, so
289
+ # `_asv … | grep -q …` yields the pipeline's rightmost non-zero — i.e. the assertion failed while
290
+ # grep had actually matched. Measured on this very lane's first run
291
+ # ([[feedback_pipefail_fallback_disarms_guard]]: `out=$(cmd); rc=$?` is the canonical form).
292
+ _asv_out=$(_asv scripts/count_check.sh)
293
+ printf '%s' "$_asv_out" | grep -q 'DECLARED SHIPPED'
294
+ chk $? "…and the message names WHY, so the verdict is diagnosable"
295
+
296
+ # known-NEGATIVE: an ACCEPTED_ABSENT subject must still SKIP. Without this arm the fix would be an
297
+ # over-block, which trains --no-verify — the trade this repo has logged explicitly.
298
+ [ "$(_asv_rc scripts/probe_scope_check.sh)" = 0 ]
299
+ chk $? "known-NEGATIVE: genuinely-unshipped subject absent → SKIP (no over-block)"
300
+ [ "$(_asv_rc scripts/sync-to-be.sh)" = 0 ]
301
+ chk $? "known-NEGATIVE: operator-private subject absent → SKIP"
302
+
303
+ # The UNKNOWN arm: an unreadable manifest cannot tell deletion from package mode, so it must NOT
304
+ # take the lenient branch. `not found != 0` applied to the oracle itself.
305
+ _TD=$(mktemp -d)
306
+ _urc=$( ( cd "$_TD" && bash -c "
307
+ $_SPF
308
+ $_ASV
309
+ _absent_subject_verdict 'lane' 'scripts/x.sh'" >/dev/null 2>&1 ); echo $? )
310
+ rm -rf "$_TD"
311
+ [ "$_urc" = 1 ]
312
+ chk $? "UNKNOWN (no readable package.json) → FAIL, not a lenient SKIP"
313
+
314
+ # REVERSION: every one of the 18 call sites must route through the helper. A site that re-spells the
315
+ # old `echo "SKIP … absent"` without setting fail has restored the defect. Counting call sites rather
316
+ # than grepping for the old string, because the old string is what a reverter would reproduce.
317
+ _ASV_CALLS=$(grep -c '_absent_subject_verdict "' "$SELFCHECK" || true)
318
+ [ "$_ASV_CALLS" -ge 18 ]
319
+ chk $? "all 18 absent-subject sites route through the helper (found $_ASV_CALLS, expected >=18)"
320
+ # And no site may print a bare absent-SKIP for a subject that IS declared shipped.
321
+ # ⚠️ The first draft asserted ZERO bare absent-SKIP echoes and failed with 5 — all five legitimate:
322
+ # the helper's own SKIP arm, plus four subjects that genuinely never ship (probe_scope_check.sh,
323
+ # ablation_calibrate.sh, sync-to-be.sh, sync-from-be.sh — all ACCEPTED_ABSENT). A green SKIP is
324
+ # CORRECT there, and an assertion that forbids it would have forced an over-block. So the predicate
325
+ # is the same one the fix uses — is the named subject declared shipped — not a count that would need
326
+ # hand-tuning every time an exception is added ([[feedback_control_presence_is_not_discrimination]]:
327
+ # a control that fires on everything measures nothing).
328
+ _BARE=$( ( cd "$SCRIPT_DIR/.." && python3 - <<'BAREPY'
329
+ import re, json
330
+ sc = open('scripts/selfcheck.sh', encoding='utf-8').read().split('\n')
331
+ files = json.load(open('package.json'))['files']
332
+ def ships(p): return any(p == f or p.startswith(f.rstrip('/') + '/') for f in files)
333
+ bad = []
334
+ for i, l in enumerate(sc, 1):
335
+ if '_absent_subject_verdict' in l: # the helper itself, and its call sites
336
+ continue
337
+ m = re.search(r'echo "SKIP\s+.*subject ([A-Za-z0-9_./-]+) absent', l)
338
+ if m and ships(m.group(1)):
339
+ bad.append(f"{i}:{m.group(1)}")
340
+ print(' '.join(bad))
341
+ BAREPY
342
+ ) )
343
+ [ -z "$_BARE" ]
344
+ chk $? "no bare absent-SKIP remains for a DECLARED-SHIPPED subject (found: ${_BARE:-none})"
345
+ # Control for the line above: the predicate must be able to FIRE. If ships() ever returns False for
346
+ # everything (unreadable manifest, changed schema), the assertion passes vacuously and certifies
347
+ # nothing — so prove the same detector flags a planted declared-shipped subject.
348
+ _BARE_CTL=$( ( cd "$SCRIPT_DIR/.." && python3 - <<'CTLPY'
349
+ import re, json
350
+ files = json.load(open('package.json'))['files']
351
+ def ships(p): return any(p == f or p.startswith(f.rstrip('/') + '/') for f in files)
352
+ planted = 'echo "SKIP planted (subject scripts/count_check.sh absent)"'
353
+ m = re.search(r'echo "SKIP\s+.*subject ([A-Za-z0-9_./-]+) absent', planted)
354
+ print('FIRED' if (m and ships(m.group(1))) else 'DEAD')
355
+ CTLPY
356
+ ) )
357
+ [ "$_BARE_CTL" = FIRED ]
358
+ chk $? "CONTROL: that detector fires on a planted declared-shipped subject (got $_BARE_CTL)"
359
+
187
360
  # ── SCOPE OF THIS ANCHOR — stated so it is not over-trusted ───────────────────
188
361
  # These lanes catch REVERSION (the run-twice form coming back, the helper being gutted, the
189
362
  # call-site redirect returning). They do NOT catch deliberate EVASION: a cross-family round
@@ -0,0 +1,30 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "WebSearch(*)",
5
+ "WebFetch(*)",
6
+ "Read(**)",
7
+ "Bash(find *)",
8
+ "Bash(ls *)",
9
+ "Bash(mkdir*)",
10
+ "Bash(date*)",
11
+ "Bash(grep *)"
12
+ ]
13
+ },
14
+ "enabledPlugins": {
15
+ "frontend-design@claude-plugins-official": true
16
+ },
17
+ "hooks": {
18
+ "Stop": [
19
+ {
20
+ "matcher": "",
21
+ "hooks": [
22
+ {
23
+ "type": "command",
24
+ "command": "bash -c 'f=\".claude/goal-quench.active\"; [ -f \"$f\" ] && echo \"\\n[goal-quench] /goal finished. Running quality verification...\" && cp \"$f\" \".claude/goal-quench.pending\" && rm -f \"$f\" || true'"
25
+ }
26
+ ]
27
+ }
28
+ ]
29
+ }
30
+ }