@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.
Files changed (76) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/AGENTS.md +18 -0
  3. package/CHEATSHEET.md +1 -1
  4. package/CLAUDE.md +11 -4
  5. package/README.md +19 -0
  6. package/knowledge/shared/harness-core/fh_detail_protocols.md +12 -0
  7. package/knowledge/shared/harness-core/ship_readiness_gate.md +7 -4
  8. package/knowledge/shared/learnings/subagent_invocations_log.yaml +113 -1
  9. package/package.json +10 -2
  10. package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
  11. package/plugins/fh-commons/agents/quench-challenger.md +49 -23
  12. package/plugins/fh-commons/skills/convergence-loop/SKILL.md +14 -0
  13. package/plugins/fh-commons/skills/deliberation/SKILL.md +14 -0
  14. package/plugins/fh-commons/skills/mcp-circuit-breaker/SKILL.md +10 -1
  15. package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
  16. package/plugins/fh-meta/CHANGELOG.md +36 -0
  17. package/plugins/fh-meta/agents/beginner.md +4 -1
  18. package/plugins/fh-meta/agents/challenger.md +7 -1
  19. package/plugins/fh-meta/agents/expert.md +1 -1
  20. package/plugins/fh-meta/agents/fact-checker.md +7 -1
  21. package/plugins/fh-meta/agents/hub-persona-auditor.md +2 -1
  22. package/plugins/fh-meta/agents/main-player.md +4 -1
  23. package/plugins/fh-meta/agents/persona-innovator.md +10 -2
  24. package/plugins/fh-meta/skills/agent-composer/SKILL.md +2 -2
  25. package/plugins/fh-meta/skills/apex-review/SKILL.md +5 -0
  26. package/plugins/fh-meta/skills/asset-placement-gate/SKILL.md +38 -8
  27. package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +16 -2
  28. package/plugins/fh-meta/skills/context-doctor/SKILL_detail.md +45 -10
  29. package/plugins/fh-meta/skills/corpus-grounding-expander/SKILL.md +14 -5
  30. package/plugins/fh-meta/skills/cross-ecosystem-synergy-detection/SKILL.md +93 -30
  31. package/plugins/fh-meta/skills/deep-clarify/SKILL.md +28 -9
  32. package/plugins/fh-meta/skills/fh/SKILL.md +4 -0
  33. package/plugins/fh-meta/skills/frontier-digest/SKILL.md +64 -8
  34. package/plugins/fh-meta/skills/frontier-digest/SKILL_detail.md +20 -7
  35. package/plugins/fh-meta/skills/goal-quench/SKILL.md +48 -15
  36. package/plugins/fh-meta/skills/goal-quench/SKILL_detail.md +58 -11
  37. package/plugins/fh-meta/skills/harness-doctor/SKILL_detail.md +109 -33
  38. package/plugins/fh-meta/skills/harvest-loop/SKILL.md +6 -1
  39. package/plugins/fh-meta/skills/hub-cc-pr-reviewer/SKILL.md +126 -17
  40. package/plugins/fh-meta/skills/install-doctor/SKILL.md +50 -14
  41. package/plugins/fh-meta/skills/install-wizard/SKILL.md +26 -7
  42. package/plugins/fh-meta/skills/install-wizard/SKILL_detail.md +68 -21
  43. package/plugins/fh-meta/skills/memory-hygiene/SKILL.md +64 -17
  44. package/plugins/fh-meta/skills/meta-prompt-builder/SKILL.md +38 -4
  45. package/plugins/fh-meta/skills/persona-roster-expander/SKILL.md +15 -7
  46. package/plugins/fh-meta/skills/plugin-recommender/SKILL.md +39 -11
  47. package/plugins/fh-meta/skills/plugin-recommender/SKILL_detail.md +24 -7
  48. package/plugins/fh-meta/skills/prompt-regression/SKILL.md +54 -11
  49. package/plugins/fh-meta/skills/salience-splitter/SKILL.md +120 -7
  50. package/plugins/fh-meta/skills/salience-splitter/SKILL_detail.md +46 -13
  51. package/plugins/fh-meta/skills/sim-conductor/SKILL_detail.md +28 -3
  52. package/plugins/fh-meta/skills/steel-quench/SKILL.md +3 -1
  53. package/plugins/fh-meta/skills/verify-bidirectional/SKILL.md +72 -14
  54. package/scripts/compaction_probe.sh +153 -10
  55. package/scripts/consent_registry_check.sh +63 -0
  56. package/scripts/count_check.sh +47 -1
  57. package/scripts/degrade_direction_scan.sh +276 -6
  58. package/scripts/degrade_probe_capability.sh +105 -0
  59. package/scripts/lane_runner_check.sh +425 -0
  60. package/scripts/package_coverage_check.sh +190 -35
  61. package/scripts/psa_probe_capability.sh +78 -0
  62. package/scripts/psa_scan_lib.sh +164 -0
  63. package/scripts/public_surface_scan_files.sh +38 -0
  64. package/scripts/selfcheck.sh +346 -28
  65. package/scripts/test_capability_entrypoint_shipping.sh +132 -0
  66. package/scripts/test_consent_registry.sh +54 -0
  67. package/scripts/test_count_check_readme_format_lanes.sh +75 -0
  68. package/scripts/test_degrade_scan_shell_probes.sh +415 -0
  69. package/scripts/test_marker_crossfamily_lanes.sh +10 -1
  70. package/scripts/test_marker_floor_lanes.sh +25 -1
  71. package/scripts/test_psa_singlefile_lanes.sh +288 -0
  72. package/scripts/test_reviewer_capability_conformance.sh +10 -1
  73. package/scripts/test_selfcheck_state_lanes.sh +173 -0
  74. package/scripts/validate_yaml.sh +146 -0
  75. package/templates/degrade_direction_scan.sh +276 -6
  76. package/templates/goal-quench-settings-merged.json +30 -0
@@ -15,6 +15,78 @@ set -u
15
15
  cd "$(dirname "${BASH_SOURCE[0]}")/.."
16
16
  fail=0
17
17
 
