@chrono-meta/fh-gate 1.4.95 → 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.
- package/.claude-plugin/marketplace.json +2 -2
- package/AGENTS.md +18 -0
- package/CHEATSHEET.md +1 -1
- package/CLAUDE.md +11 -4
- package/README.md +19 -0
- package/knowledge/shared/harness-core/fh_detail_protocols.md +12 -0
- package/knowledge/shared/harness-core/ship_readiness_gate.md +7 -4
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +113 -1
- package/package.json +10 -2
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-commons/agents/quench-challenger.md +49 -23
- package/plugins/fh-commons/skills/convergence-loop/SKILL.md +14 -0
- package/plugins/fh-commons/skills/deliberation/SKILL.md +14 -0
- package/plugins/fh-commons/skills/mcp-circuit-breaker/SKILL.md +10 -1
- package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/CHANGELOG.md +36 -0
- package/plugins/fh-meta/agents/beginner.md +4 -1
- package/plugins/fh-meta/agents/challenger.md +7 -1
- package/plugins/fh-meta/agents/expert.md +1 -1
- package/plugins/fh-meta/agents/fact-checker.md +7 -1
- package/plugins/fh-meta/agents/hub-persona-auditor.md +2 -1
- package/plugins/fh-meta/agents/main-player.md +4 -1
- package/plugins/fh-meta/agents/persona-innovator.md +10 -2
- package/plugins/fh-meta/skills/agent-composer/SKILL.md +2 -2
- package/plugins/fh-meta/skills/apex-review/SKILL.md +5 -0
- package/plugins/fh-meta/skills/asset-placement-gate/SKILL.md +38 -8
- package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +16 -2
- package/plugins/fh-meta/skills/context-doctor/SKILL_detail.md +45 -10
- package/plugins/fh-meta/skills/corpus-grounding-expander/SKILL.md +14 -5
- package/plugins/fh-meta/skills/cross-ecosystem-synergy-detection/SKILL.md +93 -30
- package/plugins/fh-meta/skills/deep-clarify/SKILL.md +28 -9
- package/plugins/fh-meta/skills/fh/SKILL.md +4 -0
- package/plugins/fh-meta/skills/frontier-digest/SKILL.md +64 -8
- package/plugins/fh-meta/skills/frontier-digest/SKILL_detail.md +20 -7
- package/plugins/fh-meta/skills/goal-quench/SKILL.md +48 -15
- package/plugins/fh-meta/skills/goal-quench/SKILL_detail.md +58 -11
- package/plugins/fh-meta/skills/harness-doctor/SKILL_detail.md +109 -33
- package/plugins/fh-meta/skills/harvest-loop/SKILL.md +6 -1
- package/plugins/fh-meta/skills/hub-cc-pr-reviewer/SKILL.md +126 -17
- package/plugins/fh-meta/skills/install-doctor/SKILL.md +50 -14
- package/plugins/fh-meta/skills/install-wizard/SKILL.md +26 -7
- package/plugins/fh-meta/skills/install-wizard/SKILL_detail.md +68 -21
- package/plugins/fh-meta/skills/memory-hygiene/SKILL.md +64 -17
- package/plugins/fh-meta/skills/meta-prompt-builder/SKILL.md +38 -4
- package/plugins/fh-meta/skills/persona-roster-expander/SKILL.md +15 -7
- package/plugins/fh-meta/skills/plugin-recommender/SKILL.md +39 -11
- package/plugins/fh-meta/skills/plugin-recommender/SKILL_detail.md +24 -7
- package/plugins/fh-meta/skills/prompt-regression/SKILL.md +54 -11
- package/plugins/fh-meta/skills/salience-splitter/SKILL.md +120 -7
- package/plugins/fh-meta/skills/salience-splitter/SKILL_detail.md +46 -13
- package/plugins/fh-meta/skills/sim-conductor/SKILL_detail.md +28 -3
- package/plugins/fh-meta/skills/steel-quench/SKILL.md +3 -1
- package/plugins/fh-meta/skills/verify-bidirectional/SKILL.md +72 -14
- package/scripts/compaction_probe.sh +153 -10
- package/scripts/consent_registry_check.sh +63 -0
- package/scripts/count_check.sh +47 -1
- package/scripts/degrade_direction_scan.sh +276 -6
- package/scripts/degrade_probe_capability.sh +105 -0
- package/scripts/lane_runner_check.sh +425 -0
- package/scripts/package_coverage_check.sh +190 -35
- package/scripts/psa_probe_capability.sh +78 -0
- package/scripts/psa_scan_lib.sh +164 -0
- package/scripts/public_surface_scan_files.sh +38 -0
- package/scripts/selfcheck.sh +346 -28
- package/scripts/test_capability_entrypoint_shipping.sh +132 -0
- package/scripts/test_consent_registry.sh +54 -0
- package/scripts/test_count_check_readme_format_lanes.sh +75 -0
- package/scripts/test_degrade_scan_shell_probes.sh +415 -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/validate_yaml.sh +146 -0
- package/templates/degrade_direction_scan.sh +276 -6
- package/templates/goal-quench-settings-merged.json +30 -0
|
@@ -198,6 +198,118 @@ n=$(p_hits "$TMP/kn.py")
|
|
|
198
198
|
[ "$n" -eq 0 ] && ok "python known-negative: still silent" \
|
|
199
199
|
|| bad "python known-negative: $n hit(s) — Python probes became noisy"
|
|
200
200
|
|
|
201
|
+
# ── Markdown-fence lanes (added 2026-08-12) ───────────────────────────────────────────────────
|
|
202
|
+
# WHY THESE EXIST: the fence-extraction feature shipped with ZERO anchors, and a revert probe run
|
|
203
|
+
# by the pre-publish security pass proved it — neutralizing `_md_shadow()` entirely left this suite
|
|
204
|
+
# at "14 passed, 0 failed". A feature you can delete without reddening a lane is not covered, and
|
|
205
|
+
# that gap is exactly why the python3-absence hole below reached a release candidate.
|
|
206
|
+
MDT="$TMP/mdlanes"; mkdir -p "$MDT"
|
|
207
|
+
printf '# t\n\n```bash\nverify() { run || return 0; }\n```\n' > "$MDT/pos.md"
|
|
208
|
+
printf '# t\n\nprose only, no fence\n' > "$MDT/neg.md"
|
|
209
|
+
|
|
210
|
+
# L-MD1 known-positive: a defect inside a ```bash fence is FOUND (the whole point of the feature)
|
|
211
|
+
out=$(bash "$SCAN" "$MDT/pos.md" 2>&1); rc=$?
|
|
212
|
+
if printf '%s' "$out" | grep -q 'bash fence'; then
|
|
213
|
+
ok "MD1 a defect inside a bash fence is detected and reported at the ORIGIN path"
|
|
214
|
+
else
|
|
215
|
+
bad "MD1 fence extraction found nothing in a known-positive — feature is inert (rc=$rc)"
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
# L-MD2 known-negative: a markdown file with NO fence must stay UNSCANNABLE, never 'clean'
|
|
219
|
+
out=$(bash "$SCAN" "$MDT/neg.md" 2>&1); rc=$?
|
|
220
|
+
if printf '%s' "$out" | grep -qi 'unscannable' && [ "$rc" -ne 0 ]; then
|
|
221
|
+
ok "MD2 fence-less markdown reports UNSCANNABLE (not measured != clean)"
|
|
222
|
+
else
|
|
223
|
+
bad "MD2 fence-less markdown did not report UNSCANNABLE (rc=$rc) — absence rendered as pass"
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
# L-MD3 THE REGRESSION THIS SUITE WAS MISSING: with python3 unreachable, extraction is impossible.
|
|
227
|
+
# The file must land in UNSCANNABLE and the run must NOT exit 0. Before the fix it fell into
|
|
228
|
+
# neither set and vanished from the summary, so the scan reported "no smells ... exit 0".
|
|
229
|
+
MDBIN="$TMP/mdbin"; mkdir -p "$MDBIN"
|
|
230
|
+
for c in bash grep sed awk find cksum cut tr mktemp rm cat sort head wc; do
|
|
231
|
+
src=$(command -v "$c" 2>/dev/null) && ln -sf "$src" "$MDBIN/$c"
|
|
232
|
+
done
|
|
233
|
+
# The stub must be CONTROLLED — a stub missing `find` makes the scan report "no scannable target
|
|
234
|
+
# files" and this lane would then pass for a reason unrelated to python3.
|
|
235
|
+
_mdbin_ok=$(env PATH="$MDBIN" bash -c 'command -v find >/dev/null 2>&1 && echo 1 || echo 0')
|
|
236
|
+
out=$(env PATH="$MDBIN" bash "$SCAN" "$MDT/pos.md" 2>&1); rc=$?
|
|
237
|
+
if [ "$_mdbin_ok" != "1" ]; then
|
|
238
|
+
bad "MD3 stub PATH lacks find — lane NOT RUN (not a pass)"
|
|
239
|
+
elif [ "$rc" -ne 0 ] && printf '%s' "$out" | grep -qi 'COULD NOT BE MEASURED'; then
|
|
240
|
+
# NOTE the wording: this asserts UNMEASURED, not UNSCANNABLE. The two were one bucket in the
|
|
241
|
+
# first fix and a re-verification round showed why that mattered — "no fence here" (a normal
|
|
242
|
+
# state) and "extraction impossible" (a blind spot) must not share an exit path.
|
|
243
|
+
ok "MD3 python3 unreachable -> markdown reports COULD-NOT-MEASURE and exit != 0 (never a silent clean)"
|
|
244
|
+
else
|
|
245
|
+
bad "MD3 python3 unreachable produced rc=$rc without a could-not-measure signal — the not-found==0 hole is open"
|
|
246
|
+
fi
|
|
247
|
+
|
|
248
|
+
# L-MD4 shadow-name collision: `a/b.md` and `a_b.md` must not map to the same shadow file, or one
|
|
249
|
+
# of them is silently overwritten while the scanned COUNT still says 2 (coverage counted, detection
|
|
250
|
+
# impossible). Measured before the fix: the defective file disappeared and the run exited 0.
|
|
251
|
+
mkdir -p "$MDT/col/a"
|
|
252
|
+
printf '# c\n\n```bash\nverify() { run || return 0; }\n```\n' > "$MDT/col/a/b.md"
|
|
253
|
+
printf '# c\n\n```bash\nverify() { run || return 1; }\n```\n' > "$MDT/col/a_b.md"
|
|
254
|
+
out=$(bash "$SCAN" "$MDT/col/a/b.md" "$MDT/col/a_b.md" 2>&1); rc=$?
|
|
255
|
+
if printf '%s' "$out" | grep -q 'a/b.md'; then
|
|
256
|
+
ok "MD4 colliding basenames keep separate shadows (the defective file is still reported)"
|
|
257
|
+
else
|
|
258
|
+
bad "MD4 a/b.md vanished under collision with a_b.md — shadow name is not unique"
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
# L-MD5/MD6 — THE DIRECTORY ARM. MD3 only exercises the single-file path, and a re-verification
|
|
262
|
+
# round proved that mattered: the first fix promoted "could not measure" to exit 2 ONLY when FILES
|
|
263
|
+
# was empty, so a directory containing one scannable .sh demoted the failure to a note line and the
|
|
264
|
+
# run exited 0 = CLEAN. That is the dominant path (typed capability scans directories), i.e. the
|
|
265
|
+
# defect this release claims to fix was still live where it actually runs.
|
|
266
|
+
# The stub PATH is CONTROLLED first: `command -v` under zsh can return a bare name, which produces
|
|
267
|
+
# a self-referential symlink and a stub with no `find` — then the scan reports "no scannable target
|
|
268
|
+
# files" and the lane would pass for a reason that has nothing to do with python3.
|
|
269
|
+
MDD="$TMP/mddir"; mkdir -p "$MDD/repo" "$MDD/bin"
|
|
270
|
+
printf '# t\n\n```bash\nverify() { run || return 0; }\n```\n' > "$MDD/repo/SKILL.md"
|
|
271
|
+
printf '#!/usr/bin/env bash\necho ok\n' > "$MDD/repo/helper.sh"
|
|
272
|
+
for c in bash sh grep sed awk find cut tr mktemp rm cat sort head wc printf cksum dirname basename; do
|
|
273
|
+
p=$(command -v "$c" 2>/dev/null); case "$p" in /*) ln -sf "$p" "$MDD/bin/$c" ;; esac
|
|
274
|
+
done
|
|
275
|
+
_stub_md=$(env PATH="$MDD/bin" find "$MDD/repo" -type f -name '*.md' 2>/dev/null | grep -c .)
|
|
276
|
+
if [ "$_stub_md" != "1" ]; then
|
|
277
|
+
bad "MD5/MD6 stub PATH is unusable (find missing) — lanes NOT RUN, which is not a pass"
|
|
278
|
+
else
|
|
279
|
+
out=$(env PATH="$MDD/bin" bash "$SCAN" "$MDD/repo" 2>&1); rc=$?
|
|
280
|
+
if [ "$rc" -ne 0 ] && printf '%s' "$out" | grep -qi 'COULD NOT BE MEASURED'; then
|
|
281
|
+
ok "MD5 directory scan + python3 unreachable -> non-clean exit (not demoted to a note)"
|
|
282
|
+
else
|
|
283
|
+
bad "MD5 directory scan with python3 unreachable exited $rc — 'could not measure' rendered as clean"
|
|
284
|
+
fi
|
|
285
|
+
# cksum is the OTHER undeclared dependency: without it the uniqueness token is empty and the
|
|
286
|
+
# shadow-name collision (MD4) silently returns, so it must gate the same way python3 does.
|
|
287
|
+
cp -R "$MDD/bin" "$MDD/bin2"
|
|
288
|
+
p=$(command -v python3 2>/dev/null); case "$p" in /*) ln -sf "$p" "$MDD/bin2/python3" ;; esac
|
|
289
|
+
rm -f "$MDD/bin2/cksum"
|
|
290
|
+
out=$(env PATH="$MDD/bin2" bash "$SCAN" "$MDD/repo" 2>&1); rc=$?
|
|
291
|
+
if [ "$rc" -ne 0 ] && printf '%s' "$out" | grep -qi 'COULD NOT BE MEASURED'; then
|
|
292
|
+
ok "MD6 cksum unreachable -> non-clean exit (collision guard cannot silently degrade)"
|
|
293
|
+
else
|
|
294
|
+
bad "MD6 cksum unreachable exited $rc — the A-1 collision returns without any signal"
|
|
295
|
+
fi
|
|
296
|
+
# MD8 — `cut` is the THIRD undeclared dependency of the same pipeline (`cksum | cut`). Guarding
|
|
297
|
+
# cksum alone left it half-guarded: without `cut` the uniqueness token is empty and the MD4
|
|
298
|
+
# collision returns. Cross-family finding (gpt-5.5, 2026-08-12); the guard shipped without an
|
|
299
|
+
# anchor, so deleting it left every lane green — this is that missing half.
|
|
300
|
+
cp -R "$MDD/bin" "$MDD/bin3"
|
|
301
|
+
for c in python3 cksum; do
|
|
302
|
+
p=$(command -v "$c" 2>/dev/null); case "$p" in /*) ln -sf "$p" "$MDD/bin3/$c" ;; esac
|
|
303
|
+
done
|
|
304
|
+
rm -f "$MDD/bin3/cut"
|
|
305
|
+
out=$(env PATH="$MDD/bin3" bash "$SCAN" "$MDD/repo" 2>&1); rc=$?
|
|
306
|
+
if [ "$rc" -ne 0 ] && printf '%s' "$out" | grep -qi 'COULD NOT BE MEASURED'; then
|
|
307
|
+
ok "MD8 cut unreachable -> non-clean exit (the cksum|cut pipeline is guarded as a whole)"
|
|
308
|
+
else
|
|
309
|
+
bad "MD8 cut unreachable exited $rc — the uniqueness token silently empties and MD4's collision returns"
|
|
310
|
+
fi
|
|
311
|
+
fi
|
|
312
|
+
|
|
201
313
|
# The field-propagated copy must not drift from the canonical one. Two copies of the same
|
|
202
314
|
# normalizer diverge, and the lenient half silently drops what the strict half catches — measured
|
|
203
315
|
# 2026-07-28: `templates/` was 2 lines behind BEFORE this session's fix and then a full 8 KB behind
|
|
@@ -215,6 +327,309 @@ else
|
|
|
215
327
|
bad "templates/degrade_direction_scan.sh has DRIFTED from scripts/ — the field copy is what qasp/pmh run; sync it (cp scripts/degrade_direction_scan.sh templates/)"
|
|
216
328
|
fi
|
|
217
329
|
|
|
330
|
+
# MD7 — "extraction FAILED" must not be spelled the same way as "this file has no fence".
|
|
331
|
+
# Cross-family finding (gpt-5.5, 2026-08-12) against the first language-split draft: an unreadable
|
|
332
|
+
# SKILL.md fell into UNSCANNABLE, and one clean .sh in the same directory then carried the run to
|
|
333
|
+
# exit 0 = clean. The scanner would have reported a corpus it could not open as verified.
|
|
334
|
+
MDE="$TMP/mderr"; mkdir -p "$MDE"
|
|
335
|
+
printf '# t\n\n```bash\nverify() { run || return 0; }\n```\n' > "$MDE/SKILL.md"
|
|
336
|
+
printf '#!/usr/bin/env bash\necho ok\n' > "$MDE/clean.sh"
|
|
337
|
+
chmod 000 "$MDE/SKILL.md" 2>/dev/null
|
|
338
|
+
# CONTROL: root (and some filesystems) ignore mode 000, and then this lane would pass or fail for a
|
|
339
|
+
# reason unrelated to the code. Prove unreadability before asserting on it.
|
|
340
|
+
if head -c1 "$MDE/SKILL.md" >/dev/null 2>&1; then
|
|
341
|
+
printf ' – MD7 lane NOT RUN (file still readable at mode 000 — running as root?) — this is not a pass\n'
|
|
342
|
+
else
|
|
343
|
+
out=$(bash "$SCAN" "$MDE" 2>&1); rc=$?
|
|
344
|
+
if [ "$rc" -ne 0 ] && printf '%s' "$out" | grep -qi 'COULD NOT BE MEASURED'; then
|
|
345
|
+
ok "MD7 unreadable markdown reports COULD-NOT-MEASURE even with a clean .sh beside it"
|
|
346
|
+
else
|
|
347
|
+
bad "MD7 unreadable markdown exited $rc without a could-not-measure signal — extraction failure is being read as 'no fence'"
|
|
348
|
+
fi
|
|
349
|
+
fi
|
|
350
|
+
chmod 644 "$MDE/SKILL.md" 2>/dev/null
|
|
351
|
+
|
|
352
|
+
# ── S6 word-split lanes (added 2026-08-12) ────────────────────────────────────────────────────
|
|
353
|
+
# The probe claims a SHELL-BEHAVIOUR difference, so the fixtures are not enough on their own: a
|
|
354
|
+
# regex can look right while the premise is wrong. S6c measures the premise itself in both shells.
|
|
355
|
+
# The fixtures carry a ZSH shebang on purpose. S6 is scoped to surfaces whose execution shell is not
|
|
356
|
+
# pinned to a splitting shell (markdown fences, zsh scripts) — see S6d for the measurement that
|
|
357
|
+
# forced that scoping and for the bash-shebang arm that must stay silent.
|
|
358
|
+
cat > "$TMP/s6_positive.sh" <<'EOF'
|
|
359
|
+
#!/usr/bin/env zsh
|
|
360
|
+
for f in $recent_sessions; do echo "$f"; done
|
|
361
|
+
git add -- $FILES
|
|
362
|
+
EOF
|
|
363
|
+
cat > "$TMP/s6_negative.sh" <<'EOF'
|
|
364
|
+
#!/usr/bin/env zsh
|
|
365
|
+
# Command substitution DOES split in zsh — flagging it would be noise, not a finding.
|
|
366
|
+
for f in $(find . -name '*.md'); do echo "$f"; done
|
|
367
|
+
# Arrays split by element in both shells.
|
|
368
|
+
for f in "${FILES[@]}"; do echo "$f"; done
|
|
369
|
+
git add -- "${FILES[@]}"
|
|
370
|
+
# The portable form this probe prescribes.
|
|
371
|
+
printf '%s\n' "$recent_sessions" | while IFS= read -r f; do echo "$f"; done
|
|
372
|
+
EOF
|
|
373
|
+
n=$(bash "$SCAN" "$TMP/s6_positive.sh" 2>&1 | grep -cE '\[S6:')
|
|
374
|
+
[ "$n" -eq 2 ] && ok "S6 known-positive: 2/2 (\`for x in \$VAR\` and \`-- \$LIST\`)" \
|
|
375
|
+
|| bad "S6 known-positive: expected 2 hits, got $n — the zsh word-split class is invisible"
|
|
376
|
+
|
|
377
|
+
n=$(bash "$SCAN" "$TMP/s6_negative.sh" 2>&1 | grep -cE '\[S6:')
|
|
378
|
+
[ "$n" -eq 0 ] && ok "S6 known-negative: \$(cmd), \"\${arr[@]}\" and printf|while stay silent" \
|
|
379
|
+
|| bad "S6 known-negative: $n hit(s) — S6 fires on forms that behave identically in both shells"
|
|
380
|
+
|
|
381
|
+
# S6c — the PREMISE, measured rather than asserted. If zsh ever word-split a parameter expansion,
|
|
382
|
+
# this probe would be a style rule wearing a defect's clothes, and every finding it produced would
|
|
383
|
+
# be a false positive. Pinning the premise means a future zsh/bash change reddens a lane here
|
|
384
|
+
# instead of silently invalidating the probe.
|
|
385
|
+
if command -v zsh >/dev/null 2>&1; then
|
|
386
|
+
_b=$(bash -c 'v="a b"; n=0; for x in $v; do n=$((n+1)); done; echo $n')
|
|
387
|
+
_z=$(zsh -c 'v="a b"; n=0; for x in $v; do n=$((n+1)); done; echo $n')
|
|
388
|
+
_zc=$(zsh -c 'n=0; for x in $(echo a b); do n=$((n+1)); done; echo $n')
|
|
389
|
+
if [ "$_b" = "2" ] && [ "$_z" = "1" ] && [ "$_zc" = "2" ]; then
|
|
390
|
+
ok "S6c premise holds: bash splits \$VAR (2), zsh does not (1), zsh splits \$(cmd) (2)"
|
|
391
|
+
else
|
|
392
|
+
bad "S6c premise BROKEN: bash=\$VAR:$_b zsh=\$VAR:$_z zsh=\$(cmd):$_zc — S6's scope is no longer justified"
|
|
393
|
+
fi
|
|
394
|
+
else
|
|
395
|
+
printf ' – S6c premise lane NOT RUN (zsh unavailable) — this is not a pass\n'
|
|
396
|
+
fi
|
|
397
|
+
|
|
398
|
+
# S6d — APPLICABILITY. Measured 2026-08-12 across 28 hits: 7/7 real inside markdown fences,
|
|
399
|
+
# 21/21 false in `.sh` files with a bash shebang (bash runs those, so splitting is intended there).
|
|
400
|
+
# A 75%-noise probe trains dismissal — this file already paid that at 9/9 FP on S5. Both arms are
|
|
401
|
+
# pinned, because scoping a probe is one edit away from blinding it.
|
|
402
|
+
cat > "$TMP/s6_pinned_bash.sh" <<'EOF'
|
|
403
|
+
#!/usr/bin/env bash
|
|
404
|
+
for f in $recent_sessions; do echo "$f"; done
|
|
405
|
+
git add -- $FILES
|
|
406
|
+
EOF
|
|
407
|
+
n=$(bash "$SCAN" "$TMP/s6_pinned_bash.sh" 2>&1 | grep -cE '\[S6:')
|
|
408
|
+
[ "$n" -eq 0 ] && ok "S6d bash-shebang file: silent (execution shell is pinned — splitting is intended)" \
|
|
409
|
+
|| bad "S6d bash-shebang file fired $n time(s) — S6 is back to 21/28 noise"
|
|
410
|
+
|
|
411
|
+
# S6d-2 — a `.sh` with NO shebang is not pinned either, so the probe MUST apply. The first version
|
|
412
|
+
# of the scoping asked "is this a markdown shadow or a zsh script?" and therefore went silent here;
|
|
413
|
+
# the question that decides it is "does a shebang pin a splitting shell?" (cross-family, round 4).
|
|
414
|
+
cat > "$TMP/s6_no_shebang.sh" <<'EOF'
|
|
415
|
+
for f in $recent_sessions; do echo "$f"; done
|
|
416
|
+
git add -- $FILES
|
|
417
|
+
EOF
|
|
418
|
+
n=$(bash "$SCAN" "$TMP/s6_no_shebang.sh" 2>&1 | grep -cE '\[S6:')
|
|
419
|
+
[ "$n" -eq 2 ] && ok "S6d-2 shebang-less .sh: fires (unknown shell is not a pinned shell)" \
|
|
420
|
+
|| bad "S6d-2 shebang-less .sh produced $n S6 hit(s), expected 2 — the scoping excludes an unpinned surface"
|
|
421
|
+
|
|
422
|
+
# S6d-3 — /bin/sh pins a splitting shell too, so it must stay silent (the exclusion is about the
|
|
423
|
+
# SHELL, not about the presence of a shebang line).
|
|
424
|
+
cat > "$TMP/s6_posix.sh" <<'EOF'
|
|
425
|
+
#!/bin/sh
|
|
426
|
+
for f in $recent_sessions; do echo "$f"; done
|
|
427
|
+
EOF
|
|
428
|
+
n=$(bash "$SCAN" "$TMP/s6_posix.sh" 2>&1 | grep -cE '\[S6:')
|
|
429
|
+
[ "$n" -eq 0 ] && ok "S6d-3 /bin/sh shebang: silent (POSIX sh splits — intended semantics)" \
|
|
430
|
+
|| bad "S6d-3 /bin/sh file fired $n time(s) — the pinned-shell test does not recognize sh"
|
|
431
|
+
|
|
432
|
+
# SYM — a target that is a SYMLINK to a directory must still be traversed. `[ -d ]` follows the
|
|
433
|
+
# link but `find <link> -type f` does not, so the scan enumerated nothing and exited 0 = clean on a
|
|
434
|
+
# directory full of known-positives (cross-family repro, round 4). `-H` follows the argument only.
|
|
435
|
+
SYMREAL="$TMP/symreal"; mkdir -p "$SYMREAL"
|
|
436
|
+
printf '#!/usr/bin/env bash\nscan=$(run) || exit 0\n' > "$SYMREAL/bad.sh"
|
|
437
|
+
ln -sfn "$SYMREAL" "$TMP/symlink"
|
|
438
|
+
n=$(s_hits "$TMP/symlink")
|
|
439
|
+
[ "$n" -ge 1 ] && ok "SYM symlinked directory target is traversed (known-positive inside is found)" \
|
|
440
|
+
|| bad "SYM symlinked directory target produced $n hits — an entire target renders as 'no scannable files, exit 0'"
|
|
441
|
+
|
|
442
|
+
# ...and the same content in a markdown fence, where no shebang pins the shell, MUST still fire.
|
|
443
|
+
printf '# t\n\n```bash\nfor f in $recent_sessions; do echo "$f"; done\n```\n' > "$MDT/s6.md"
|
|
444
|
+
n=$(bash "$SCAN" "$MDT/s6.md" 2>&1 | grep -cE '\[S6:')
|
|
445
|
+
[ "$n" -eq 1 ] && ok "S6d markdown fence: still fires (the surface a human pastes into zsh)" \
|
|
446
|
+
|| bad "S6d markdown fence produced $n S6 hit(s), expected 1 — the scoping blinded the real class"
|
|
447
|
+
|
|
448
|
+
# S6e — a trailing COMMENT must not mask a real hit. Cross-family repro (gpt-5.5, 2026-08-12):
|
|
449
|
+
# `grep -vE '\[[@*]\]'` ran against the raw line, so a real defect whose comment mentioned the safe
|
|
450
|
+
# array form was suppressed entirely. The mirror arm is pinned too: a line matching ONLY inside its
|
|
451
|
+
# comment must stay silent.
|
|
452
|
+
cat > "$TMP/s6_comment.sh" <<'EOF'
|
|
453
|
+
#!/usr/bin/env zsh
|
|
454
|
+
git add -- $FILES # use "${arr[@]}" later
|
|
455
|
+
for x in $VAR; do :; done # "${arr[@]}" would be safe
|
|
456
|
+
echo hi # for y in $OTHER <- match lives only in this comment
|
|
457
|
+
EOF
|
|
458
|
+
n=$(bash "$SCAN" "$TMP/s6_comment.sh" 2>&1 | grep -cE '\[S6:')
|
|
459
|
+
# 3, not 2: the comment-only line IS reported. That is the accepted trade after both comment-aware
|
|
460
|
+
# filters were removed — the array exclusion was dead code that suppressed real hits, and the sed
|
|
461
|
+
# that replaced it could not tell a comment from a `#` inside a string. See the scanner's comment.
|
|
462
|
+
[ "$n" -eq 3 ] && ok "S6e trailing comments never mask a real hit (3/3, comment-only match reported by design)" \
|
|
463
|
+
|| bad "S6e expected 3 S6 hits, got $n — 2 means a comment-aware filter is masking a real defect again"
|
|
464
|
+
|
|
465
|
+
# S6f — a `#` inside a QUOTED STRING must not swallow the real hit that follows it. This is the
|
|
466
|
+
# defect the comment-stripping sed introduced and its removal closes (cross-family repro, round 4).
|
|
467
|
+
cat > "$TMP/s6_quoted_hash.sh" <<'EOF'
|
|
468
|
+
#!/usr/bin/env zsh
|
|
469
|
+
printf "#"; git add -- $FILES
|
|
470
|
+
printf "#"; for f in $recent_sessions; do :; done
|
|
471
|
+
EOF
|
|
472
|
+
n=$(bash "$SCAN" "$TMP/s6_quoted_hash.sh" 2>&1 | grep -cE '\[S6:')
|
|
473
|
+
[ "$n" -eq 2 ] && ok "S6f a quoted '#' before the defect does not hide it (2/2)" \
|
|
474
|
+
|| bad "S6f expected 2 S6 hits, got $n — comment stripping is back and it cannot see quotes"
|
|
475
|
+
|
|
476
|
+
# S6g — expansion OPERATORS. `for f in ${FILES:-}` is the same defect one operator away and was a
|
|
477
|
+
# false negative until round 5. The negative arm matters just as much: widening the pattern is what
|
|
478
|
+
# makes `${arr[@]}` reachable, and an array expansion splits by element in zsh too.
|
|
479
|
+
cat > "$TMP/s6_ops.sh" <<'EOF'
|
|
480
|
+
#!/usr/bin/env zsh
|
|
481
|
+
for f in ${FILES:-}; do :; done
|
|
482
|
+
for f in ${FILES:=x}; do :; done
|
|
483
|
+
for f in ${FILES#p}; do :; done
|
|
484
|
+
git add -- ${FILES:-}
|
|
485
|
+
EOF
|
|
486
|
+
n=$(bash "$SCAN" "$TMP/s6_ops.sh" 2>&1 | grep -cE '\[S6:')
|
|
487
|
+
[ "$n" -eq 4 ] && ok "S6g braced expansions with operators are detected (4/4)" \
|
|
488
|
+
|| bad "S6g expected 4 S6 hits, got $n — \${VAR:-} class is invisible again"
|
|
489
|
+
|
|
490
|
+
cat > "$TMP/s6_ops_neg.sh" <<'EOF'
|
|
491
|
+
#!/usr/bin/env zsh
|
|
492
|
+
for f in ${FILES[@]}; do :; done
|
|
493
|
+
for f in "${FILES[@]}"; do :; done
|
|
494
|
+
git add -- ${FILES[*]}
|
|
495
|
+
EOF
|
|
496
|
+
n=$(bash "$SCAN" "$TMP/s6_ops_neg.sh" 2>&1 | grep -cE '\[S6:')
|
|
497
|
+
[ "$n" -eq 0 ] && ok "S6g-neg array expansions stay silent even under the widened pattern" \
|
|
498
|
+
|| bad "S6g-neg fired $n time(s) on array expansions — the widening reintroduced the class it must exclude"
|
|
499
|
+
|
|
500
|
+
# S6i — the `/` (substitution) and `@` operators. `${FILES//old/new}` splits in bash and not in zsh
|
|
501
|
+
# exactly like a plain `$FILES`, and it was invisible until round 6 because `/` was missing from the
|
|
502
|
+
# operator class. Adding operators one incident at a time is why this lane enumerates them.
|
|
503
|
+
cat > "$TMP/s6_subst.sh" <<'EOF'
|
|
504
|
+
#!/usr/bin/env zsh
|
|
505
|
+
for f in ${FILES//old/new}; do :; done
|
|
506
|
+
for f in ${FILES/#p/q}; do :; done
|
|
507
|
+
git add -- ${FILES//,/ }
|
|
508
|
+
EOF
|
|
509
|
+
n=$(bash "$SCAN" "$TMP/s6_subst.sh" 2>&1 | grep -cE '\[S6:')
|
|
510
|
+
[ "$n" -eq 3 ] && ok "S6i substitution operators (\${V//a/b}) are detected (3/3)" \
|
|
511
|
+
|| bad "S6i expected 3 S6 hits, got $n — the substitution class is invisible again"
|
|
512
|
+
|
|
513
|
+
# HYPH — a target FILE whose name starts with `-`. Normalizing only the directory branch left the
|
|
514
|
+
# file branch feeding `-bad.sh` straight into `grep`, which parsed it as an option cluster and
|
|
515
|
+
# reported "no smells in 1 scanned file" (round 6). Both branches now normalize at the loop head.
|
|
516
|
+
HYPHD="$TMP/hyph"; mkdir -p "$HYPHD"
|
|
517
|
+
printf '#!/usr/bin/env bash\nscan=$(run) || exit 0\n' > "$HYPHD/-bad.sh"
|
|
518
|
+
( cd "$HYPHD" && bash "$SCAN" "-bad.sh" >/dev/null 2>&1 ); rc=$?
|
|
519
|
+
[ "$rc" -eq 2 ] && ok "HYPH a file target named -bad.sh is scanned, not swallowed as options (rc=2)" \
|
|
520
|
+
|| bad "HYPH file target starting with '-' returned rc=$rc — a known-positive renders as clean"
|
|
521
|
+
n=$(s_hits "$HYPHD")
|
|
522
|
+
[ "$n" -ge 1 ] && ok "HYPH-dir the same file is found through a directory walk" \
|
|
523
|
+
|| bad "HYPH-dir directory walk missed the hyphen-named file ($n hits)"
|
|
524
|
+
|
|
525
|
+
# S6h — `#!/usr/bin/env ksh93`: a digit is a word character, so `\bksh\b` could not match it and the
|
|
526
|
+
# file was treated as unpinned. ksh93 splits `$VAR`, so it is pinned and must stay silent.
|
|
527
|
+
cat > "$TMP/s6_ksh93.sh" <<'EOF'
|
|
528
|
+
#!/usr/bin/env ksh93
|
|
529
|
+
for f in $recent_sessions; do :; done
|
|
530
|
+
EOF
|
|
531
|
+
n=$(bash "$SCAN" "$TMP/s6_ksh93.sh" 2>&1 | grep -cE '\[S6:')
|
|
532
|
+
[ "$n" -eq 0 ] && ok "S6h ksh93 shebang recognized as a pinned splitting shell" \
|
|
533
|
+
|| bad "S6h ksh93 fired $n time(s) — versioned shell names escape the pinned-shell test"
|
|
534
|
+
|
|
535
|
+
# DISC — a directory that cannot be traversed must not render as "no scannable target files, exit 0".
|
|
536
|
+
# The `find` preflight proves the BINARY exists; it says nothing about THIS traversal. Cross-family
|
|
537
|
+
# repro used a mode-000 directory holding a known-positive.
|
|
538
|
+
DISCD="$TMP/discblind"; mkdir -p "$DISCD"
|
|
539
|
+
printf '#!/usr/bin/env bash\nscan=$(run) || exit 0\n' > "$DISCD/bad.sh"
|
|
540
|
+
chmod 000 "$DISCD" 2>/dev/null
|
|
541
|
+
if find "$DISCD" -type f >/dev/null 2>&1; then
|
|
542
|
+
printf ' – DISC lane NOT RUN (directory still traversable at mode 000 — running as root?) — not a pass\n'
|
|
543
|
+
else
|
|
544
|
+
out=$(bash "$SCAN" "$DISCD" 2>&1); rc=$?
|
|
545
|
+
if [ "$rc" -ne 0 ] && printf '%s' "$out" | grep -qi 'COULD NOT BE MEASURED'; then
|
|
546
|
+
ok "DISC unreadable directory reports COULD-NOT-MEASURE and exits non-zero"
|
|
547
|
+
else
|
|
548
|
+
bad "DISC unreadable directory exited $rc without a could-not-measure signal — an unenumerated surface renders clean"
|
|
549
|
+
fi
|
|
550
|
+
fi
|
|
551
|
+
chmod 755 "$DISCD" 2>/dev/null
|
|
552
|
+
|
|
553
|
+
# CAP — the typed capability entry point must reach the scanner for a markdown-only target, and must
|
|
554
|
+
# not collapse "could not measure" into "found findings". Both were live defects on 2026-08-12.
|
|
555
|
+
CAPSH="$REPO_ROOT/scripts/degrade_probe_capability.sh"
|
|
556
|
+
if [ ! -f "$CAPSH" ]; then
|
|
557
|
+
printf ' – CAP lanes NOT RUN (degrade_probe_capability.sh absent) — not a pass\n'
|
|
558
|
+
else
|
|
559
|
+
CAPD="$TMP/capmd"; mkdir -p "$CAPD"
|
|
560
|
+
printf '# t\n\n```bash\nverify() { run || return 0; }\n```\n' > "$CAPD/SKILL.md"
|
|
561
|
+
bash "$CAPSH" --target "$CAPD" >/dev/null 2>&1; rc=$?
|
|
562
|
+
[ "$rc" -eq 2 ] && ok "CAP1 markdown-only target reaches the scanner through the typed path (rc=2)" \
|
|
563
|
+
|| bad "CAP1 markdown-only target returned rc=$rc — the headline feature is unreachable on the typed path (3 = the pre-fix NO_TARGET)"
|
|
564
|
+
|
|
565
|
+
CAPD2="$TMP/capprose"; mkdir -p "$CAPD2"; printf '# prose only\n' > "$CAPD2/doc.md"
|
|
566
|
+
bash "$CAPSH" --target "$CAPD2" >/dev/null 2>&1; rc=$?
|
|
567
|
+
[ "$rc" -eq 3 ] && ok "CAP2 fence-less markdown-only target is NO_TARGET, not FINDINGS (rc=3)" \
|
|
568
|
+
|| bad "CAP2 fence-less markdown returned rc=$rc, expected 3"
|
|
569
|
+
|
|
570
|
+
# CAP3 — MIXED: one measurable finding + one unmeasurable file. "Could not measure" must win.
|
|
571
|
+
CAPD3="$TMP/capmixed"; mkdir -p "$CAPD3"
|
|
572
|
+
printf '# t\n\n```bash\nverify() { run || return 0; }\n```\n' > "$CAPD3/good.md"
|
|
573
|
+
printf '# t\n\n```bash\nx=1\n```\n' > "$CAPD3/blind.md"
|
|
574
|
+
chmod 000 "$CAPD3/blind.md" 2>/dev/null
|
|
575
|
+
if head -c1 "$CAPD3/blind.md" >/dev/null 2>&1; then
|
|
576
|
+
printf ' – CAP3 lane NOT RUN (file readable at mode 000 — running as root?) — not a pass\n'
|
|
577
|
+
else
|
|
578
|
+
bash "$CAPSH" --target "$CAPD3" >/dev/null 2>&1; rc=$?
|
|
579
|
+
[ "$rc" -eq 10 ] && ok "CAP3 findings + unmeasurable → HARNESS_ERROR (could-not-measure outranks findings)" \
|
|
580
|
+
|| bad "CAP3 returned rc=$rc, expected 10 — a partly-unmeasured scan is being reported as a plain FINDINGS verdict"
|
|
581
|
+
fi
|
|
582
|
+
chmod 644 "$CAPD3/blind.md" 2>/dev/null
|
|
583
|
+
|
|
584
|
+
# CAP4 — instrument absence must not be spelled as target absence. The wrapper's own pre-count
|
|
585
|
+
# runs `find`; without it the count is 0 and the run reported NO_TARGET, making the HARNESS_ERROR
|
|
586
|
+
# verdict unreachable for that failure while the scanner itself reports it correctly
|
|
587
|
+
# (cross-family, round 4, A). The stub PATH is CONTROLLED first, as everywhere else in this file.
|
|
588
|
+
CAPBIN="$TMP/capbin"; mkdir -p "$CAPBIN"
|
|
589
|
+
for c in bash sh grep sed awk cut tr mktemp rm cat sort head wc printf cksum python3 dirname basename; do
|
|
590
|
+
p=$(command -v "$c" 2>/dev/null); case "$p" in /*) ln -sf "$p" "$CAPBIN/$c" ;; esac
|
|
591
|
+
done
|
|
592
|
+
if env PATH="$CAPBIN" sh -c 'command -v bash >/dev/null 2>&1' && ! env PATH="$CAPBIN" sh -c 'command -v find >/dev/null 2>&1'; then
|
|
593
|
+
env PATH="$CAPBIN" bash "$CAPSH" --target "$CAPD" >/dev/null 2>&1; rc=$?
|
|
594
|
+
[ "$rc" -eq 10 ] && ok "CAP4 find unavailable → HARNESS_ERROR (instrument absence is not target absence)" \
|
|
595
|
+
|| bad "CAP4 returned rc=$rc, expected 10 — a missing instrument is reported as 'no targets' (3)"
|
|
596
|
+
else
|
|
597
|
+
printf ' – CAP4 lane NOT RUN (stub PATH unusable: bash missing or find still resolvable) — not a pass\n'
|
|
598
|
+
fi
|
|
599
|
+
fi
|
|
600
|
+
|
|
601
|
+
# ── Python-fence lanes (added 2026-08-12) ─────────────────────────────────────────────────────
|
|
602
|
+
# ```python fences were outside the extractor, so `except: pass` — the exact shape probes A/B exist
|
|
603
|
+
# for — was unmeasured wherever it actually lives in this corpus (SKILL.md, not .py).
|
|
604
|
+
printf '# t\n\n```python\ndef f(x):\n try:\n return g(x)\n except Exception:\n return True\n```\n' > "$MDT/py.md"
|
|
605
|
+
out=$(bash "$SCAN" "$MDT/py.md" 2>&1)
|
|
606
|
+
if printf '%s' "$out" | grep -q 'python fence'; then
|
|
607
|
+
ok "PY1 a defect inside a \`\`\`python fence is detected and LABELLED as a python fence"
|
|
608
|
+
else
|
|
609
|
+
bad "PY1 python fence not scanned (or mislabelled as bash) — the surface where except/pass lives is unmeasured"
|
|
610
|
+
fi
|
|
611
|
+
|
|
612
|
+
# PY2 — the reason the shadow carries a .py extension instead of reusing .sh. `is_sh` keys on the
|
|
613
|
+
# extension, so a python fence written into a .sh shadow would be probed by the SHELL rules. The
|
|
614
|
+
# fixture below is python whose COMMENT contains a shell shape; a .sh shadow scores it S1.
|
|
615
|
+
printf '# t\n\n```python\n# scan=$(run) || exit 0\ndef f():\n return None\n```\n' > "$MDT/py_shellish.md"
|
|
616
|
+
n=$(bash "$SCAN" "$MDT/py_shellish.md" 2>&1 | grep -cE '\[S[0-9]:')
|
|
617
|
+
[ "$n" -eq 0 ] && ok "PY2 python fences are NOT probed by the shell rules (language-split shadows hold)" \
|
|
618
|
+
|| bad "PY2 shell probes fired $n time(s) on python source — the shadow extension collapsed back to .sh"
|
|
619
|
+
|
|
620
|
+
# PY3 — a markdown file carrying BOTH fence languages must yield BOTH, at their own origin lines.
|
|
621
|
+
# NOTE the python half returns True, not `pass`: probe A keys on a permissive RETURN. The first
|
|
622
|
+
# draft of this lane used `pass` and went red against a correct implementation — the fixture was
|
|
623
|
+
# measuring nothing. Verified by running the extractor directly on both languages before editing
|
|
624
|
+
# any code (the shadows were fine; the assertion was not).
|
|
625
|
+
printf '# t\n\n```bash\nverify() { run || return 0; }\n```\n\n```python\ndef f(x):\n try:\n return g(x)\n except Exception:\n return True\n```\n' > "$MDT/both.md"
|
|
626
|
+
out=$(bash "$SCAN" "$MDT/both.md" 2>&1)
|
|
627
|
+
if printf '%s' "$out" | grep -q 'bash fence' && printf '%s' "$out" | grep -q 'python fence'; then
|
|
628
|
+
ok "PY3 one markdown file yields both a bash and a python shadow (neither overwrites the other)"
|
|
629
|
+
else
|
|
630
|
+
bad "PY3 mixed-language markdown lost one language — shadows collide by name or the loop stops at the first hit"
|
|
631
|
+
fi
|
|
632
|
+
|
|
218
633
|
echo "----"
|
|
219
634
|
echo "degrade-scan shell probes: $pass passed, $fail failed"
|
|
220
635
|
[ "$fail" -eq 0 ] || exit 1
|
|
@@ -17,7 +17,16 @@
|
|
|
17
17
|
# Usage: bash scripts/test_marker_crossfamily_lanes.sh Exit: 0 = all behave; 1 = regression.
|
|
18
18
|
|
|
19
19
|
set -uo pipefail
|
|
20
|
-
|
|
20
|
+
# Script-relative, NOT `git rev-parse --show-toplevel`. Measured 2026-08-13 in a vendored tree
|
|
21
|
+
# (npm install, then `git init` at a level above — a monorepo committing node_modules is the
|
|
22
|
+
# same shape): rev-parse answers with the OUTER repo's root, so this suite looked for the
|
|
23
|
+
# package's own files inside somebody else's checkout, found nothing, and reported
|
|
24
|
+
# HARNESS-ERROR. The consumer sees a red `npm test` caused entirely by where their .git is.
|
|
25
|
+
# The subject of these lanes ships INSIDE this package, so the package root is the only root
|
|
26
|
+
# that can be right. Same form as test_capability_entrypoint_shipping.sh:29.
|
|
27
|
+
# The exposure is new: before these suites were wired into selfcheck.sh they ran nowhere, so
|
|
28
|
+
# the wrong root never cost anything. Wiring a dead lane surfaces every assumption it made.
|
|
29
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
21
30
|
HOOK="$REPO_ROOT/templates/.git-hooks/pre-commit"
|
|
22
31
|
T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
|
|
23
32
|
|
|
@@ -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
|
-
|
|
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
|