18
+ # Single source of "declared legitimately unshipped" — package_coverage_check.sh's ACCEPTED_ABSENT,
19
+ # read once via its --list-accepted flag. Two blocks below (ref-path, SessionStart anchor pairs) used
20
+ # to each re-derive "is this absence OK" from an environment predicate (`.git` presence) instead of
21
+ # consulting this declaration — which reproduces the exact bug the declaration exists to prevent in
22
+ # any git-TRACKED tree that vendors this package (a monorepo committing node_modules, or a consumer
23
+ # who runs `git init` after install): `.git` is present there, so the environment predicate answered
24
+ # "source checkout", ran the full check, and FAILed on paths the declaration had already said were
25
+ # fine to omit. Cross-family review, 2026-08-12 (reship axis, card §🔱⑮ G). `--list-accepted` has no
26
+ # git/package.json dependency itself, so this load is safe to attempt unconditionally.
27
+ _PKG_ACCEPTED_ABSENT=""
28
+ if [ -f scripts/package_coverage_check.sh ]; then
29
+ _PKG_ACCEPTED_ABSENT="$(bash scripts/package_coverage_check.sh --list-accepted 2>/dev/null)"
30
+ fi
31
+ _pkg_accepted_absent() { printf '%s\n' "$_PKG_ACCEPTED_ABSENT" | grep -qxF "$1"; }
32
+
33
+ # Is this path DECLARED shipped by package.json files[]? The companion question to the one above:
34
+ # ACCEPTED_ABSENT answers "is this absence legitimate", this answers "should this be here at all".
35
+ # Together they turn a bare "file missing" into a routed verdict instead of a blanket SKIP.
36
+ # Directory entries in files[] cover everything under them, which is how `templates/.git-hooks`
37
+ # covers `templates/.git-hooks/pre-push` — a prefix test, not equality (getting this wrong is what
38
+ # made a comment claim the hook does not ship while package.json:117 declared its whole directory).
39
+ # NOTE ON WHAT THIS DOES *NOT* PROVE: files[] membership is a DECLARATION, not the tarball. This repo
40
+ # has already measured the two diverging (card §🔱⑮ G/C: repo ✅ / files[] ❌). Here the direction is
41
+ # safe — an over-declaration makes this check stricter, never more lenient — but do not reuse this
42
+ # helper anywhere the answer needs to be "what the consumer actually received"; that needs
43
+ # `npm pack --dry-run --json`.
44
+ _ships_per_files() {
45
+ python3 - "$1" <<'SHIPPY' 2>/dev/null
46
+ import json, sys
47
+ p = sys.argv[1]
48
+ try:
49
+ files = json.load(open('package.json'))['files']
50
+ except Exception:
51
+ sys.exit(2) # unreadable manifest: UNKNOWN, and the caller must not read that as "no"
52
+ sys.exit(0 if any(p == f or p.startswith(f.rstrip('/') + '/') for f in files) else 1)
53
+ SHIPPY
54
+ }
55
+
56
+ # ── The single verdict for "my subject is not here" ───────────────────────────────────────────
57
+ # Measured 2026-08-12 (card §🔱⑮ A2): **18 blocks in this file** rendered a green SKIP when their
58
+ # subject was absent, and **all 18 subjects are declared in package.json files[] and present in the
59
+ # real tarball** (verified with `npm pack --dry-run --json`, 20/20 — so routing them to FAIL cannot
60
+ # over-block a legitimate consumer). For a subject that always ships, "absent" cannot mean "package
61
+ # mode"; the only ways to reach it are DELETION or a broken install, and both were reported green.
62
+ # That is the fourth face of the axis the card names three of: 미측정→clean · 미측정→findings ·
63
+ # 해당없음→FAIL · **삭제→SKIP** — and it is the quiet one, which is why it survived longest.
64
+ #
65
+ # Two of the eighteen are worth naming because they read as already-handled and were not:
66
+ # · the gate_pathspec block printed "— not-checked, NOT a pass" and then did not set fail. The
67
+ # LABEL was honest and the VERDICT was green; a reader greps the message and believes it.
68
+ # · the --self-test loop's comment says "never a silent pass" directly above the arm that was one.
69
+ # A comment asserting a property is not the property. Both were hand-verified by eye, not inferred.
70
+ #
71
+ # THE UNKNOWN ARM IS NOT A SKIP. `_ships_per_files` exits 2 when package.json is unreadable, and an
72
+ # unreadable manifest means we cannot tell deletion from package mode — `not found != 0`, so that
73
+ # case must not silently take the lenient branch (this repo's whole §Instrument-Calibration rule).
74
+ # Usage: [ -f "$subj" ] || { _absent_subject_verdict "<label>" "$subj" || fail=1; }
75
+ _absent_subject_verdict() {
76
+ local label="$1" subj="$2"
77
+ _ships_per_files "$subj"
78
+ case $? in
79
+ 0) echo "FAIL $label: $subj is DECLARED SHIPPED (package.json files[]) but absent — that is a"
80
+ echo " deletion or a broken install, not package mode. The subject itself is missing."
81
+ return 1 ;;
82
+ 1) echo "SKIP $label (subject $subj not in package.json files[], and absent)"
83
+ return 0 ;;
84
+ *) echo "FAIL $label: cannot read package.json, so '$subj absent' is UNDECIDABLE between a"
85
+ echo " deletion and package mode — unmeasured, not clean."
86
+ return 1 ;;
87
+ esac
88
+ }
89
+
18
90
  check() { # check <label> <cmd...>
19
91
  local label="$1"; shift
20
92
  if "$@" 2>/dev/null; then
@@ -152,12 +224,32 @@ fi
152
224
  # above the ref loop drains git's ref list → Destructive-Op gate silently allows a delete/force push).
153
225
  # Wired here, not left standalone: an unwired checker is the exact defect this session found in
154
226
  # session_close_check.sh — building the test and not running it repeats it one layer up.
155
- # Package-mode guard: neither the test nor its subject (templates/.git-hooks/pre-push) is in
156
- # package.json files[] both are source-tree-only infra. Without this guard the SHIPPED selfcheck
157
- # fails for every consumer running `npm test` on the installed package. Caught pre-publish 2026-07-20
158
- # by reproducing package mode; mirrors the ref-path SKIP below.
227
+ # ⚠️ CORRECTED 2026-08-12 (innovator Mode F scan resolved by measurement). This comment used to
228
+ # read: "neither the test nor its subject (templates/.git-hooks/pre-push) is in package.json files[]
229
+ # both are source-tree-only infra." **That was false**, and it had been false long enough to be
230
+ # load-bearing. `npm pack --dry-run --json` on this tree returns 262 files including
231
+ # templates/.git-hooks/pre-push, templates/.git-hooks/pre-commit AND
232
+ # scripts/test_prepush_stdin_integrity.sh — subject and anchor both ship (package.json:117 declares
233
+ # the whole `templates/.git-hooks` directory).
234
+ # The consequence is what makes this worth fixing rather than just re-wording: if the subject always
235
+ # ships, then "subject absent" can no longer mean "package mode" — the only way to reach that arm is
236
+ # that **the hook was deleted**, and it was rendering that as a green SKIP. This is the fourth face
237
+ # of the axis the card names three of at §🔱⑮ (미측정→clean · 미측정→findings · 해당없음→FAIL):
238
+ # **삭제→SKIP**. A deleted Destructive-Op gate reporting green on every surface is the worst of the
239
+ # four, because the other three are loud.
240
+ # Kept as a three-valued check rather than a bare FAIL: a consumer's tree can legitimately lack the
241
+ # directory if they installed with --ignore-scripts and pruned, so the *declaration* is what decides,
242
+ # not the environment (same discipline as `_pkg_accepted_absent` above — consult what ships, do not
243
+ # re-derive it from what happens to be on disk).
159
244
  if [ ! -f templates/.git-hooks/pre-push ]; then
160
- echo "SKIP pre-push stdin integrity (package mode: templates/.git-hooks absent)"
245
+ if _ships_per_files "templates/.git-hooks/pre-push"; then
246
+ echo "FAIL pre-push stdin integrity: templates/.git-hooks/pre-push is DECLARED SHIPPED but absent"
247
+ echo " — that is a deletion or a broken install, not package mode. The Destructive-Op gate"
248
+ echo " this anchors is the thing that is missing."
249
+ fail=1
250
+ else
251
+ echo "SKIP pre-push stdin integrity (not shipped per package.json files[], and absent)"
252
+ fi
161
253
  elif [ -f scripts/test_prepush_stdin_integrity.sh ]; then
162
254
  if ! bash scripts/test_prepush_stdin_integrity.sh; then
163
255
  fail=1
@@ -173,7 +265,7 @@ fi
173
265
  # (scripts/degrade_direction_scan.sh) and the anchor both ship, so this runs in package mode too;
174
266
  # only a missing SUBJECT is a legitimate skip.
175
267
  if [ ! -f scripts/degrade_direction_scan.sh ]; then
176
- echo "SKIP degrade-scan shell probes (subject scripts/degrade_direction_scan.sh absent)"
268
+ _absent_subject_verdict "degrade-scan shell probes" "scripts/degrade_direction_scan.sh" || fail=1
177
269
  elif [ -f scripts/test_degrade_scan_shell_probes.sh ]; then
178
270
  if ! bash scripts/test_degrade_scan_shell_probes.sh; then
179
271
  fail=1
@@ -184,6 +276,37 @@ else
184
276
  fail=1
185
277
  fi
186
278
 
279
+ # count_check's README format override is a mandatory-pass gate whose pattern is caller-supplied.
280
+ # Same subject-present/anchor-gone shape as the block above: if the guard ships without its known
281
+ # pair, a template that defeats the gate (a rendered newline turns the pattern into an OR search)
282
+ # passes silently — measured 2026-08-12 on a stale README that the gate reported as PASS.
283
+ if [ ! -f scripts/count_check.sh ]; then
284
+ _absent_subject_verdict "count_check README-format lanes" "scripts/count_check.sh" || fail=1
285
+ elif [ -f scripts/test_count_check_readme_format_lanes.sh ]; then
286
+ if ! bash scripts/test_count_check_readme_format_lanes.sh; then
287
+ fail=1
288
+ fi
289
+ else
290
+ echo "FAIL count_check README-format lanes: count_check.sh present but its anchor is missing"
291
+ fail=1
292
+ fi
293
+
294
+ # The public-surface scanner's SINGLE-FILE and MISUSE paths. Same subject-present/anchor-gone shape:
295
+ # the scanner is a fail-closed gate on an irreversible surface, and its failure mode is a green that
296
+ # was never earned — a misuse, an unloaded pattern set, or dead plumbing all used to render as clean.
297
+ # Wired here on purpose (cross-family round 2): the lane file existed and was syntax-checked only, so
298
+ # `npm test` and `prepublishOnly` never executed it. A checker nobody calls is prose.
299
+ if [ ! -f scripts/psa_scan_lib.sh ]; then
300
+ _absent_subject_verdict "psa single-file lanes" "scripts/psa_scan_lib.sh" || fail=1
301
+ elif [ -f scripts/test_psa_singlefile_lanes.sh ]; then
302
+ if ! bash scripts/test_psa_singlefile_lanes.sh; then
303
+ fail=1
304
+ fi
305
+ else
306
+ echo "FAIL psa single-file lanes: psa_scan_lib.sh present but its anchor is missing"
307
+ fail=1
308
+ fi
309
+
187
310
  # package-coverage — a shipped doc must not point at a file the tarball omits. Distinct from the
188
311
  # ref-path check below: that one asks "does this path exist at all", this one asks "does the
189
312
  # CONSUMER get it". Measured 2026-07-28: 35 paths existed, were named by a shipped doc, and were
@@ -198,7 +321,7 @@ fi
198
321
  # to reason about CI. scripts/test_package_coverage_lanes.sh pins the predicate across all four
199
322
  # tree shapes plus a known pair.
200
323
  if [ ! -f scripts/package_coverage_check.sh ]; then
201
- echo "SKIP test_package_coverage_lanes.sh (subject scripts/package_coverage_check.sh absent)"
324
+ _absent_subject_verdict "test_package_coverage_lanes.sh" "scripts/package_coverage_check.sh" || fail=1
202
325
  elif [ -f scripts/test_package_coverage_lanes.sh ]; then
203
326
  if ! bash scripts/test_package_coverage_lanes.sh; then
204
327
  fail=1
@@ -211,6 +334,138 @@ else
211
334
  fail=1
212
335
  fi
213
336
 
337
+ # lane-runner — sibling of package-coverage one level up: that one asks "does the CONSUMER get the
338
+ # file a shipped doc names", this one asks "does ANYTHING execute the lane suite we wrote". Measured
339
+ # 2026-08-12 (reship axis, card §🔱⑮ A): 12 of 43 suites under scripts/ had no runner in selfcheck,
340
+ # the git hooks, or CI — including scripts/test_marker_crossfamily_lanes.sh, whose subject is a
341
+ # marker field that hard-blocks commits, and scripts/test_marker_floor_lanes.sh, whose subject is
342
+ # pre-commit's live validate_marker_floor(). Both gates ship; neither calibration had ever run.
343
+ # The card recorded this as three specific repairs needing "one anchor each"; wiring three anchors
344
+ # would have closed those three and stayed blind to the other nine and to the thirteenth. This runs
345
+ # unconditionally when present because its own absence is the defect class it exists to detect —
346
+ # there is no package-mode arm to skip into (a consumer running `npm test` should learn that a
347
+ # shipped suite of theirs is dead code just as much as we should).
348
+ # THREE-VALUED, and the third value exists because the two-valued version was this session's own
349
+ # instance of the defect it detects. The first draft was `if [ -f … ]; then run; fi` — an absent
350
+ # checker fell through to nothing, silently. That is "미측정을 0으로 렌더" reproduced by the commit
351
+ # that closed it: a 1.4.96 consumer has no lane_runner_check.sh (it was added to files[] AFTER that
352
+ # publish), so on their machine this block would have printed nothing at all and `npm test` would
353
+ # have reported a clean run of a check that never existed there.
354
+ # The three states are distinguished by the DECLARATION, not by the environment (same discipline as
355
+ # the pre-push block above): present → run · absent-but-declared-shipped → FAIL (deletion or broken
356
+ # install) · absent-and-not-declared → SKIP, saying so. For a 1.4.96 consumer the third arm is the
357
+ # correct and honest answer, and it names itself rather than being silent.
358
+ if [ -f scripts/lane_runner_check.sh ]; then
359
+ if ! bash scripts/lane_runner_check.sh; then
360
+ fail=1
361
+ fi
362
+ elif _ships_per_files "scripts/lane_runner_check.sh"; then
363
+ echo "FAIL lane-runner: scripts/lane_runner_check.sh is DECLARED SHIPPED but absent — deletion or"
364
+ echo " broken install. The check that detects unrun lane suites is itself missing."
365
+ fail=1
366
+ else
367
+ echo "SKIP lane-runner (not in this package's files[] — predates the version that ships it)"
368
+ fi
369
+
370
+ # ── the twelve suites lane_runner_check.sh measured as having NO runner (2026-08-12) ───────────
371
+ # The check directly above COUNTS them; this block is what makes the count go down. Until now the
372
+ # repo shipped a checker that reported its own todo list every run and nothing that discharged it,
373
+ # which is a decision surface only for as long as someone acts on it.
374
+ #
375
+ # What was actually measured before this wiring existed, because the size of the fact is the reason
376
+ # the block is here: all twelve pass when run by hand (rc=0, ~40s total), and NINE OF THE TWELVE
377
+ # ARE IN THE PUBLISHED TARBALL (`npm pack --dry-run --json`, 263 files). So a consumer running
378
+ # `npm test` was shipping-and-carrying nine test suites that nothing on their machine ever called.
379
+ # The two that hurt most: test_marker_crossfamily_lanes.sh calibrates the `crossfamily:` marker enum
380
+ # that hard-blocks commits, and test_marker_floor_lanes.sh calibrates pre-commit's live
381
+ # validate_marker_floor(). Both gates ship and block; neither calibration had ever executed.
382
+ #
383
+ # ONE loop, not twelve blocks, and that is deliberate: the four-value routing below would otherwise
384
+ # be hand-copied twelve times, and this repo has already measured what that produces — two copies of
385
+ # a normalizer drifting in leniency until one of them silently drops its input
386
+ # ([[feedback_divergent_leniency_duplicate_normalizers]]). The pair table is data; the verdict logic
387
+ # exists once. The existing SessionStart pair-loop below/above uses the same shape.
388
+ #
389
+ # FOUR values, and every arm is reachable on a real machine:
390
+ # subject absent → _absent_subject_verdict (deletion vs package mode, decided by files[])
391
+ # anchor present → run it. exit 10 is called out separately: several of these suites use it
392
+ # for "I could not set myself up" (mktemp failure), which is not a lane
393
+ # failure and must not be reported as one — it still sets fail, because a
394
+ # harness that could not measure did not pass ([[not_found_is_not_zero]]).
395
+ # anchor absent+shipped → FAIL. Deletion or broken install, exactly like the block above.
396
+ # anchor absent+unshipped → SKIP, naming itself. Three anchors genuinely do not ship
397
+ # (chamber_run · frontier_digest_retry · residency_closure), so for a
398
+ # consumer this arm is the honest answer, not a dodge.
399
+ #
400
+ # Subject choice is the file whose behaviour the suite pins, so that deleting the subject routes to
401
+ # "your install is broken" rather than to a green run of a test about nothing.
402
+ # test_capability_entrypoint_shipping.sh pins two (degrade_probe_capability.sh and
403
+ # psa_probe_capability.sh); degrade is named here as the sentinel — the suite itself checks both and
404
+ # fails if either is gone, so nothing is lost by not listing both in this table.
405
+ _LANE_TO=""; command -v timeout >/dev/null 2>&1 && _LANE_TO="timeout 300"
406
+ for _pair in \
407
+ "scripts/degrade_probe_capability.sh|scripts/test_capability_entrypoint_shipping.sh" \
408
+ "scripts/chamber_run.sh|scripts/test_chamber_run_lanes.sh" \
409
+ "scripts/destructive_pre_gate.sh|scripts/test_destructive_pre_gate_lanes.sh" \
410
+ "scripts/env_purity_scan.sh|scripts/test_env_purity_lanes.sh" \
411
+ "scripts/frontier_digest_daily.sh|scripts/test_frontier_digest_retry.sh" \
412
+ "scripts/knowledge_seam_check.sh|scripts/test_knowledge_seam_lanes.sh" \
413
+ "templates/.git-hooks/pre-commit|scripts/test_marker_crossfamily_lanes.sh" \
414
+ "templates/.git-hooks/pre-commit|scripts/test_marker_floor_lanes.sh" \
415
+ "scripts/residency_closure_scan.py|scripts/test_residency_closure_lanes.sh" \
416
+ "scripts/reviewer_capability_corpus.tsv|scripts/test_reviewer_capability_conformance.sh"
417
+ do
418
+ _subj="${_pair%%|*}"; _anc="${_pair##*|}"; _lbl="${_anc##*/}"
419
+ if [ ! -f "$_subj" ]; then
420
+ _absent_subject_verdict "$_lbl" "$_subj" || fail=1
421
+ elif [ -f "$_anc" ]; then
422
+ # `< /dev/null` and the timeout are not decoration: test_frontier_digest_retry.sh deliberately
423
+ # plants a `sleep 300` stub and asserts a 3s watchdog kills it. If that watchdog ever regresses
424
+ # — which is the single defect this suite exists to catch — an unguarded call does not go RED,
425
+ # it HANGS, and CI dies on a job timeout with the cause unattributable. The suite that detects
426
+ # a broken watchdog must not be able to inherit the hang. Same `command -v timeout` guard as the
427
+ # --self-test loop below, because `timeout` is GNU coreutils and stock macOS has neither it nor
428
+ # a substitute; without it `< /dev/null` is the whole defence and a suite that reads stdin ends
429
+ # immediately instead of waiting forever.
430
+ $_LANE_TO bash "$_anc" < /dev/null; _rc=$?
431
+ case "$_rc" in
432
+ 0) ;;
433
+ # The suite could not MEASURE. Distinguished from "the lane failed" because the two send a
434
+ # reader to different places, and a bare fail=1 sends them to the wrong one. 10 = the suite's
435
+ # own setup failed (mktemp, used by several of these); 2 = its subject was missing when it
436
+ # looked (test_knowledge_seam_lanes.sh:7 FATALs this way); 126/127 = the anchor is not
437
+ # executable or not found at all, i.e. a broken install that reached this arm anyway.
438
+ # Every one of them still sets fail — a harness that could not measure did not pass — but it
439
+ # is LABELLED, so the next reader debugs the instrument instead of the lane.
440
+ # The first draft of this loop routed only 10 and let everything else fall into an unlabelled
441
+ # fail=1. That is the same "assumed impossible rather than routed" shape that the sibling
442
+ # commit in lane_runner_check.sh had just written a paragraph against; adversarial review
443
+ # caught the inconsistency between the two files.
444
+ 10|2|126|127)
445
+ echo "HARNESS ERROR $_lbl: the suite exited $_rc — it could not measure, so its verdicts"
446
+ echo " prove nothing about $_subj. Not a lane failure, and not a pass either."
447
+ fail=1 ;;
448
+ *) fail=1 ;;
449
+ esac
450
+ elif _pkg_accepted_absent "$_anc"; then
451
+ # DECLARED legitimately unshipped — the single source for that answer, not a guess from the
452
+ # environment. The first draft printed "this package predates it", which is a WRONG DIAGNOSIS
453
+ # printed forever on every consumer machine: these anchors do not lag the package, they are
454
+ # deliberately not in it (their subjects do not ship either). A confident wrong reason in a
455
+ # verdict line is worse than no reason, because it is the line the next person greps.
456
+ echo "SKIP $_lbl (declared legitimately unshipped — package_coverage_check.sh ACCEPTED_ABSENT)"
457
+ else
458
+ # Not present, and NOT declared absent — so either it should be here (deletion / broken
459
+ # install) or the declaration is stale. Routed through the same three-valued helper as the
460
+ # subject arm above, and for the identical reason: `elif _ships_per_files "$_anc"` was a
461
+ # BOOLEAN test over a THREE-valued function, so its exit 2 (package.json unreadable = UNKNOWN)
462
+ # fell through to a green SKIP. That is the lenient branch the helper's own comment forbids
463
+ # ("THE UNKNOWN ARM IS NOT A SKIP"), rebuilt twelve times in one loop, thirty lines under the
464
+ # helper that exists to prevent it. Found by adversarial review; I had written both.
465
+ _absent_subject_verdict "$_lbl (anchor)" "$_anc" || fail=1
466
+ fi
467
+ done
468
+
214
469
  # embedded --self-test suites (compaction_probe · judgment_circuit_lint · novelty_claim_check).
215
470
  # These three carry their lanes INSIDE the script (`--self-test`) rather than in a sibling
216
471
  # test_*_lanes.sh, so the name-list wiring above skipped them silently: 48 lanes existed and ran
@@ -220,7 +475,7 @@ fi
220
475
  # but self-test missing → FAIL, never a silent pass.
221
476
  for _subj in compaction_probe judgment_circuit_lint novelty_claim_check; do
222
477
  if [ ! -f "scripts/$_subj.sh" ]; then
223
- echo "SKIP $_subj --self-test (subject scripts/$_subj.sh absent)"
478
+ _absent_subject_verdict "$_subj --self-test" "scripts/$_subj.sh" || fail=1
224
479
  else
225
480
  # ⚠️ **문자열 존재로 판정하지 마라.** 초판은 `grep -q -- '--self-test'` 였는데, 그 문자열은
226
481
  # 헤더 주석과 usage echo 에도 있어서 **디스패처 한 줄만 지워도 여전히 매치**한다. 그리고
@@ -234,7 +489,16 @@ for _subj in compaction_probe judgment_circuit_lint novelty_claim_check; do
234
489
  # 난다(실측: homebrew 없는 PATH 에서 3개 subject 전부). 소비자 머신에서 `npm test` 와
235
490
  # `prepublishOnly` 를 깨뜨리는 경로다. 정답 폼은 이미 레포에 있다(sync-from-be.sh:134).
236
491
  # 없으면 무한대기 방지를 잃는 대신 도는 쪽을 택한다 — `< /dev/null` 이 그 방어의 본체다.
237
- local _to=""; command -v timeout >/dev/null 2>&1 && _to="timeout 120"
492
+ # `local` OUTSIDE A FUNCTION, which this loop is. bash prints
493
+ # "selfcheck.sh: line N: local: can only be used in a function"
494
+ # on stderr, the assignment never happens, and `_to` stays empty — so the timeout this line
495
+ # exists to install was NEVER INSTALLED. The guard has been decoration since it was written;
496
+ # the comment above it describing what it protects against was true and unimplemented. Measured
497
+ # 2026-08-13: the error printed three times (once per subject) in a full run, and had been
498
+ # printing in every run before that, unread, because stderr scrolls past a 240-second check.
499
+ # ★ A guard that announces its own failure every single run is still a silent failure if
500
+ # nothing reads the announcement. Fixed by deleting one word.
501
+ _to=""; command -v timeout >/dev/null 2>&1 && _to="timeout 120"
238
502
  _st_out="$($_to bash "scripts/$_subj.sh" --self-test < /dev/null 2>&1)"; _st_rc=$?
239
503
  case "$_st_out" in
240
504
  *캘리브레이션*) : ;;
@@ -276,7 +540,7 @@ fi
276
540
  # anchor was written into tests/ with ZERO callers first — the same defect this file already
277
541
  # records twice above; wiring it here is the fix, not a note about the fix.
278
542
  if [ ! -f scripts/consent_registry_check.sh ]; then
279
- echo "SKIP test_consent_registry.sh (subject scripts/consent_registry_check.sh absent)"
543
+ _absent_subject_verdict "test_consent_registry.sh" "scripts/consent_registry_check.sh" || fail=1
280
544
  elif [ -f scripts/test_consent_registry.sh ]; then
281
545
  if ! bash scripts/test_consent_registry.sh; then
282
546
  fail=1
@@ -291,7 +555,7 @@ fi
291
555
  # cross-family verification. The anchor's first version shipped in tests/ with ZERO callers — the
292
556
  # same defect the comment above records for test_card_drift_probe.sh, repeated one file later.
293
557
  if [ ! -f scripts/sidecar_wait.sh ]; then
294
- echo "SKIP test_sidecar_wait_stdin.sh (subject scripts/sidecar_wait.sh absent)"
558
+ _absent_subject_verdict "test_sidecar_wait_stdin.sh" "scripts/sidecar_wait.sh" || fail=1
295
559
  elif [ -f scripts/test_sidecar_wait_stdin.sh ]; then
296
560
  if ! bash scripts/test_sidecar_wait_stdin.sh; then
297
561
  fail=1
@@ -310,7 +574,7 @@ fi
310
574
  # states that look identical from outside ("the sidecar ran" vs "the model I pinned answered",
311
575
  # "absent" vs "unmeasured"), and its lanes are hermetic stubs, so running them costs nothing.
312
576
  if [ ! -f scripts/sidecar_calibrate.sh ]; then
313
- echo "SKIP test_sidecar_calibrate_lanes.sh (subject scripts/sidecar_calibrate.sh absent)"
577
+ _absent_subject_verdict "test_sidecar_calibrate_lanes.sh" "scripts/sidecar_calibrate.sh" || fail=1
314
578
  elif [ -f scripts/test_sidecar_calibrate_lanes.sh ]; then
315
579
  if ! bash scripts/test_sidecar_calibrate_lanes.sh; then
316
580
  fail=1
@@ -407,7 +671,7 @@ _gps_missing=""
407
671
  [ -f templates/.git-hooks/pre-commit ] || _gps_missing="templates/.git-hooks/pre-commit"
408
672
  [ -f templates/regression_guard.sh ] || _gps_missing="${_gps_missing:+$_gps_missing, }templates/regression_guard.sh"
409
673
  if [ -n "$_gps_missing" ]; then
410
- echo "SKIP gate_pathspec_check.sh (subject absent: $_gps_missing) not-checked, NOT a pass"
674
+ _absent_subject_verdict "gate_pathspec_check.sh" "$_gps_missing" || fail=1
411
675
  elif [ -f scripts/gate_pathspec_check.sh ]; then
412
676
  if ! bash scripts/gate_pathspec_check.sh; then
413
677
  fail=1
@@ -429,7 +693,7 @@ else
429
693
  fi
430
694
 
431
695
  if [ ! -f scripts/fh_node_check.sh ]; then
432
- echo "SKIP test_node_check_lanes.sh (subject scripts/fh_node_check.sh absent)"
696
+ _absent_subject_verdict "test_node_check_lanes.sh" "scripts/fh_node_check.sh" || fail=1
433
697
  elif [ -f scripts/test_node_check_lanes.sh ]; then
434
698
  if ! bash scripts/test_node_check_lanes.sh; then
435
699
  fail=1
@@ -442,7 +706,7 @@ fi
442
706
  # The infra-delta half of the same subject. Separate suite, same pairing rule: it exists only because
443
707
  # fh_node_check.sh does, so its absence beside a present subject is a FAIL, not a skip.
444
708
  if [ ! -f scripts/fh_node_check.sh ]; then
445
- echo "SKIP test_node_infra_delta_lanes.sh (subject scripts/fh_node_check.sh absent)"
709
+ _absent_subject_verdict "test_node_infra_delta_lanes.sh" "scripts/fh_node_check.sh" || fail=1
446
710
  elif [ -f scripts/test_node_infra_delta_lanes.sh ]; then
447
711
  if ! bash scripts/test_node_infra_delta_lanes.sh; then
448
712
  fail=1
@@ -453,14 +717,43 @@ else
453
717
  fi
454
718
 
455
719
  # SessionStart multi-hook + install-wizard snippet merge. Subject for both = the shipped settings
456
- # snippets; a clone without them is a legitimate SKIP, a clone with them and no anchor is not.
720
+ # snippets; a clone without them is a legitimate SKIP, a clone with them and no anchor is not
721
+ # UNLESS the anchor itself is declared package-mode-optional. test_sessionstart_multihook_lanes.sh
722
+ # is exactly that. Its ACCEPTED_ABSENT entry (package_coverage_check.sh) covers a DIFFERENT case than
723
+ # this loop implements: that entry's "selfcheck reports it NOT EXERCISED (exit 2)" describes the
724
+ # anchor RUNNING and finding no CLI (handled a few lines below, rc==2). It never claimed anything
725
+ # about the anchor FILE being missing — a real consumer never gets to run it at all (the file is not
726
+ # in files[]), and this loop had no branch for that at all, so every installed package hit
727
+ # "FAIL … anchor is missing" (corrected 2026-08-12, cross-family review — an earlier revision of this
728
+ # comment miscited the ACCEPTED_ABSENT entry as already covering the missing-file case). Fixed by
729
+ # consulting the SAME declaration (`_pkg_accepted_absent`, loaded once near the top of this file) that
730
+ # the ref-path block above uses — not an environment predicate — because `.git` presence answers "is
731
+ # there a git repo here", not "was this anchor declared intentionally unshipped", and the two diverge
732
+ # in a git-tracked vendored tree (a monorepo committing node_modules, `git init` after install): `.git`
733
+ # exists there too, which would silently reproduce the original FAIL.
734
+ # test_wizard_snippet_merge_lanes.sh is NOT in ACCEPTED_ABSENT (it does ship, per files[]) — routing
735
+ # both anchors through the same declaration lookup, rather than hardcoding one as always-FAIL, means a
736
+ # future person who genuinely needs to exempt it does so by editing ONE list, not by finding this loop.
457
737
  for _pair in \
458
- "templates/settings.SessionStart.snippet.json|scripts/test_sessionstart_multihook_lanes.sh" \
459
- "templates/settings.SessionStart.snippet.json|scripts/test_wizard_snippet_merge_lanes.sh"
738
+ "templates/settings.SessionStart.snippet.json|scripts/test_sessionstart_multihook_lanes.sh|package-optional" \
739
+ "templates/settings.SessionStart.snippet.json|scripts/test_wizard_snippet_merge_lanes.sh|always-shipped"
460
740
  do
461
- _subj="${_pair%%|*}"; _anc="${_pair#*|}"
741
+ _subj="${_pair%%|*}"; _rest="${_pair#*|}"; _anc="${_rest%%|*}"; _mode="${_rest#*|}"
742
+ # This mode field is a gate-verdict policy value (does a missing anchor FAIL or SKIP), not free
743
+ # text — an unrecognized value must not silently fall through to whichever branch string-matching
744
+ # happens to miss. Cross-family review, 2026-08-12: the two known values were previously the only
745
+ # ones exercised, so a typo (e.g. "alway-shipped") landed in the `else` FAIL branch by accident of
746
+ # string mismatch rather than by a checked policy — fail-closed in practice, but undeclared.
747
+ case "$_mode" in
748
+ package-optional|always-shipped) ;;
749
+ *)
750
+ echo "FAIL ${_anc##*/}: unrecognized pair mode '$_mode' (expected package-optional or always-shipped)"
751
+ fail=1
752
+ continue
753
+ ;;
754
+ esac
462
755
  if [ ! -f "$_subj" ]; then
463
- echo "SKIP ${_anc##*/} (subject $_subj absent)"
756
+ _absent_subject_verdict "${_anc##*/}" "$_subj" || fail=1
464
757
  elif [ -f "$_anc" ]; then
465
758
  # THREE-valued, like the session-close anchors above — and for a third reason they do not have.
466
759
  # test_sessionstart_multihook_lanes.sh measures what the LIVE `claude` CLI does with several
@@ -479,6 +772,8 @@ do
479
772
  elif [ "$_rc" -ne 0 ]; then
480
773
  fail=1
481
774
  fi
775
+ elif [ "$_mode" = "package-optional" ] && _pkg_accepted_absent "$_anc"; then
776
+ echo "SKIP ${_anc##*/} (declared ACCEPTED_ABSENT — CLI/cost-gated, see package_coverage_check.sh)"
482
777
  else
483
778
  echo "FAIL ${_anc##*/}: $_subj present but its anchor is missing"
484
779
  fail=1
@@ -489,7 +784,7 @@ done
489
784
  # 2026-07-31; the pipe-verdict lane shipped in PR #209 WITHOUT this wiring, which is itself the
490
785
  # half-fix class the second guard exists to catch — found by running that guard on this repo.
491
786
  if [ ! -f scripts/sidecar_calibrate.sh ]; then
492
- echo "SKIP test_ollama_panel_lanes.sh (subject scripts/sidecar_calibrate.sh absent)"
787
+ _absent_subject_verdict "test_ollama_panel_lanes.sh" "scripts/sidecar_calibrate.sh" || fail=1
493
788
  elif [ -f scripts/test_ollama_panel_lanes.sh ]; then
494
789
  if ! bash scripts/test_ollama_panel_lanes.sh; then
495
790
  fail=1
@@ -500,7 +795,7 @@ else
500
795
  fi
501
796
 
502
797
  if [ ! -f scripts/pipe_verdict_guard.sh ]; then
503
- echo "SKIP test_pipe_verdict_guard_lanes.sh (subject scripts/pipe_verdict_guard.sh absent)"
798
+ _absent_subject_verdict "test_pipe_verdict_guard_lanes.sh" "scripts/pipe_verdict_guard.sh" || fail=1
504
799
  elif [ -f scripts/test_pipe_verdict_guard_lanes.sh ]; then
505
800
  if ! bash scripts/test_pipe_verdict_guard_lanes.sh; then
506
801
  fail=1
@@ -511,7 +806,7 @@ else
511
806
  fi
512
807
 
513
808
  if [ ! -f scripts/halffix_propagation_scan.sh ]; then
514
- echo "SKIP test_halffix_lanes.sh (subject scripts/halffix_propagation_scan.sh absent)"
809
+ _absent_subject_verdict "test_halffix_lanes.sh" "scripts/halffix_propagation_scan.sh" || fail=1
515
810
  elif [ -f scripts/test_halffix_lanes.sh ]; then
516
811
  if ! bash scripts/test_halffix_lanes.sh; then
517
812
  fail=1
@@ -535,7 +830,7 @@ fi
535
830
 
536
831
  for _anchor in scripts/test_session_close_lanes.sh scripts/test_card_drift_probe.sh scripts/test_session_close_chain_lanes.sh; do
537
832
  if [ ! -f scripts/session_close_check.sh ]; then
538
- echo "SKIP ${_anchor##*/} (subject scripts/session_close_check.sh absent)"
833
+ _absent_subject_verdict "${_anchor##*/}" "scripts/session_close_check.sh" || fail=1
539
834
  elif [ -f "$_anchor" ]; then
540
835
  # Preserve the anchor's two failure CLASSES instead of flattening them into one `fail=1`.
541
836
  # An anchor exits 3 when a fixture's premise never obtained — "this run's verdicts prove
@@ -645,7 +940,7 @@ fi
645
940
  # tag reached the remote and a publish from the wrong tree was stopped only by npm's own collision
646
941
  # check, so an unrun anchor here would be the same luck-as-floor arrangement one layer up.
647
942
  if [ ! -f templates/.git-hooks/pre-push ]; then
648
- echo "SKIP test_tag_version_lanes.sh (subject templates/.git-hooks/pre-push absent)"
943
+ _absent_subject_verdict "test_tag_version_lanes.sh" "templates/.git-hooks/pre-push" || fail=1
649
944
  elif [ -f scripts/test_tag_version_lanes.sh ]; then
650
945
  # RUN-ONCE, CAPTURE (2026-08-05) — rationale in the sync_from_be_lanes block later in this file.
651
946
  if _out=$(bash scripts/test_tag_version_lanes.sh 2>&1); then
@@ -665,7 +960,7 @@ fi
665
960
  # per-plugin entries inside marketplace.json, which the tag lane never opens. Measured 2026-08-06:
666
961
  # a bump left the second marketplace entry behind and the tag lane passed 8/8 straight through it.
667
962
  if [ ! -f scripts/version_lockstep_check.sh ]; then
668
- echo "SKIP test_version_lockstep_lanes.sh (subject scripts/version_lockstep_check.sh absent)"
963
+ _absent_subject_verdict "test_version_lockstep_lanes.sh" "scripts/version_lockstep_check.sh" || fail=1
669
964
  elif [ -f scripts/test_version_lockstep_lanes.sh ]; then
670
965
  if _out=$(bash scripts/test_version_lockstep_lanes.sh 2>&1); then
671
966
  echo "PASS test_version_lockstep_lanes.sh (drift blocks · aligned silent · unreadable = exit 2, not pass)"
@@ -751,7 +1046,28 @@ fi
751
1046
 
752
1047
  # Referenced-path existence is a source-tree check. The npm package intentionally
753
1048
  # ships a narrower runtime surface, so package-mode selfcheck skips this section.
754
- if [ -d ".claude/rules" ]; then
1049
+ # ⚠️ The skip predicate used to be `[ -d ".claude/rules" ]`. That broke the moment the tarball
1050
+ # started shipping PART of that directory (`.claude/rules/fh_4axis_gate.md` etc. are in files[]) —
1051
+ # the directory then exists in BOTH source and package mode, so the check never skipped in package
1052
+ # mode at all. It ran, extracted refs from the shipped CLAUDE.md/.claude/rules/*.md (which still
1053
+ # name `.claude/regression/ablation_verdicts.md` and `scripts/probe_scope_check.sh` — both
1054
+ # deliberately unshipped, per package_coverage_check.sh's own ACCEPTED_ABSENT list), then tried
1055
+ # `git check-ignore` against a tree with no `.git` at all (a real `npm pack` tarball is not a git
1056
+ # repo) — that call errors rather than confirming "ignored", so the fallback `[ -f "$p" ]` ran and
1057
+ # correctly found nothing, and the check reported FAIL on two paths it had already been told, one
1058
+ # check over, were fine to omit. First fix (2026-08-12, reship axis, card §🔱⑮ G/D) swapped the skip
1059
+ # predicate for `[ -e .git ]`. Cross-family review then caught that `[ -e .git ]` answers a different
1060
+ # question than the one this check needs: a git-TRACKED vendor of this package (a monorepo that
1061
+ # commits node_modules, or a consumer who runs `git init` after installing) has `.git` and IS a
1062
+ # legitimate package consumer, yet the predicate would route it into the full extraction and
1063
+ # reproduce the exact original FAIL on these same two paths — the fix would have closed the bug only
1064
+ # in the one shape it was tested against. The general fix is not a better environment predicate; it
1065
+ # is not re-deriving "is this legitimately absent" from the environment at all when a DECLARED answer
1066
+ # already exists — `_pkg_accepted_absent()`, loaded once near the top of this file — this block now
1067
+ # consults it before FAILing, so the two paths render SKIP regardless of how `.git` happens to be
1068
+ # shaped in the consumer's tree. `[ -e .git ]` still gates whether the SCAN runs at all (an installed
1069
+ # package with no tracked-refs source to lint), which is a real and unrelated question.
1070
+ if [ -e ".git" ]; then
755
1071
  # Backtick-quoted repo-relative file refs in the always-loaded governance surface
756
1072
  # (CLAUDE.md + .claude/rules/*.md) must exist. Phantom-reference class recurred
757
1073
  # N>=3 in the 2026-06-11 audit window — instrument-not-habit.
@@ -796,6 +1112,8 @@ REFPY
796
1112
  echo "SKIP ref-path (gitignored): $p"
797
1113
  elif [ -f "$p" ]; then
798
1114
  echo "PASS ref-path: $p"
1115
+ elif _pkg_accepted_absent "$p"; then
1116
+ echo "SKIP ref-path (declared ACCEPTED_ABSENT — see package_coverage_check.sh): $p"
799
1117
  else
800
1118
  echo "FAIL ref-path: $p — referenced in CLAUDE.md/.claude/rules but missing"
801
1119
  fail=1
@@ -805,7 +1123,7 @@ $_refs
805
1123
  REFS
806
1124
  fi
807
1125
  else
808
- echo "SKIP ref-path (package mode: .claude/rules absent)"
1126
+ echo "SKIP ref-path (package mode: no .git at package root — source-tree-only check)"
809
1127
  fi
810
1128
 
811
1129
  if [ "$fail" -ne 0 ]; then