#!/usr/bin/env bash
# selfcheck.sh — mandatory-pass (deterministic) checks on FH's own executable surface.
# Class: mandatory-pass (harness_6axis_framework.md §Check classes) — blocks on fail.
# Scope: executables shipped via npm files[] + the bash infra driving the FH gate chain.
# NOT syntax-only any more, and this line used to say it was. Syntax checks (node --check / bash -n)
# are only the first section; behavioural lane suites follow and they DO have side effects and
# environment needs: temp dirs, a loopback HTTP stub on 127.0.0.1:18011, git, `timeout`, and — via
# the session-close lanes — an optional `gh` call that reaches GitHub when the binary is present.
# Corrected 2026-07-31 (cross-family review): the stale "zero side effects, no network" claim
# survived the additions that falsified it, which is how a reader ends up trusting the wrong
# invariant. No remote network is REQUIRED; some is possible.
# Wiring: `npm test` for any session; `prepublishOnly` so a publish cannot ship a
# syntactically broken executable.
set -u
cd "$(dirname "${BASH_SOURCE[0]}")/.."
fail=0

# Single source of "declared legitimately unshipped" — package_coverage_check.sh's ACCEPTED_ABSENT,
# read once via its --list-accepted flag. Two blocks below (ref-path, SessionStart anchor pairs) used
# to each re-derive "is this absence OK" from an environment predicate (`.git` presence) instead of
# consulting this declaration — which reproduces the exact bug the declaration exists to prevent in
# any git-TRACKED tree that vendors this package (a monorepo committing node_modules, or a consumer
# who runs `git init` after install): `.git` is present there, so the environment predicate answered
# "source checkout", ran the full check, and FAILed on paths the declaration had already said were
# fine to omit. Cross-family review, 2026-08-12 (reship axis, card §🔱⑮ G). `--list-accepted` has no
# git/package.json dependency itself, so this load is safe to attempt unconditionally.
_PKG_ACCEPTED_ABSENT=""
if [ -f scripts/package_coverage_check.sh ]; then
  _PKG_ACCEPTED_ABSENT="$(bash scripts/package_coverage_check.sh --list-accepted 2>/dev/null)"
fi
_pkg_accepted_absent() { printf '%s\n' "$_PKG_ACCEPTED_ABSENT" | grep -qxF "$1"; }

# Is this path DECLARED shipped by package.json files[]? The companion question to the one above:
# ACCEPTED_ABSENT answers "is this absence legitimate", this answers "should this be here at all".
# Together they turn a bare "file missing" into a routed verdict instead of a blanket SKIP.
# Directory entries in files[] cover everything under them, which is how `templates/.git-hooks`
# covers `templates/.git-hooks/pre-push` — a prefix test, not equality (getting this wrong is what
# made a comment claim the hook does not ship while package.json:117 declared its whole directory).
# NOTE ON WHAT THIS DOES *NOT* PROVE: files[] membership is a DECLARATION, not the tarball. This repo
# has already measured the two diverging (card §🔱⑮ G/C: repo ✅ / files[] ❌). Here the direction is
# safe — an over-declaration makes this check stricter, never more lenient — but do not reuse this
# helper anywhere the answer needs to be "what the consumer actually received"; that needs
# `npm pack --dry-run --json`.
_ships_per_files() {
  python3 - "$1" <<'SHIPPY' 2>/dev/null
import json, sys
p = sys.argv[1]
try:
    files = json.load(open('package.json'))['files']
except Exception:
    sys.exit(2)          # unreadable manifest: UNKNOWN, and the caller must not read that as "no"
sys.exit(0 if any(p == f or p.startswith(f.rstrip('/') + '/') for f in files) else 1)
SHIPPY
}

# ── The single verdict for "my subject is not here" ───────────────────────────────────────────
# Measured 2026-08-12 (card §🔱⑮ A2): **18 blocks in this file** rendered a green SKIP when their
# subject was absent, and **all 18 subjects are declared in package.json files[] and present in the
# real tarball** (verified with `npm pack --dry-run --json`, 20/20 — so routing them to FAIL cannot
# over-block a legitimate consumer). For a subject that always ships, "absent" cannot mean "package
# mode"; the only ways to reach it are DELETION or a broken install, and both were reported green.
# That is the fourth face of the axis the card names three of: 미측정→clean · 미측정→findings ·
# 해당없음→FAIL · **삭제→SKIP** — and it is the quiet one, which is why it survived longest.
#
# Two of the eighteen are worth naming because they read as already-handled and were not:
#   · the gate_pathspec block printed "— not-checked, NOT a pass" and then did not set fail. The
#     LABEL was honest and the VERDICT was green; a reader greps the message and believes it.
#   · the --self-test loop's comment says "never a silent pass" directly above the arm that was one.
# A comment asserting a property is not the property. Both were hand-verified by eye, not inferred.
#
# THE UNKNOWN ARM IS NOT A SKIP. `_ships_per_files` exits 2 when package.json is unreadable, and an
# unreadable manifest means we cannot tell deletion from package mode — `not found != 0`, so that
# case must not silently take the lenient branch (this repo's whole §Instrument-Calibration rule).
# Usage:  [ -f "$subj" ] || { _absent_subject_verdict "<label>" "$subj" || fail=1; }
_absent_subject_verdict() {
  local label="$1" subj="$2"
  _ships_per_files "$subj"
  case $? in
    0) echo "FAIL  $label: $subj is DECLARED SHIPPED (package.json files[]) but absent — that is a"
       echo "      deletion or a broken install, not package mode. The subject itself is missing."
       return 1 ;;
    1) echo "SKIP  $label (subject $subj not in package.json files[], and absent)"
       return 0 ;;
    *) echo "FAIL  $label: cannot read package.json, so '$subj absent' is UNDECIDABLE between a"
       echo "      deletion and package mode — unmeasured, not clean."
       return 1 ;;
  esac
}

check() { # check <label> <cmd...>
  local label="$1"; shift
  if "$@" 2>/dev/null; then
    echo "PASS  $label"
  else
    echo "FAIL  $label"
    "$@" || true
    fail=1
  fi
}
# ⚠️ KNOWN DEFECT, NOT FIXED HERE — `check()` above has the same evidence-discarding shape the
# lane blocks below were repaired for (2026-08-05): it decides on `"$@" 2>/dev/null` (stderr of the
# DECIDING run is destroyed) and then re-runs to print. It is left alone deliberately: `check()` is
# called by every `node --check` / `bash -n` line in this file, so changing it changes the whole
# surface at once, which is a different job from repairing the four lane blocks (CLAUDE.md
# §Added-Scope Gate question 2). `scripts/probe_scope_check.sh`'s caller near the probe-scope block
# carries the same shape. Both are tracked separately — do NOT read the lane-block repair below as
# having cleared this file.

# _show_failure <captured-output> — print a FAILING suite's evidence without truncating it away.
# Single source for all four lane blocks (a second copy would drift; the divergent-normalizer class).
# WHY NOT `tail -N`: measured 2026-08-05 on sync_from_be_lanes.sh — output is 98 lines and a planted
# lane failure at line ~22 is INVISIBLE to `tail -20` (0 hits), while the summary banner still reads
# "1 failed". The reader gets a FAIL verdict sitting on top of passing log lines — the exact shape
# this whole repair exists to remove. The failing-line extraction finds it (1 hit, known pair).
# All four suites mark failures with `❌` (`no()` in sync_from_be_lanes.sh:21, `chk()` in the other
# three) or an early `FAIL ` line when a subject is missing; grep handles the multi-byte glyph
# (known pair: 1 hit on a ❌ line, 0 on a ✅-only line — verified, not assumed).
_show_failure() {
  local out="$1" fails n banner shown nonblank
  # Whitespace-only counts as empty: guarding with [ -z "$out" ] alone let a suite emitting "   "
  # fall into the died-early branch and print indented blank lines — silence rendered as evidence.
  # SHELL PATTERN, NOT `tr`: the obvious `tr -d '[:space:]'` is a measured defect on BSD. Given a
  # line containing invalid UTF-8, macOS `tr` aborts with `tr: Illegal byte sequence` and emits
  # NOTHING, so the guard concludes "empty" and reports "no output captured" while real evidence is
  # sitting in $out — the exact mis-report this helper exists to prevent, reintroduced by the guard
  # against it. (GNU tr passes the bytes through; the arms disagree, and the failing arm is the
  # author's own machine.) Case-matching is a shell builtin: no subprocess, no charset decoding, so
  # invalid bytes cannot make it lie. Known pair: invalid-byte→nonblank, spaces/tabs/newlines→blank,
  # ""→blank, "hello"→nonblank (4/4), while the tr form returns 0 bytes on arm 1.
  case "$out" in *[![:space:]]*) nonblank=1 ;; *) nonblank= ;; esac
  # NO LOCALE PIN HERE, and its absence is a measured result — same disposition, and same reasoning,
  # as the pin `.github/workflows/validate.yml` removed after refuting its own locale hypothesis.
  # Two model families independently suspected that matching the multi-byte `❌` would break under a
  # C/POSIX locale (one filed it as UNCALIBRATED for the GNU arm, the other as an unpinned-locale
  # defect), so `LC_ALL=C` was added — then both arms were actually measured:
  #   BSD grep (macOS)      : C, UTF-8              → 1 hit each
  #   GNU grep 3.12 (Linux) : C, C.UTF-8, unset     → 1 hit each; and with invalid UTF-8 bytes mixed
  #                                                   in, the ❌ line still extracts (no binary-file
  #                                                   collapse, the specific feared mode)
  # The hypothesis is REFUTED on both arms, so the pin demonstrated nothing and was removed rather
  # than kept as insurance — a knob retained because it might help is indistinguishable from one
  # that does, and the next reader would inherit it as evidence that the danger is real.
  fails=$(printf '%s\n' "$out" | grep -E '❌|^FAIL ' || true)
  banner=$(printf '%s\n' "$out" | grep -E '════' | tail -1 || true)
  if [ -n "$fails" ]; then
    shown=$(printf '%s\n' "$fails" | head -25)
    printf '%s\n' "$shown" | sed 's/^/     /'
    n=$(printf '%s\n' "$fails" | wc -l | tr -d ' ')
    [ "$n" -gt 25 ] && echo "     … ($((n - 25)) more failing lines not shown)"
  elif [ -z "$nonblank" ]; then
    echo "     (no output captured — the suite produced nothing before failing)"
  else
    echo "     (no ❌/FAIL line found — suite likely died early; showing tail)"
    printf '%s\n' "$out" | tail -12 | sed 's/^/     /'
  fi
  # Summary banner: matched by shape, not by position. A blind `tail -2` re-printed lines already
  # shown above (measured: a ❌ within the last 2 lines appeared twice, and the "N more not shown"
  # notice was immediately followed by one of the lines it had just declined to show), and on empty
  # input it emitted a stray indented line. Print it only when it exists and is not already on screen.
  # Dedupe against what was ACTUALLY PRINTED ($shown), not against the full $fails set. Searching
  # $fails suppressed the banner whenever a failing line beyond the head -25 cut merely contained
  # the banner text — i.e. it hid the banner precisely because a line the reader never saw mentioned
  # it. $shown is empty in the non-fails branches, so the banner prints there as before.
  if [ -n "$banner" ] && ! printf '%s\n' "${shown:-}" | grep -qF -- "$banner"; then
    printf '     %s\n' "$banner"
  fi
}

# Node executables (npm-shipped)
for f in bin/*.js; do
  check "node --check $f" node --check "$f"
done

# Codex adapter drift: the thin Codex runtime must keep reading canonical FH
# skill/agent surfaces without silently accepting Claude-native primitives as
# Codex-native.
# NOT via check(): that helper decides on a run whose stderr is discarded, and this call site used to
# additionally discard the subject's STDOUT (`--strict >/dev/null`). fh-codex-doctor writes 100% of
# its drift diagnostics to stdout (measured 2026-08-05: 686 B stdout / 0 B stderr), so a failure
# printed a bare `FAIL` line carrying no diagnosis at all — worse than the truncation this session
# repaired in the lane blocks. Fixed at the call site; check() itself is a separate job (see above).
if _out=$(node bin/fh-codex-doctor.js --strict 2>&1); then
  echo "PASS  fh-codex-doctor --strict"
else
  echo "FAIL  fh-codex-doctor --strict"
  _show_failure "$_out"
  fail=1
fi

# Bash surface: npm-shipped scripts + local bin wrappers + gate-chain infra.
# `bin/fh-gate` · `bin/fh-run` · `bin/fh-goal` are named EXPLICITLY here (not a glob) and, unlike
# most of this list, are not covered by files_manifest_shipping_check.sh either (only their
# `.js` counterparts are declared in package.json files[]) — so a plain `[ -f "$f" ] || continue`
# made their disappearance invisible to BOTH checks at once. Reproduced 2026-09-03: deleting
# bin/fh-gate from a fixture tree left fail=0, no FAIL line, nothing. `scripts/*.sh` staying a
# silent skip on a genuinely-empty glob is correct (that arm still has no non-glob name); the
# named gate-chain-infra paths must not degrade the same way.
for f in scripts/*.sh bin/fh-gate bin/fh-run bin/fh-goal \
         templates/regression_guard.sh templates/temper_check.sh templates/predelete_check.sh templates/.git-hooks/pre-commit; do
  if [ ! -f "$f" ]; then
    case "$f" in
      *'*'*) continue ;;   # unmatched glob (nullglob off) — not a real path, legitimate skip
      *) echo "FAIL  bash -n coverage: gate-chain infra file missing: $f"; fail=1; continue ;;
    esac
  fi
  check "bash -n $f" bash -n "$f"
done

# Count consistency: stated skill/agent counts vs actual directories.
# Drift class recurred 4x on 2026-06-10 alone (local_fh_context 26, plugin.json "3 agents",
# README "5 agents", marketplace.json "3 agents") — this makes the check mechanical and permanent.
# Logic extracted to scripts/count_check.sh so the SAME check also runs at commit time in
# the pre-commit hook (shift-left, gated on a skills-dir add/remove — fh_signal_2026-06-21
# gate-locality gap: the check previously lived only here at the publish boundary, so a
# skill-adding PR could merge with stale counts undetected until the next publish).
if ! bash scripts/count_check.sh; then
  fail=1
fi

# Behavioural regressions on the verdict surface. Syntax checks above prove the scripts parse;
# these prove the gate still fails CLOSED on the holes confirmed open in v1.4.59 (model verdict
# contradicting its own findings, FH_TIMEOUT reaching command position, dry-run readable as
# PASS, an unperformed review reported as a verdict, a forgeable plaintext evidence fence).
# Wired here so `npm test` and prepublishOnly both run them: a publish must not be able to
# ship a gate that has quietly reopened one of them.
if [ -f scripts/test_fh_gate_regressions.sh ]; then
  if ! bash scripts/test_fh_gate_regressions.sh; then
    fail=1
  fi
else
  echo "FAIL  fh-gate regressions: scripts/test_fh_gate_regressions.sh missing"
  fail=1
fi

# pre-push stdin integrity — anchors the 2026-07-20 fail-open hole (a stdin-inheriting subprocess
# above the ref loop drains git's ref list → Destructive-Op gate silently allows a delete/force push).
# Wired here, not left standalone: an unwired checker is the exact defect this session found in
# session_close_check.sh — building the test and not running it repeats it one layer up.
# ⚠️ CORRECTED 2026-08-12 (innovator Mode F scan → resolved by measurement). This comment used to
# read: "neither the test nor its subject (templates/.git-hooks/pre-push) is in package.json files[]
# — both are source-tree-only infra." **That was false**, and it had been false long enough to be
# load-bearing. `npm pack --dry-run --json` on this tree returns 262 files including
# templates/.git-hooks/pre-push, templates/.git-hooks/pre-commit AND
# scripts/test_prepush_stdin_integrity.sh — subject and anchor both ship (package.json:117 declares
# the whole `templates/.git-hooks` directory).
# The consequence is what makes this worth fixing rather than just re-wording: if the subject always
# ships, then "subject absent" can no longer mean "package mode" — the only way to reach that arm is
# that **the hook was deleted**, and it was rendering that as a green SKIP. This is the fourth face
# of the axis the card names three of at §🔱⑮ (미측정→clean · 미측정→findings · 해당없음→FAIL):
# **삭제→SKIP**. A deleted Destructive-Op gate reporting green on every surface is the worst of the
# four, because the other three are loud.
# Kept as a three-valued check rather than a bare FAIL: a consumer's tree can legitimately lack the
# directory if they installed with --ignore-scripts and pruned, so the *declaration* is what decides,
# not the environment (same discipline as `_pkg_accepted_absent` above — consult what ships, do not
# re-derive it from what happens to be on disk).
if [ ! -f templates/.git-hooks/pre-push ]; then
  if _ships_per_files "templates/.git-hooks/pre-push"; then
    echo "FAIL  pre-push stdin integrity: templates/.git-hooks/pre-push is DECLARED SHIPPED but absent"
    echo "      — that is a deletion or a broken install, not package mode. The Destructive-Op gate"
    echo "      this anchors is the thing that is missing."
    fail=1
  else
    echo "SKIP  pre-push stdin integrity (not shipped per package.json files[], and absent)"
  fi
elif [ -f scripts/test_prepush_stdin_integrity.sh ]; then
  if ! bash scripts/test_prepush_stdin_integrity.sh; then
    fail=1
  fi
else
  # source tree HAS the hook but NOT the test => the anchor was deleted. That is a real failure.
  echo "FAIL  pre-push stdin integrity: hook present but scripts/test_prepush_stdin_integrity.sh missing"
  fail=1
fi

# degrade-scan shell probes — the anchor was written 2026-07-28 and shipped with ZERO callers,
# reproducing [[feedback_built_but_not_wired]] in the same session that cited it. The subject
# (scripts/degrade_direction_scan.sh) and the anchor both ship, so this runs in package mode too;
# only a missing SUBJECT is a legitimate skip.
if [ ! -f scripts/degrade_direction_scan.sh ]; then
  _absent_subject_verdict "degrade-scan shell probes" "scripts/degrade_direction_scan.sh" || fail=1
elif [ -f scripts/test_degrade_scan_shell_probes.sh ]; then
  if ! bash scripts/test_degrade_scan_shell_probes.sh; then
    fail=1
  fi
else
  # subject present, anchor gone => the calibration was deleted. Real failure, not a skip.
  echo "FAIL  degrade-scan shell probes: scan present but scripts/test_degrade_scan_shell_probes.sh missing"
  fail=1
fi

# gate-anchor harness — the SAME defect the block above documents, still open six days later.
# MEASURED 2026-08-16 (weekly_audit_2026-08-16.md 🟥-2): scripts/gate_anchor_check.sh has shipped in
# package.json files[] since 2026-08-10 (b18a2bf, PR #323) with ZERO callers — not selfcheck, not
# prepublishOnly, not .git-hooks/, not .github/, not npm scripts. Its only other mention is a comment
# line in a .cap adapter, which is not a call. So the one instrument this repo owns for asking
# "is the gate green for the RIGHT REASON?" was itself never green, never red, never run.
#
# 🟥 The detector did not see it: lane_runner_check.sh reports 46/46 wired · self-test 13/13, because
# this file is neither a lane suite nor a --self-test subject — it is OUTSIDE the detector's field of
# view, exactly like directional_diff_gate on 2026-08-15, and it survived that repair.
#
# ⚠️ Do NOT use "documented in a .md" as the wiring test: publish_freshness_check.sh has no reference
# in any TRACKED doc (knowledge/, docs/) and IS wired (prepublishOnly), and it blocked a publish on
# 2026-08-16. (An earlier draft of this line said "zero .md references" flat; that is false — three
# gitignored tracks/*.md files mention it. The thesis survives, the number did not, and Axis 3 caught
# it.) Call sites are the evidence; documentation is not. The first scan for this finding also missed
# templates/.git-hooks/* because those files carry no extension — scope corrected twice.
#
# Exit contract (from the script header): 0 = every known-pair held · 1 = a pair COLLAPSED (real
# defect) · 10 = harness error OR hooks-not-installed.
#
# 🟥 10 IS TWO DIFFERENT PROPOSITIONS AND THIS BLOCK MUST SPLIT THEM. gate_anchor_check.sh reaches
# `exit 10` from FOUR places, and only ONE of them is a legitimate consumer state:
#     :43  cwd/script repo mismatch      → harness defect
#     :99  mktemp failed                 → harness defect
#     :133 _fixture_fail (git init/seed/remote/origin-main)  → harness defect
#     :339 NOT_INSTALLED > 0             → legitimate: a package-mode consumer has no hooks
# Folding all four into a quiet SKIP is fail-open: a fixture collapse in THIS repo (a git default
# -branch policy change, a restricted TMPDIR, forced commit.gpgsign) would render byte-identically to
# a consumer who simply has no hooks. That is the "unmeasured → clean" move this file legislates
# against — and worse, the --self-test loop 200 lines below already routes rc=10 to `fail=1` saying
# "it could not measure, so its verdicts prove nothing". Same file, same code, opposite verdict.
# (Adversarial review 2026-08-16 found this. ⚠️ An earlier draft of this comment cited `:524` for
# it; the standpoint review measured the real sites — the loop dispatches at :528 and routes
# rc=10 to fail=1 at :562, and the quoted sentence lives at :429/:475. `:524` is none of them, so
# a reviewer following it lands on prose. A stale line number in the comment that argues against
# stale instruments is this commit's own defect class, recorded rather than quietly corrected.)
#
# The discriminator is mechanical, not a guess: the three harness-defect paths use plain `echo
# "❌ harness-error: …"`, while the NOT-INSTALLED path goes through `say()` which `--quiet` suppresses.
# Verified 2026-08-16: running the script from an unrelated git repo with --quiet returns rc=10 AND
# prints `harness-error` to the captured stream. So harness-error-in-output → FAIL; rc=10 without it
# → genuine NOT-INSTALLED → SKIP, labelled UNMEASURED and loud.
#
# ⚠️ NAMED RESIDUAL, not closed here: in CI this lane is structurally UNMEASURED. No workflow sets
# core.hooksPath (grep: 0 hits in .github/workflows/), so validate.yml's selfcheck step always lands
# on the NOT-INSTALLED arm — and `validate` is this repo's only required status check. A permanently
# SKIPPED lane in the one required check is worse than a permanently red one, because SKIP does not
# catch the eye. Fixing it is one line in validate.yml (set core.hooksPath before the selfcheck step)
# and is deliberately NOT bundled here — see the handoff. Until then the only real measurement arm is
# the author's machine, which is the exact shape portability_lint.sh was written to condemn.
# ── prepush-guard known-pair (2026-08-31 배선) ─────────────────────────────────────────
# WHY: 이 스크립트는 pre-push 게이트의 known-pair 앵커인데 **아무도 부르지 않았다**.
# 그리고 그것이 실사고의 출처였다 — 픽스처 루트가 빈 값이 되면 레포 루트에 파일 13개를
# 뿌린다(2026-08-31 고장 주입으로 재현: `mktemp` 가 rc=0 으로 빈 출력을 낼 때).
# 「호출부가 없는 앵커는 산문」이므로 배선한다. 종료코드 계약은 0=통과 / 그 외=실패.
if [ ! -f scripts/prepush_guard_check.sh ]; then
  _absent_subject_verdict "prepush-guard known-pair" "scripts/prepush_guard_check.sh" || fail=1
else
  # 🟥 파이프 앞에서 rc 를 잡는다 — 표시 필터의 rc 를 읽으면 실패가 0 으로 보인다.
  _pg_out="$(bash scripts/prepush_guard_check.sh 2>&1)"; _pg_rc=$?
  if [ "$_pg_rc" -eq 0 ]; then
    echo "PASS  prepush-guard known-pair"
  else
    echo "FAIL  prepush-guard known-pair (rc=$_pg_rc)"
    [ -n "$_pg_out" ] && printf '%s\n' "$_pg_out" | tail -12 | sed 's/^/      /'
    fail=1
  fi
fi

if [ ! -f scripts/gate_anchor_check.sh ]; then
  _absent_subject_verdict "gate-anchor harness" "scripts/gate_anchor_check.sh" || fail=1
else
  # Capture the status BEFORE any display filter — $? after a pipe is the filter's, and a failed
  # check would read as 0 (the repo's own pipe-verdict guard fired on this exact shape twice while
  # this block was being written).
  _ga_out="$(bash scripts/gate_anchor_check.sh --quiet 2>&1)"; _ga_rc=$?
  case "$_ga_rc" in
    0)  echo "PASS  gate-anchor harness (every known-pair held — gate is green for the right reason)" ;;
    1)  echo "FAIL  gate-anchor harness: a known-pair COLLAPSED — a gate is green for the wrong reason"
        [ -n "$_ga_out" ] && printf '%s\n' "$_ga_out" | sed 's/^/      /'
        fail=1 ;;
    10) if printf '%s' "$_ga_out" | grep -q 'harness-error'; then
          echo "FAIL  gate-anchor harness: the anchor could not MEASURE (harness-error, rc=10) — a"
          echo "      collapsed fixture is a defect in the instrument, not a consumer's missing hook."
          printf '%s\n' "$_ga_out" | sed 's/^/      /'
          fail=1
        else
          echo "SKIP  gate-anchor harness: UNMEASURED (hooks not installed, rc=10)"
          echo "      Absence is not a pass — this run did NOT establish that the gates are sound."
          [ -n "$_ga_out" ] && printf '%s\n' "$_ga_out" | sed 's/^/      /'
        fi ;;
    *)  echo "FAIL  gate-anchor harness: undeclared exit code $_ga_rc — the contract is 0/1/10, so an"
        echo "      unknown status is a harness defect, not a pass."
        [ -n "$_ga_out" ] && printf '%s\n' "$_ga_out" | sed 's/^/      /'
        fail=1 ;;
  esac
fi

# count_check's README format override is a mandatory-pass gate whose pattern is caller-supplied.
# Same subject-present/anchor-gone shape as the block above: if the guard ships without its known
# pair, a template that defeats the gate (a rendered newline turns the pattern into an OR search)
# passes silently — measured 2026-08-12 on a stale README that the gate reported as PASS.
if [ ! -f scripts/count_check.sh ]; then
  _absent_subject_verdict "count_check README-format lanes" "scripts/count_check.sh" || fail=1
elif [ -f scripts/test_count_check_readme_format_lanes.sh ]; then
  if ! bash scripts/test_count_check_readme_format_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  count_check README-format lanes: count_check.sh present but its anchor is missing"
  fail=1
fi

# The public-surface scanner's SINGLE-FILE and MISUSE paths. Same subject-present/anchor-gone shape:
# the scanner is a fail-closed gate on an irreversible surface, and its failure mode is a green that
# was never earned — a misuse, an unloaded pattern set, or dead plumbing all used to render as clean.
# Wired here on purpose (cross-family round 2): the lane file existed and was syntax-checked only, so
# `npm test` and `prepublishOnly` never executed it. A checker nobody calls is prose.
if [ ! -f scripts/psa_scan_lib.sh ]; then
  _absent_subject_verdict "psa single-file lanes" "scripts/psa_scan_lib.sh" || fail=1
elif [ -f scripts/test_psa_singlefile_lanes.sh ]; then
  if ! bash scripts/test_psa_singlefile_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  psa single-file lanes: psa_scan_lib.sh present but its anchor is missing"
  fail=1
fi

# doc-claim triad (bridge ①) — a doc that says «A uses B» where A never executes B.
# Subject = scripts/doc_claim_triad_scan.py (review surface, not a verdict). Wired in the same
# commit that created its lane, because lane_runner_check.sh rejects an undeclared unwired suite —
# and adding it to DEBT instead would be the regrowth that file exists to stop.
if [ ! -f scripts/doc_claim_triad_scan.py ]; then
  _absent_subject_verdict "doc-claim triad lanes" "scripts/doc_claim_triad_scan.py" || fail=1
elif [ -f scripts/test_doc_claim_triad_lanes.sh ]; then
  if ! bash scripts/test_doc_claim_triad_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  doc-claim triad lanes: doc_claim_triad_scan.py present but its anchor is missing"
  fail=1
fi

# typed-finding pipeline — fleet (multi-family, parallel) + reject stage (cross-family verdict, code
# drops false positives). Subject = scripts/finding_fleet.sh + scripts/finding_verify.py.
if [ ! -f scripts/finding_verify.py ]; then
  _absent_subject_verdict "finding pipeline lanes" "scripts/finding_verify.py" || fail=1
elif [ -f scripts/test_finding_pipeline_lanes.sh ]; then
  if ! bash scripts/finding_fleet.sh --selftest >/dev/null 2>&1; then
    echo "FAIL  finding fleet selftest: known-pair calibration failed"; fail=1
  fi
  if ! bash scripts/test_finding_pipeline_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  finding pipeline lanes: scripts present but their anchor is missing"
  fail=1
fi

# gate-shape classifier — the mechanical half of the unmapped-file trigger of the Field-Harness
# Load-Bearing Change Gate (2026-09-08). A classifier of scope, not a verdict; its lane holds the
# known-pair (exposure/verdict positives · util/comment negatives · Promise reject( FP anchor).
if [ ! -f scripts/gate_shape_scan.sh ]; then
  _absent_subject_verdict "gate-shape lanes" "scripts/gate_shape_scan.sh" || fail=1
elif [ -f scripts/test_gate_shape_scan_lanes.sh ]; then
  # direct dispatch of the subject's own selftest (caller surface for the ratchet), then the lane
  if ! bash scripts/gate_shape_scan.sh --selftest >/dev/null 2>&1; then
    echo "FAIL  gate-shape selftest: known-pair calibration failed"; fail=1
  fi
  if ! bash scripts/test_gate_shape_scan_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  gate-shape lanes: gate_shape_scan.sh present but its anchor is missing"
  fail=1
fi

# package-coverage — a shipped doc must not point at a file the tarball omits. Distinct from the
# ref-path check below: that one asks "does this path exist at all", this one asks "does the
# CONSUMER get it". Measured 2026-07-28: 35 paths existed, were named by a shipped doc, and were
# absent from the tarball — including templates/predelete_check.sh, which CLAUDE.md instructs you
# to run before a destructive op. Wired here in the same commit that created it, because the two
# previous anchors this session shipped with zero callers.
# Anchored 2026-07-31. Until then this was the ONE subject in this file exempt from the
# "subject present but anchor missing => FAIL" rule the eight blocks below enforce — and the
# exemption cost something real: its source-checkout predicate tested `-d .git`, so inside a git
# WORKTREE (where .git is a FILE) it printed SKIP and returned 0 without scanning. A worktree is
# how a fresh CI checkout gets approximated, so the check was absent from exactly the tree used
# to reason about CI. scripts/test_package_coverage_lanes.sh pins the predicate across all four
# tree shapes plus a known pair.
# ── FH 내장 어댑터 레인 ────────────────────────────────────────────────────
# 어댑터(`.claude/capabilities/adapters/*.cap` + `scripts/adapters/*.sh`)는 남의 하네스 능력을
# FH 안에서 호출하는 **기본 경로**다(운영자 결정 2026-08-16 — 남의 레포에 선언을 심지 않는다).
# 🟥 배선이 없으면 이 레인은 «만들고 배선 안 함» 이 된다 — 같은 날 `cluster_capability_scan.sh`
# 가 비재귀 글롭이라 어댑터를 통째로 못 보던 것을 이 세션이 실측으로 잡았고, 그 회귀를 막는
# 레인(L16/L16b)이 그 파일 self-test 안에 있다. 어댑터 자체의 레인은 여기서 돈다.
if [ ! -f scripts/adapters/peer_resolve.sh ]; then
  _absent_subject_verdict "test_adapter_lanes.sh" "scripts/adapters/peer_resolve.sh" || fail=1
elif [ -f scripts/test_adapter_lanes.sh ]; then
  if ! bash scripts/test_adapter_lanes.sh; then
    fail=1
  fi
else
  _absent_subject_verdict "test_adapter_lanes.sh" "scripts/test_adapter_lanes.sh" || fail=1
fi

# track→repo 해석기 단일화 레인 (2026-08-21 harness-doctor F-1).
# 🟥 배선이 없으면 이 레인은 «만들고 배선 안 함» 이 된다. 그리고 이 수리는 특히 그 위험이 크다 —
# 세 소비자가 전부 npm 으로 나가므로, 라이브러리가 files[] 나 selfcheck 어느 한쪽에서 빠지면
# 소비자 install 에서만 조용히 수리 이전 동작으로 강등된다(로컬은 초록).
if [ ! -f scripts/fh_track_resolve.sh ]; then
  _absent_subject_verdict "test_track_resolve_lanes.sh" "scripts/fh_track_resolve.sh" || fail=1
elif [ -f scripts/test_track_resolve_lanes.sh ]; then
  if ! bash scripts/test_track_resolve_lanes.sh; then
    fail=1
  fi
else
  _absent_subject_verdict "test_track_resolve_lanes.sh" "scripts/test_track_resolve_lanes.sh" || fail=1
fi

if [ ! -f scripts/package_coverage_check.sh ]; then
  _absent_subject_verdict "test_package_coverage_lanes.sh" "scripts/package_coverage_check.sh" || fail=1
elif [ -f scripts/test_package_coverage_lanes.sh ]; then
if ! bash scripts/test_package_coverage_lanes.sh; then
    fail=1
  fi
  # The CHECKER's semantics are deliberately fail-closed and must not be softened: given a git
  # checkout with no package.json it reports UNMEASURED, never "clean", and its own lane L4 pins
  # exactly that. Applicability is the CALLER's judgment, and it is made mechanically here.
  #
  # A repo that ships no npm surface at all is not "unmeasured npm coverage" — it has no npm
  # coverage to measure. Measured 2026-08-16 in a sibling harness that carries this suite verbatim
  # and is not an npm package: this was a PERMANENT red, on every run forever. A gate that can
  # never go green is not strict, it is a gate people learn to ignore — and this suite is the
  # mandatory-pass surface, so that habit is expensive.
  #
  # Note the asymmetry, because it is the whole point: this repo HAS an npm surface, so if its
  # package.json ever went missing the checker would fire and SHOULD — that is a real defect here.
  # The guard below distinguishes "the surface is absent by nature" from "the surface is missing",
  # which is the distinction `not found ≠ 0` exists to protect.
  if [ -f package.json ] || [ -d bin ]; then
    if ! bash scripts/package_coverage_check.sh; then
      fail=1
    fi
  else
    echo "SKIP  package-coverage — NOT APPLICABLE: this repo ships no npm surface"
    echo "      (checked mechanically: package.json absent, bin/ absent — not a claim, a test)"
  fi
else
  echo "FAIL  test_package_coverage_lanes.sh: package_coverage_check.sh present but its anchor is missing"
  fail=1
fi

# files-manifest-shipping — sibling of package-coverage, DIFFERENT question. That check walks
# REFERENCES (a shipped doc names a path, does the path ship); this one walks package.json's files[]
# ARRAY ITSELF and asks whether every declared entry exists on disk. selfcheck.sh's own branch_claim
# block (below) names the exact gap this closes: "measured 2026-08-09, package_coverage_check.sh
# returns PASS on a files[] entry whose file does not exist, so a deleted subject would be green on
# every surface" — that comment calls fixing it, for every subject and not just branch_claim.sh, "a
# separate change with its own verification". This is that change.
if [ ! -f scripts/files_manifest_shipping_check.sh ]; then
  _absent_subject_verdict "test_files_manifest_shipping_lanes.sh" "scripts/files_manifest_shipping_check.sh" || fail=1
elif [ -f scripts/test_files_manifest_shipping_lanes.sh ]; then
  if ! bash scripts/test_files_manifest_shipping_lanes.sh; then
    fail=1
  fi
  # Same applicability guard as package-coverage directly above, same reason: a repo with no npm
  # surface has no files[] to audit, and that is NOT-APPLICABLE, not a red.
  if [ -f package.json ] || [ -d bin ]; then
    if ! bash scripts/files_manifest_shipping_check.sh; then
      fail=1
    fi
  else
    echo "SKIP  files-manifest-shipping — NOT APPLICABLE: this repo ships no npm surface"
    echo "      (checked mechanically: package.json absent, bin/ absent — not a claim, a test)"
  fi
else
  echo "FAIL  test_files_manifest_shipping_lanes.sh: files_manifest_shipping_check.sh present but its anchor is missing"
  fail=1
fi

# lane-runner — sibling of package-coverage one level up: that one asks "does the CONSUMER get the
# file a shipped doc names", this one asks "does ANYTHING execute the lane suite we wrote". Measured
# 2026-08-12 (reship axis, card §🔱⑮ A): 12 of 43 suites under scripts/ had no runner in selfcheck,
# the git hooks, or CI — including scripts/test_marker_crossfamily_lanes.sh, whose subject is a
# marker field that hard-blocks commits, and scripts/test_marker_floor_lanes.sh, whose subject is
# pre-commit's live validate_marker_floor(). Both gates ship; neither calibration had ever run.
# The card recorded this as three specific repairs needing "one anchor each"; wiring three anchors
# would have closed those three and stayed blind to the other nine and to the thirteenth. This runs
# unconditionally when present because its own absence is the defect class it exists to detect —
# there is no package-mode arm to skip into (a consumer running `npm test` should learn that a
# shipped suite of theirs is dead code just as much as we should).
# THREE-VALUED, and the third value exists because the two-valued version was this session's own
# instance of the defect it detects. The first draft was `if [ -f … ]; then run; fi` — an absent
# checker fell through to nothing, silently. That is "미측정을 0으로 렌더" reproduced by the commit
# that closed it: a 1.4.96 consumer has no lane_runner_check.sh (it was added to files[] AFTER that
# publish), so on their machine this block would have printed nothing at all and `npm test` would
# have reported a clean run of a check that never existed there.
# The three states are distinguished by the DECLARATION, not by the environment (same discipline as
# the pre-push block above): present → run · absent-but-declared-shipped → FAIL (deletion or broken
# install) · absent-and-not-declared → SKIP, saying so. For a 1.4.96 consumer the third arm is the
# correct and honest answer, and it names itself rather than being silent.
if [ -f scripts/lane_runner_check.sh ]; then
  if ! bash scripts/lane_runner_check.sh; then
    fail=1
  fi
elif _ships_per_files "scripts/lane_runner_check.sh"; then
  echo "FAIL  lane-runner: scripts/lane_runner_check.sh is DECLARED SHIPPED but absent — deletion or"
  echo "      broken install. The check that detects unrun lane suites is itself missing."
  fail=1
else
  echo "SKIP  lane-runner (not in this package's files[] — predates the version that ships it)"
fi

# ── the twelve suites lane_runner_check.sh measured as having NO runner (2026-08-12) ───────────
# The check directly above COUNTS them; this block is what makes the count go down. Until now the
# repo shipped a checker that reported its own todo list every run and nothing that discharged it,
# which is a decision surface only for as long as someone acts on it.
#
# What was actually measured before this wiring existed, because the size of the fact is the reason
# the block is here: all twelve pass when run by hand (rc=0, ~40s total), and NINE OF THE TWELVE
# ARE IN THE PUBLISHED TARBALL (`npm pack --dry-run --json`, 263 files). So a consumer running
# `npm test` was shipping-and-carrying nine test suites that nothing on their machine ever called.
# The two that hurt most: test_marker_crossfamily_lanes.sh calibrates the `crossfamily:` marker enum
# that hard-blocks commits, and test_marker_floor_lanes.sh calibrates pre-commit's live
# validate_marker_floor(). Both gates ship and block; neither calibration had ever executed.
#
# ONE loop, not twelve blocks, and that is deliberate: the four-value routing below would otherwise
# be hand-copied twelve times, and this repo has already measured what that produces — two copies of
# a normalizer drifting in leniency until one of them silently drops its input
# ([[feedback_divergent_leniency_duplicate_normalizers]]). The pair table is data; the verdict logic
# exists once. The existing SessionStart pair-loop below/above uses the same shape.
#
# FOUR values, and every arm is reachable on a real machine:
#   subject absent        → _absent_subject_verdict (deletion vs package mode, decided by files[])
#   anchor present        → run it. exit 10 is called out separately: several of these suites use it
#                           for "I could not set myself up" (mktemp failure), which is not a lane
#                           failure and must not be reported as one — it still sets fail, because a
#                           harness that could not measure did not pass ([[not_found_is_not_zero]]).
#   anchor absent+shipped → FAIL. Deletion or broken install, exactly like the block above.
#   anchor absent+unshipped → SKIP, naming itself. Three anchors genuinely do not ship
#                           (chamber_run · frontier_digest_retry · residency_closure), so for a
#                           consumer this arm is the honest answer, not a dodge.
#
# Subject choice is the file whose behaviour the suite pins, so that deleting the subject routes to
# "your install is broken" rather than to a green run of a test about nothing.
# test_capability_entrypoint_shipping.sh pins two (degrade_probe_capability.sh and
# psa_probe_capability.sh); degrade is named here as the sentinel — the suite itself checks both and
# fails if either is gone, so nothing is lost by not listing both in this table.
# test_script_caller_ratchet_lanes.sh is the second of that shape: it pins BOTH
# scripts/script_caller_ratchet.sh (the checker) and scripts/ratchet_base_resolve.sh (the base
# resolver its L24–L26 lanes execute), and it hard-exits 1 naming the missing file if either is
# gone. The checker is the sentinel because it is the one whose absence means the gate itself is
# gone; the resolver's absence is a narrower failure the suite still reports by name. Neither
# script ships — both, and this anchor, are declared in package_coverage_check.sh ACCEPTED_ABSENT.
# On a consumer install the SUBJECT arm fires first and `_ships_per_files` returns 1, so the row
# resolves to "SKIP (subject not in files[], and absent)" — the anchor arm is never reached.
_LANE_TO=""; command -v timeout >/dev/null 2>&1 && _LANE_TO="timeout 300"
for _pair in \
  `# 사용 원장 (2026-09-13 신설). SUBJECT 를 원장 모듈로 잡는다 — 배선이 끊기는 자리는 preprep.py 의 __main__ 한 줄이 아니라 모듈이 사라지는 쪽이고, import 가 try/except 라 모듈이 없으면 조용히 무동작으로 돌아간다. 그 무음이 이 레인이 잡을 것이다.` \
  "plugins/fh-preprep/skills/preprep/usage_ledger.py|scripts/test_usage_ledger_lanes.sh" \
  `# fh-run 의 이름→스킬 해석기 (2026-09-13 신설). preprep 승격이 --skill preprep 을 조용히 깨뜨렸고, fh-qp 의 4개는 애초부터 안 닿고 있었다. 소비자 대면 경로인데 커버리지가 0이었다.` \
  "scripts/fh-run.sh|scripts/test_fh_run_resolver_lanes.sh" \
  `# 채널 인벤토리 (2026-09-13 신설). «부재» 주장 전에 디렉터리의 채널을 세고, 읽었다고 선언한 것과 대조한다. 같은 날 세 번 낸 실수 — 내가 연 채널에만 없는 것을 무기록으로 단정 — 의 기계층.` \
  "scripts/channel_inventory.sh|scripts/test_channel_inventory_lanes.sh" \
  "scripts/degrade_probe_capability.sh|scripts/test_capability_entrypoint_shipping.sh" \
  "scripts/chamber_run.sh|scripts/test_chamber_run_lanes.sh" \
  "scripts/chamber_candidate_collect.sh|scripts/test_chamber_sig_lanes.sh" \
  "scripts/destructive_pre_gate.sh|scripts/test_destructive_pre_gate_lanes.sh" \
  "templates/.git-hooks/pre-push|scripts/test_prepush_destructive_lanes.sh" \
  "templates/.git-hooks/pre-push|scripts/test_prepush_destructive_liveness.sh" \
  `# ── 증거 vs 판정 토큰 충돌(2026-09-19). SUBJECT 는 훅이 아니라 «되돌림 프로브 자신»이다 — 이 레인이 재는 것은 게이트의 동작이 아니라 그 프로브의 «출력 계약»(자기가 authored 한 줄에만 ❌ 를 쓴다)이고, 이 집계기가 ❌ 로 실패를 판정하므로 둘이 같은 채널을 공유한다. 실측: run 35426860480 의 로그를 사람이 두 번 연속 오귀속했다 ──` \
  "scripts/test_prepush_destructive_liveness.sh|scripts/test_liveness_echo_token_lanes.sh" \
  "templates/.git-hooks/pre-push|scripts/test_push_zone_lanes.sh" \
  "scripts/push_zone_check.sh|scripts/test_push_zone_lanes.sh" \
  "scripts/session_close_check.sh|scripts/test_push_zone_lanes.sh" \
  "scripts/session_close_check.sh|scripts/test_peer_worktree_detect_lanes.sh" \
  "scripts/env_purity_scan.sh|scripts/test_env_purity_lanes.sh" \
  "scripts/frontier_digest_daily.sh|scripts/test_frontier_digest_retry.sh" \
  "scripts/knowledge_seam_check.sh|scripts/test_knowledge_seam_lanes.sh" \
  "templates/.git-hooks/pre-commit|scripts/test_marker_crossfamily_lanes.sh" \
  "templates/.git-hooks/pre-commit|scripts/test_marker_floor_lanes.sh" \
  ".github/workflows/regression-guard.yml|scripts/test_regression_guard_ci_lanes.sh" \
  ".github/workflows/validate.yml|scripts/test_leak_scan_control_lanes.sh" \
  "templates/.git-hooks/pre-commit|scripts/test_marker_standpoint_lanes.sh" \
  "templates/.git-hooks/pre-commit|scripts/test_marker_thirdparty_lanes.sh" \
  "templates/.git-hooks/pre-commit|scripts/test_marker_soul_check_lanes.sh" \
  "templates/.git-hooks/pre-commit|scripts/test_marker_soul_tenet_lanes.sh" \
  "templates/.git-hooks/pre-commit|scripts/test_marker_affected_lanes.sh" \
  "templates/.git-hooks/pre-commit|scripts/test_marker_oracle_lanes.sh" \
  `# ── 판정을 둘로 (2026-09-06, SWE-Gate 답습): 합성 «PASS» 가 acceptance-evidence 를 가리지 않는가 ──` \
  "templates/.git-hooks/pre-commit|scripts/test_gate_two_verdicts_lanes.sh" \
  `# ── fh-qp (QP) — chamber run #18 EMIT 2026-09-05: qp_tools.sh known-pair + residency lanes ──` \
  "plugins/fh-qp/scripts/qp_tools.sh|scripts/test_fh_qp_lanes.sh" \
  "plugins/fh-preprep/skills/preprep/diagram_from_json.py|scripts/test_preprep_diagram_lanes.sh" \
  `# ── action.yml — the GitHub Action wrapper: its exit-code mapping is where a typed verdict could become a boolean ──` \
  "action.yml|scripts/test_action_yml_lanes.sh" \
  "scripts/sim_isolated_run.sh|scripts/test_sim_path_isolation_lanes.sh" \
  `# ── live-eval — the live twin of the static /prompt-regression probe check (2026-09-04) ──` \
  "scripts/probe_live_eval.sh|scripts/test_probe_live_eval_lanes.sh" \
  "scripts/probe_live_eval_lib.py|scripts/test_probe_live_eval_lanes.sh" \
  "templates/.git-hooks/pre-commit|scripts/test_marker_first_use_lanes.sh" \
  "scripts/fixture_guard_lib.sh|scripts/test_fixture_guard_lanes.sh" \
  "scripts/stray_path_scan.sh|scripts/test_stray_path_lanes.sh" \
  "scripts/session_close_check.sh|scripts/test_stray_path_lanes.sh" \
  `# 티키타카 채점기 (2026-09-19 신설) — 다중턴 sim 의 «수렴/반영» 을 채점한다. 러너(--turns)는 있었고 채점기가 0줄이었다.` \
  "scripts/tikitaka_score.py|scripts/test_tikitaka_score_lanes.sh" \
  `# 체크리스트 unblocked (2026-09-19 신설) — 막혀 있던 행의 블로커가 DONE 이 됐는데 그 행이 안 움직였나.
  #   운영자 지적: «내 발화가 도중에 열화되었다 … 테스트 한 차례 끝날 때마다 들여다봐야 할 것 같다».
  #   SUBJECT 를 session_checklist.py 로 잡는다 — 이 레인이 재는 것은 그 도구의 판정이다.` \
  "scripts/session_checklist.py|scripts/test_checklist_unblocked_lanes.sh" \
  "templates/.git-hooks/pre-commit|scripts/test_hook_leg_wiring_lanes.sh" \
  ".claude/soul_tenets.txt|scripts/test_marker_soul_tenet_lanes.sh" \
  "docs/map/fh_assets.architecture.json|scripts/test_fh_map_paths_lanes.sh" \
  `# ── 지도 후처리(2026-09-06): 발행 폭 하한 + SVG 재생성. 리터럴 드리프트를 fail-closed 로 잡는다 ──` \
  "scripts/map_postprocess.py|scripts/test_map_postprocess_lanes.sh" \
  `# ── 플로어 없는 채널(2026-09-14): 원격 자율 노드가 FH 자산을 바꾸면 마커가 tracks/ 와 함께 휘발한다. 실측 2/2(#675·#716). CI 가 gitignored 마커를 구조적으로 못 보므로, 그 채널에만 «마커가 커밋 기록에 실려 왔나» 를 건다 ──` \
  "scripts/remote_marker_gate.sh|scripts/test_remote_marker_gate_lanes.sh" \
  `# ── 발신 전 3프로브(2026-09-18): 비소유 레포에 PR 을 «열기 직전» 에 건다. 실측 — outbound 9건 중 기술 결함 지적 3건이 전부 같은 형태다: 우리 가드와 우리 테스트가 «대상의 모형» 위에서 돌았고, 메인테이너의 증거는 우리가 한 번도 안 돌린 실행이었다 ──` \
  "scripts/outbound_pr_gate.sh|scripts/test_outbound_pr_gate_lanes.sh" \
  `# ── 발행 «확인» 예산(2026-09-18): npm publish 는 이미 rc=0 으로 끝났고 이 스크립트는 전파만 관측한다. 둘을 한 종료코드로 접으면 성공한 발행이 빨간 잡이 되고, 그 빨강이 정확히 «손 발행» 을 훈련시킨다(v3.2.0·v3.4.0) ──` \
  "scripts/publish_verify_poll.sh|scripts/test_publish_verify_poll_lanes.sh" \
  ".github/workflows/validate.yml|scripts/test_remote_marker_gate_lanes.sh" \
  "templates/.git-hooks/pre-commit|scripts/test_precommit_staged_drift_lanes.sh" \
  "templates/.git-hooks/pre-commit|scripts/test_precommit_gitlink_lanes.sh" \
  "templates/.git-hooks/pre-commit|scripts/test_marker_address_lanes.sh" \
  "templates/.git-hooks/pre-commit|scripts/test_precommit_pointer_index_lanes.sh" \
  "scripts/residency_closure_scan.py|scripts/test_residency_closure_lanes.sh" \
  "scripts/reviewer_capability_corpus.tsv|scripts/test_reviewer_capability_conformance.sh" \
  "scripts/field_canon_preload.sh|scripts/test_field_canon_lanes.sh" \
  "scripts/stale_clone_guard.sh|scripts/test_stale_clone_guard_lanes.sh" \
  "scripts/proposal_hook.sh|scripts/test_proposal_hook_lanes.sh" \
  "plugins/fh-commons/skills/ko-tech-writer/SKILL.md|scripts/test_ko_tech_writer_lanes.sh" \
  "scripts/script_caller_ratchet.sh|scripts/test_script_caller_ratchet_lanes.sh" \
  "scripts/script_caller_ratchet.sh|scripts/test_runner_surface_index_lanes.sh" \
  "scripts/mapped_tracks.sh|scripts/test_mapped_tracks_lanes.sh" \
  "scripts/fh-goal.sh|scripts/test_fh_goal_change_detection_lanes.sh" \
  "scripts/utterance_skill_probe.sh|scripts/test_utterance_skill_probe_lanes.sh" \
  `# ── preprep 스킬(2026-08-29). 주체는 스킬 안의 모듈이라 scripts/ 밖이다 ──` \
  "plugins/fh-preprep/skills/preprep/preprep.py|scripts/test_preprep_retired_lanes.sh" \
  "plugins/fh-preprep/skills/preprep/lane_progression.py|scripts/test_preprep_progression_lanes.sh" \
  "plugins/fh-preprep/skills/preprep/lane_adjacent_dup.py|scripts/test_preprep_adjacent_dup_lanes.sh" \
  "plugins/fh-preprep/skills/preprep/lane_promise.py|scripts/test_preprep_promise_lanes.sh" \
  "plugins/fh-preprep/skills/preprep/lane_slide_refs.py|scripts/test_preprep_slide_refs_lanes.sh" \
  "plugins/fh-preprep/skills/preprep/lane_font.py|scripts/test_preprep_font_lanes.sh" \
  "plugins/fh-preprep/skills/preprep/SKILL.md|scripts/test_preprep_drift_anchor.sh" \
  "scripts/test_preprep_drift_anchor.sh|scripts/test_preprep_drift_anchor_lanes.sh" \
  "scripts/field_canon_preload.sh|scripts/test_skill_canon_preload_lanes.sh" \
  `# ── round/ 회차 계기 4종(2026-09-01). 넷 다 한 스위트가 잡는다 — 주체별로 행을 둔다 ──` \
  "scripts/round/delta_guard.sh|scripts/test_round_instruments_lanes.sh" \
  "scripts/round/target_pin.sh|scripts/test_round_instruments_lanes.sh" \
  "scripts/round/instrument_manifest.sh|scripts/test_round_instruments_lanes.sh" \
  "scripts/round/eligcheck_qset.sh|scripts/test_round_instruments_lanes.sh" \
  "scripts/round/gatecheck_qset.sh|scripts/test_round_instruments_lanes.sh" \
  `# ── 발화 착지(2026-09-05). 네 주체가 한 스위트로 묶인다 — 주체별로 행을 둔다 ──` \
  "scripts/utterance_intake.sh|scripts/test_utterance_intake_lanes.sh" \
  "scripts/transcript_utterances.py|scripts/test_utterance_intake_lanes.sh" \
  "scripts/compaction_probe.sh|scripts/test_utterance_intake_lanes.sh" \
  "scripts/session_close_check.sh|scripts/test_utterance_intake_lanes.sh" \
  `# ── 워크트리 회수(2026-09-05, N=2 재발 → 스크립트). 사람이 부르는 도구지만 레인은 자동 ──` \
  "scripts/worktree_reclaim.sh|scripts/test_worktree_reclaim_lanes.sh"
do
  _subj="${_pair%%|*}"; _anc="${_pair##*|}"; _lbl="${_anc##*/}"
  if [ ! -f "$_subj" ]; then
    _absent_subject_verdict "$_lbl" "$_subj" || fail=1
  elif [ -f "$_anc" ]; then
    # `< /dev/null` and the timeout are not decoration: test_frontier_digest_retry.sh deliberately
    # plants a `sleep 300` stub and asserts a 3s watchdog kills it. If that watchdog ever regresses
    # — which is the single defect this suite exists to catch — an unguarded call does not go RED,
    # it HANGS, and CI dies on a job timeout with the cause unattributable. The suite that detects
    # a broken watchdog must not be able to inherit the hang. Same `command -v timeout` guard as the
    # --self-test loop below, because `timeout` is GNU coreutils and stock macOS has neither it nor
    # a substitute; without it `< /dev/null` is the whole defence and a suite that reads stdin ends
    # immediately instead of waiting forever.
    $_LANE_TO bash "$_anc" < /dev/null; _rc=$?
    case "$_rc" in
      0) ;;
      # The suite could not MEASURE. Distinguished from "the lane failed" because the two send a
      # reader to different places, and a bare fail=1 sends them to the wrong one. 10 = the suite's
      # own setup failed (mktemp, used by several of these); 2 = its subject was missing when it
      # looked (test_knowledge_seam_lanes.sh:7 FATALs this way); 126/127 = the anchor is not
      # executable or not found at all, i.e. a broken install that reached this arm anyway.
      # Every one of them still sets fail — a harness that could not measure did not pass — but it
      # is LABELLED, so the next reader debugs the instrument instead of the lane.
      # The first draft of this loop routed only 10 and let everything else fall into an unlabelled
      # fail=1. That is the same "assumed impossible rather than routed" shape that the sibling
      # commit in lane_runner_check.sh had just written a paragraph against; adversarial review
      # caught the inconsistency between the two files.
      10|2|126|127)
        echo "HARNESS ERROR  $_lbl: the suite exited $_rc — it could not measure, so its verdicts"
        echo "      prove nothing about $_subj. Not a lane failure, and not a pass either."
        fail=1 ;;
      *) fail=1 ;;
    esac
  elif _pkg_accepted_absent "$_anc"; then
    # DECLARED legitimately unshipped — the single source for that answer, not a guess from the
    # environment. The first draft printed "this package predates it", which is a WRONG DIAGNOSIS
    # printed forever on every consumer machine: these anchors do not lag the package, they are
    # deliberately not in it (their subjects do not ship either). A confident wrong reason in a
    # verdict line is worse than no reason, because it is the line the next person greps.
    echo "SKIP  $_lbl (declared legitimately unshipped — package_coverage_check.sh ACCEPTED_ABSENT)"
  else
    # Not present, and NOT declared absent — so either it should be here (deletion / broken
    # install) or the declaration is stale. Routed through the same three-valued helper as the
    # subject arm above, and for the identical reason: `elif _ships_per_files "$_anc"` was a
    # BOOLEAN test over a THREE-valued function, so its exit 2 (package.json unreadable = UNKNOWN)
    # fell through to a green SKIP. That is the lenient branch the helper's own comment forbids
    # ("THE UNKNOWN ARM IS NOT A SKIP"), rebuilt twelve times in one loop, thirty lines under the
    # helper that exists to prevent it. Found by adversarial review; I had written both.
    _absent_subject_verdict "$_lbl (anchor)" "$_anc" || fail=1
  fi
done

# embedded --self-test suites (compaction_probe · judgment_circuit_lint · novelty_claim_check ·
# chamber_witness · digest_landing_check).
# These carry their lanes INSIDE the script (`--self-test`) rather than in a sibling
# test_*_lanes.sh, so the name-list wiring above skipped them silently: lanes existed and ran
# only when a human typed the command. That is built-but-not-wired applied to the anchors themselves
# — a later edit that breaks a lane stays green everywhere the project actually checks
# (high re-review 2026-08-08). Same shape as the block above: subject absent → SKIP, subject present
# but self-test missing → FAIL, never a silent pass.
# chamber_witness/digest_landing_check joined 2026-08-15 (DEBT closed — lane_runner_check.sh
# flagged 4 undeclared self-test subjects; capability_registry_check and relay_channel are the
# other two and do NOT join this loop, each for its own reason, see their own blocks below).
# capability_registry_check was tried here first and reverted: its passing-run output never
# contains 캘리브레이션 as a VERDICT — the only occurrence is inside a Korean *test-case title*
# ("M4: 캘리브레이션 쌍 미선언", capability_registry_check.sh's own self-test), so a future rename
# or trim of that one test name would flip a real PASS into "dispatcher missing?" here (cross-family
# review caught this, 2026-08-15). chamber_witness/digest_landing_check both print a genuine
# terminal "캘리브레이션 통과/실패" line, so they stay.
# publish_freshness_check joined 2026-08-16. It qualifies for the same reason chamber_witness does
# and capability_registry_check does not: its terminal line is a genuine VERDICT
# ("publish_freshness 캘리브레이션 통과/실패"), not a Korean test-case title, so a future rename of
# any single lane cannot flip a real PASS into a false "dispatcher missing?" here.
# portability_lint joined 2026-08-16 (weekly_audit 🟧-3). Same qualification as publish_freshness_check:
# its terminal line is a genuine verdict ("portability-lint 캘리브레이션 통과/실패"), emitted once in
# the summary position, not a Korean test-case title — so renaming any single lane cannot flip a real
# PASS into a false "dispatcher missing?" here. It earns the slot for a second reason: its own
# self-test caught THREE defects in it before it ever shipped (a `|` field separator colliding with
# regex alternation, a line-window too narrow to see the next-line guard, and a rule that flagged its
# own prescription), and a hand-check then removed two false-positive classes that had inflated its
# first repo-wide count by 40% (50 → 30). An instrument that has never been able to fail is not
# calibrated; this one has failed, been fixed, and still discriminates (10 pass / 0 fail).
for _subj in compaction_probe judgment_circuit_lint novelty_claim_check chamber_witness digest_landing_check publish_freshness_check portability_lint; do
  if [ ! -f "scripts/$_subj.sh" ]; then
    _absent_subject_verdict "$_subj --self-test" "scripts/$_subj.sh" || fail=1
  else
    # ⚠️ **문자열 존재로 판정하지 마라.** 초판은 `grep -q -- '--self-test'` 였는데, 그 문자열은
    # 헤더 주석과 usage echo 에도 있어서 **디스패처 한 줄만 지워도 여전히 매치**한다. 그리고
    # 인식 못 한 모드에서 스크립트가 usage 를 찍고 exit 0 을 내므로, selfcheck 는 rc=0 을 보고
    # 조용히 통과했다 — 25개 레인이 통째로 사라져도 `npm test` 는 PASS (high 3차 리뷰 실측).
    # 존재검사가 진위를 못 본다는 그 클래스의 재발이다. **실행이 일어났다는 증거**를 요구한다.
    # `< /dev/null` 필수: 인식 못 한 모드로 떨어지면 스크립트가 stdin 을 기다려 **무한 대기**한다
    # (실측 — 디스패처 제거 known-negative 가 2분 타임아웃). CI 를 멈추는 건 조용한 통과보다 나쁘다.
    # `timeout` 은 GNU coreutils 이고 **stock macOS 에 없다**. 가용성 확인 없이 부르면 rc=127 +
    # 빈 출력 → 아래 `*)` 가 발동해 "dispatcher missing?" 이라는 **틀린 원인**으로 거짓 FAIL 이
    # 난다(실측: homebrew 없는 PATH 에서 3개 subject 전부). 소비자 머신에서 `npm test` 와
    # `prepublishOnly` 를 깨뜨리는 경로다. 정답 폼은 이미 레포에 있다(sync-from-be.sh:134).
    # 없으면 무한대기 방지를 잃는 대신 도는 쪽을 택한다 — `< /dev/null` 이 그 방어의 본체다.
    # `local` OUTSIDE A FUNCTION, which this loop is. bash prints
    #   "selfcheck.sh: line N: local: can only be used in a function"
    # on stderr, the assignment never happens, and `_to` stays empty — so the timeout this line
    # exists to install was NEVER INSTALLED. The guard has been decoration since it was written;
    # the comment above it describing what it protects against was true and unimplemented. Measured
    # 2026-08-13: the error printed three times (once per subject) in a full run, and had been
    # printing in every run before that, unread, because stderr scrolls past a 240-second check.
    # ★ A guard that announces its own failure every single run is still a silent failure if
    # nothing reads the announcement. Fixed by deleting one word.
    _to=""; command -v timeout >/dev/null 2>&1 && _to="timeout 120"
    _st_out="$($_to bash "scripts/$_subj.sh" --self-test < /dev/null 2>&1)"; _st_rc=$?
    # rc=10 (or the sibling harness-error codes) means the subject could not even set up its own
    # known-pair fixture (e.g. mktemp failed) — it never got to run a lane, so it never printed
    # 캘리브레이션 either way. Falling through to the *)  arm below would report the confident but
    # WRONG cause "dispatcher missing?" for an instrument that was never reached. chamber_witness.sh
    # and digest_landing_check.sh both have real mktemp-failure → return 10 paths (cross-family
    # review 2026-08-15); the two original loop members never did, so this arm is a no-op for them.
    case "$_st_rc" in
      10|2|126|127)
        echo "HARNESS ERROR  $_subj --self-test: the suite exited $_st_rc — it could not measure,"
        echo "      so its verdicts prove nothing about $_subj. Not a lane failure, not a pass either."
        fail=1; continue ;;
    esac
    # 🟥 portability_lint 만 **전체 줄 + 비영 카운트**를 요구한다 (적대검증 2026-08-16).
    # 아래 substring 게이트는 «`캘리브레이션` 이라는 글자가 출력 어딘가에 있나» 만 본다. 그래서
    # 그 스크립트의 `RULES` 를 통째로 비우고 `_ck` 호출을 전부 지워도
    # `portability-lint 캘리브레이션 통과: 0 pass / 0 fail` 이 나오고 rc=0 → **PASS** 다.
    # 이건 이 파일이 directional_diff_gate 에 대해 이미 명시적으로 방어한 결함
    # (*"a suite with nothing left in it certifies itself"*)이고, 새 subject 만 약한 게이트에
    # 들어갔다. 저자의 되돌림 프로브(«디스패처 제거 → FAIL»)는 **디스패처 존재**만 입증하고
    # **레인 생존**은 입증하지 않는다 — 더 강한 뮤턴트(레인 삭제)는 안 돌았고, 돌았으면 초록이었다.
    if [ "$_subj" = portability_lint ]; then
      case "$_st_out" in
        *"portability-lint 캘리브레이션 통과: "[1-9]*" pass / 0 fail"*) : ;;
        *) echo "FAIL  $_subj: --self-test 가 «비영 pass + 0 fail» 종단 verdict 를 내지 않았다"
           echo "      (레인이 전멸해도 substring 게이트는 통과한다 — 그래서 여기만 전체 줄을 본다)"
           printf '%s\n' "$_st_out" | tail -3 | sed 's/^/      /'
           fail=1; _st_rc=0 ;;
      esac
      continue
    fi
    case "$_st_out" in
      *캘리브레이션*) : ;;
      *) echo "FAIL  $_subj: --self-test produced no calibration verdict (dispatcher missing?)"
         printf '%s\n' "$_st_out" | head -3 | sed 's/^/      /'
         fail=1; _st_rc=0 ;;   # 이미 FAIL 로 셌으니 아래서 중복 계상 안 한다
    esac
    if [ "$_st_rc" -ne 0 ]; then
      echo "FAIL  $_subj --self-test (exit $_st_rc)"
      printf '%s\n' "$_st_out" | tail -6 | sed 's/^/      /'
      fail=1
    fi
  fi
done

# 🟥 `soul_trace.sh` 는 위 루프에 넣지 않고 **명시 호출**한다 (2026-08-30).
#    루프는 `scripts/$_subj.sh` 로 경로를 조립하므로 **리터럴 grep 에 안 잡힌다** —
#    CI 의 caller-zero ratchet 이 그것을 «호출부 0» 으로 잡았고, 그 판정이 옳다:
#    「호출된다」를 사람 눈으로만 확인할 수 있으면 그건 배선의 증거가 아니다.
#    오늘 아침 `digest_landing_check` 에서 본 것과 같은 형태다.
# 🟥 그리고 이 블록의 초판은 이 파일에 없는 변수(`$ROOT`·`$RC`)를 써서 **selfcheck 자체를
#    죽였다**(`ROOT: unbound variable`, 770줄). 관행은 상대경로 + `fail=1` 이다.
if [ ! -f "scripts/soul_trace.sh" ]; then
  _absent_subject_verdict "soul_trace --self-test" "scripts/soul_trace.sh" || fail=1
else
  if _st_out=$(bash scripts/soul_trace.sh --self-test 2>&1); then
    echo "PASS  soul_trace --self-test ($(printf '%s' "$_st_out" | grep -c '✅') lanes)"
  else
    echo "FAIL  soul_trace --self-test"
    printf '%s\n' "$_st_out" | grep '❌' | head -5
    fail=1
  fi
fi

# capability_registry_check — one of the 4 embedded --self-test subjects lane_runner_check.sh
# flagged (2026-08-15), kept out of the loop above (see that loop's comment) because its own
# terminal line is `통과 N · 실패 N`, never 캘리브레이션 — gate on exit code instead, same shape
# as utterance_landing_check.sh below. Ships via package.json files[], so absence is FAIL, not SKIP.
if [ ! -f scripts/capability_registry_check.sh ]; then
  echo "FAIL  capability_registry_check.sh: missing — it ships via package.json files[], so absence is deletion, not package mode"
  fail=1
elif _out=$(bash scripts/capability_registry_check.sh --self-test < /dev/null 2>&1); then
  echo "PASS  capability_registry_check.sh --self-test ($(printf '%s\n' "$_out" | grep -oE '통과 [0-9]+ · 실패 [0-9]+' | tail -1))"
else
  echo "FAIL  capability_registry_check.sh: --self-test failed"
  _show_failure "$_out"
  fail=1
fi

# test_marker_defense_lanes — Wave 1-D 방어줄(`axis2-defense:`)의 known-pair (2026-08-20).
# 🟥 이 레인이 존재하는 이유 자체가 기록이다: 절차를 흡수해온 sibling 문서가 «훅이 이 줄을 읽는다»·
#    «marker_floor 레인이 핀한다» 고 적었는데 **둘 다 0 히트**였다(컨트롤 crossfamily 21).
#    산문은 이식됐고 기계는 안 왔다. 그래서 FH 가 짓는다 — 우리가 그 주장을 할 땐 참이도록.
if [ ! -f scripts/test_marker_defense_lanes.sh ]; then
  echo "FAIL  test_marker_defense_lanes.sh: missing — axis2-defense 레인에 앵커 없음"
  fail=1
elif _out=$(bash scripts/test_marker_defense_lanes.sh < /dev/null 2>&1); then
  echo "PASS  test_marker_defense_lanes.sh ($(printf '%s\n' "$_out" | grep -oE '[0-9]+ lanes hold' | tail -1))"
else
  echo "FAIL  test_marker_defense_lanes.sh: 방어줄 검증이 바뀌었다"
  _show_failure "$_out"
  fail=1
fi

# test_heavy_classifier_lanes — 4축 게이트의 **라우팅 판별자**에 대한 known-pair (2026-08-20).
# 🟥 이 분류기가 놓치는 경로는 FAIL 하지 않는다 — 게이트가 **조용히 적용되지 않고** 커밋이
#    초록으로 나간다. 가장 조용한 실패 형태이고, 실측상 이걸 시험하는 레인이 **0개**였다
#    (컨트롤: 훅을 언급하는 test_*.sh 는 12개). sibling harness 에서 흡수했고, 핵심은
#    픽스처 목록이 아니라 **훅에서 정규식을 실시간 추출**하는 설계다(픽스처가 대상에서 안 떨어진다).
if [ ! -f scripts/test_heavy_classifier_lanes.sh ]; then
  echo "FAIL  test_heavy_classifier_lanes.sh: missing — 게이트 라우팅 판별자에 앵커 없음"
  fail=1
elif _out=$(bash scripts/test_heavy_classifier_lanes.sh < /dev/null 2>&1); then
  echo "PASS  test_heavy_classifier_lanes.sh (4-way routing known-pair + fail-closed extraction)"
else
  echo "FAIL  test_heavy_classifier_lanes.sh: 게이트 라우팅이 바뀌었다"
  _show_failure "$_out"
  fail=1
fi

# test_satellite_publish_gate_lanes — 위성 공개표면 게이트(2026-08-18, 원정 2차).
# 🟥 이 배선이 없어서 CI 가 `lane-runner: lane suite(s) with no runner and no declaration` 로
# 빨갰다. 스위트를 신설하고 아무 데서도 안 돌린 것 — 검사기의 표현대로
# *"A suite nothing executes is prose."* 그 검사기가 내 신설 레인을 잡았다.
# 🟥 2026-08-20 — 대상 축 가드. 이 두 레인의 **주체는 `scripts/frontier_digest_daily.sh`** 이고
# 그 러너는 의도적으로 출하 대상이 아니다(소비자 계정으로 `claude` CLI 를 태우고 `tracks/` 에 쓴다 —
# `package_coverage_check.sh` ACCEPTED_ABSENT 가 그 이유를 명시한다). 가드 전에는 레인 파일 존재만
# 봤기 때문에, 소비자 설치에서 러너가 없는 채로 레인이 실행돼 **rc=127 로 무더기 FAIL** 했다.
# 실측(레지스트리 실물 2.5.1): publish_gate 4 passed / 21 failed → `SELFCHECK: FAIL`.
# 🟥 그리고 통과한 쪽이 더 나빴다 — "G1b dispatch 자체가 안 일어남 ✅" 은 러너가 **없어서** 통과한
# 거짓 초록이다([[feedback_not_found_is_not_zero_family]]).
# 판정은 `_absent_subject_verdict` 한 곳에 위임한다 — 「files[] 에 없고 부재」면 SKIP,
# 「출하 선언됐는데 부재」면 삭제/깨진 설치라 FAIL, 매니페스트를 못 읽으면 UNDECIDABLE 로 FAIL.
if [ ! -f scripts/frontier_digest_daily.sh ]; then
  _absent_subject_verdict "test_satellite_publish_gate_lanes.sh" "scripts/frontier_digest_daily.sh" || fail=1
elif [ ! -f scripts/test_satellite_publish_gate_lanes.sh ]; then
  echo "FAIL  test_satellite_publish_gate_lanes.sh: missing — publish gate has no anchor"
  fail=1
elif _out=$(bash scripts/test_satellite_publish_gate_lanes.sh < /dev/null 2>&1); then
  echo "PASS  test_satellite_publish_gate_lanes.sh ($(printf '%s\n' "$_out" | grep -oE '[0-9]+ passed, [0-9]+ failed' | tail -1))"
else
  echo "FAIL  test_satellite_publish_gate_lanes.sh: publish gate lanes failed"
  _show_failure "$_out"
  fail=1
fi

# test_satellite_profile_schema_lanes — 프로필 스키마 게이트(2026-08-19, 처방 1+3 · C-2).
# 🟥 신설하고 안 배선해서 `lane-runner: A suite nothing executes is prose` 로 CI 가 빨갰다.
#    바로 윗 블록이 **같은 사고의 기록**인데 그걸 읽고도 같은 실수를 했다 — 산문은 자기
#    바로 위에 있어도 안 읽힌다는 실측이고, 잡은 것은 검사기다.
if [ ! -f scripts/frontier_digest_daily.sh ]; then
  _absent_subject_verdict "test_satellite_profile_schema_lanes.sh" "scripts/frontier_digest_daily.sh" || fail=1
elif [ ! -f scripts/test_satellite_profile_schema_lanes.sh ]; then
  echo "FAIL  test_satellite_profile_schema_lanes.sh: missing — 프로필 스키마 게이트에 앵커 없음"
  fail=1
elif _out=$(bash scripts/test_satellite_profile_schema_lanes.sh < /dev/null 2>&1); then
  echo "PASS  test_satellite_profile_schema_lanes.sh ($(printf '%s\n' "$_out" | grep -oE 'PASS=[0-9]+ FAIL=[0-9]+' | tail -1))"
else
  echo "FAIL  test_satellite_profile_schema_lanes.sh: 프로필 스키마 레인 실패"
  _show_failure "$_out"
  fail=1
fi

# test_evidence_root_psa_lanes — 워크트리에서 기밀성 오버라이드를 찾는가 (R4, 2026-08-18).
# 🟥 `EVIDENCE_ROOT` 에는 레인이 **하나도 없었다** — 그래서 `tracks/` 만 옮기고 gitignored
# 패턴 파일은 안 옮긴 반쪽-픽스가 그대로 출하됐다. 되돌림 프로브로 E1 만 적색 확인.
if [ ! -f scripts/test_evidence_root_psa_lanes.sh ]; then
  echo "FAIL  test_evidence_root_psa_lanes.sh: missing — EVIDENCE_ROOT 기밀성 경로에 앵커 없음"
  fail=1
elif _out=$(bash scripts/test_evidence_root_psa_lanes.sh < /dev/null 2>&1); then
  echo "PASS  test_evidence_root_psa_lanes.sh ($(printf '%s\n' "$_out" | grep -oE '[0-9]+ passed, [0-9]+ failed' | tail -1))"
else
  echo "FAIL  test_evidence_root_psa_lanes.sh: evidence-root psa lanes failed"
  _show_failure "$_out"
  fail=1
fi

# cluster_capability_scan — wired 2026-08-16 (정체성 ①-(a) cluster-wizard).
# 종단 판정줄이 `… 캘리브레이션 통과/실패: N PASS / M FAIL` 이라 위 `_subj` 루프의
# 캘리브레이션 게이트를 **진짜 판정줄로** 만족한다 — 테스트 케이스 제목이 아니다.
# 그래서 이 subject 는 그 루프에 넣어도 안전하지만, 루프는 `scripts/<name>.sh` 존재를
# 전제로 SKIP/FAIL 을 가르므로 여기 직접 디스패치로 둔다(다른 두 capability 검사기와 같은 형태).
# 🟥 exit code 만 보지 않는다 — 레인이 전멸해도 0 이 나온다.
if [ ! -f scripts/cluster_capability_scan.sh ]; then
  echo "FAIL  cluster_capability_scan.sh: missing — it ships via package.json files[], so absence is deletion, not package mode"
  fail=1
elif _out=$(bash scripts/cluster_capability_scan.sh --self-test < /dev/null 2>&1) \
     && printf '%s\n' "$_out" | grep -qE '캘리브레이션 통과: [1-9][0-9]* PASS / 0 FAIL'; then
  echo "PASS  cluster_capability_scan.sh --self-test ($(printf '%s\n' "$_out" | grep -oE '[0-9]+ PASS / [0-9]+ FAIL' | tail -1))"
else
  echo "FAIL  cluster_capability_scan.sh: --self-test failed or produced no terminal verdict line"
  _show_failure "$_out"
  fail=1
fi

# capability_effect_probe — wired 2026-08-16. `lane_runner_check.sh` had been flagging it as an
# embedded --self-test subject with ZERO dispatchers, and that warning was load-bearing: the same
# day, two SECURITY regression anchors were added to this suite (L8 args injection · L9 the git
# metadata channel that let a `read-only` declaration plant a persistent `core.pager` hook in the
# real repo while the probe printed VERIFIED). Unwired, those anchors would never have run anywhere
# but on the author's machine — the exact "built but not wired" shape this repo keeps reproducing.
# Not in the `_subj` loop above: its terminal line is `N PASS / M FAIL`, never 캘리브레이션, so that
# loop's substring gate would go red for a reason unrelated to its lanes (capability_registry_check
# was added there and reverted for precisely this). Direct dispatch instead.
# 🟥 Exit code ALONE is not enough — a suite whose lanes were all deleted still exits 0 (measured
# 2026-08-15 on directional_diff_gate). Require the terminal verdict AS A WHOLE LINE with a
# non-zero PASS count, so an emptied suite cannot certify itself.
if [ ! -f scripts/capability_effect_probe.sh ]; then
  echo "FAIL  capability_effect_probe.sh: missing — it ships via package.json files[], so absence is deletion, not package mode"
  fail=1
elif _out=$(bash scripts/capability_effect_probe.sh --self-test < /dev/null 2>&1) \
     && printf '%s\n' "$_out" | grep -qE '^── capability_effect_probe lanes: [1-9][0-9]* PASS / 0 FAIL ──$'; then
  echo "PASS  capability_effect_probe.sh --self-test ($(printf '%s\n' "$_out" | grep -oE '[0-9]+ PASS / [0-9]+ FAIL' | tail -1))"
else
  echo "FAIL  capability_effect_probe.sh: --self-test failed or produced no terminal verdict line"
  _show_failure "$_out"
  fail=1
fi

# relay_channel — the other embedded --self-test subject kept out of the loop above: its
# `--self-test` just `exec`s the sibling scripts/test_relay_channel_lanes.sh, whose own exit code
# is already disciplined (`[ "$fail" -eq 0 ] || exit 1`), so gating on exit code — same shape as
# utterance_landing_check.sh below — is both sufficient and correct here; a 캘리브레이션 substring
# check would be the wrong instrument for this subject's actual completion marker
# ("N PASS / M FAIL"). Ships via package.json files[] (no ACCEPTED_ABSENT declaration), so absence
# is FAIL, not SKIP — same reasoning as utterance_landing_check.sh's absence branch below.
# Existence check covers BOTH files: relay_channel.sh --self-test just execs the sibling lanes
# file, so a missing sibling alone produces an unrelated-looking `exec: No such file` (rc=127) that
# the generic FAIL branch would misreport as "the lane broke" rather than "the anchor is gone"
# (cross-family review 2026-08-15).
# `_LANE_TO` (defined above, the pair-suite loop's own guard) is reused rather than a fresh
# timeout var — this is, by lane count, the largest suite dispatched anywhere in this file
# (test_relay_channel_lanes.sh runs 45+ lanes, each spawning relay_channel.sh, each spawning its
# own node subprocess), and it was the one dispatch here with NO time bound at all; `< /dev/null`
# only stops a stdin-wait hang, not a runaway child (cross-family review 2026-08-15 — this file's
# own comment two loops up already names exactly this failure shape: an unguarded call does not go
# red, it hangs, and CI dies on a job timeout with the cause unattributable).
if [ ! -f scripts/relay_channel.sh ] || [ ! -f scripts/test_relay_channel_lanes.sh ]; then
  echo "FAIL  relay_channel.sh: missing (relay_channel.sh or its sibling test_relay_channel_lanes.sh) — ships via package.json files[], so absence is deletion, not package mode"
  fail=1
elif _out=$($_LANE_TO bash scripts/relay_channel.sh --self-test < /dev/null 2>&1); then
  echo "PASS  relay_channel.sh --self-test ($(printf '%s\n' "$_out" | grep -oE 'relay_channel lanes: [0-9]+ PASS / [0-9]+ FAIL' | tail -1))"
else
  echo "FAIL  relay_channel.sh: --self-test failed"
  _show_failure "$_out"
  fail=1
fi

# directional_diff_gate — the LAST embedded --self-test subject with no caller, and it stayed
# invisible for a reason worth keeping written down: lane_runner_check.sh reported it WIRED off the
# subject's OWN usage comment (`#   bash scripts/directional_diff_gate.sh --self-test`), so the
# report read `self-test: 10/10 wired` while `grep -rn directional_diff_gate` across every runner
# surface returned zero real callers. The detector was repaired in the same delta as this wiring
# (has_selftest_runner now skips the subject's own file, selftest_dispatched now drops comment
# lines — the two guards has_runner/runner_dispatches always had), which is why the two halves ship
# together: fixing the detector alone would have left a real debt item newly VISIBLE and still
# unrun, and wiring alone would have left the detector able to certify the next such subject.
# ⚠️ The cost of shipping both halves at once, named rather than left for the next reader to
# discover: the report reads `10/10` before the change and `10/10` after it, so the detector repair
# is NOT observable from this repo's own run — reverting the repair alone leaves the count
# unchanged, because the wiring that now exists is real. Its only evidence is the fixture lanes
# L14–L16 in scripts/test_lane_runner_lanes.sh, which is why L16 in particular is load-bearing:
# delete it and the self-exclusion guard has nothing anywhere that would go red.
#
# NOT in the `for _subj in ...` loop above, and that is the same call made for
# capability_registry_check: that loop gates on the substring 캘리브레이션, and this subject's
# terminal verdict is English ("✅ calibration passed (N pairs)"). Adding it there would produce
# "dispatcher missing?" — a confident and wrong cause — for a suite that passes. Gate on exit code
# instead, same shape as the two blocks above. It carries genuine HARNESS-ERROR paths (`exit 10` on
# mktemp failure, `return 10` on unreadable input), so a setup failure must not read as a lane
# failure. Ships via package.json files[] with no ACCEPTED_ABSENT declaration, so absence is
# deletion, not package mode. `< /dev/null` for the same stdin-wait reason the loop above documents.
if [ ! -f scripts/directional_diff_gate.sh ]; then
  echo "FAIL  directional_diff_gate.sh: missing — it ships via package.json files[], so absence is deletion, not package mode"
  fail=1
else
  _out=$($_LANE_TO bash scripts/directional_diff_gate.sh --self-test < /dev/null 2>&1); _dd_rc=$?
  case "$_dd_rc" in
    10|2|124|126|127)
      # 124 is `timeout`'s own kill code, so it belongs in this arm rather than the generic FAIL
      # one below: a suite that was killed did not measure, and calling that "the lane failed"
      # sends the reader to the wrong file. Reachable only because this dispatch is wrapped in
      # `$_LANE_TO` — which is REUSED from the pair-suite loop above, not installed here, so do
      # not delete it from this line believing the timeout is local to this block.
      echo "HARNESS ERROR  directional_diff_gate.sh --self-test: exited $_dd_rc — it could not measure,"
      echo "      so its verdicts prove nothing. Not a lane failure, and not a pass either."
      fail=1 ;;
    0)
      # rc=0 is NOT sufficient, and this is not a hypothetical: the `for _subj in ...` loop above
      # records the measured case where a subject fell into an unrecognized mode, printed its usage
      # banner and exited 0, and the checker read that as a pass while 25 lanes had vanished. This
      # subject happens to exit 10 from that path TODAY — a revert probe (2026-08-15: dispatcher
      # line deleted) confirmed the HARNESS ERROR arm fires — but that is incidental to how its
      # usage function is written, not a property this block should depend on. Require the
      # terminal verdict itself, so a future subject that exits 0 without running anything goes red
      # here rather than certifying an empty run.
      # Whole-LINE match, not a substring, and the pair count must be non-zero. Both halves were
      # named by cross-family review (2026-08-15) and both are reachable in the real subject, not
      # hypothetical: directional_diff_gate.sh's verdict line is
      # `[ "$f" -eq 0 ] && echo "✅ calibration passed ($n pairs)"` — delete every `t` lane and it
      # prints `calibration passed (0 pairs)` and exits 0, i.e. a suite with nothing left in it
      # certifies itself. That is the quietest face of [[feedback_not_found_is_not_zero_family]]
      # (deletion read as a pass), and a substring match would also let a usage banner or a prose
      # line carrying the same words satisfy the gate.
      _dd_verdict=$(printf '%s\n' "$_out" | grep -oE '^✅ calibration passed \([0-9]+ pairs\)$' | tail -1)
      _dd_pairs=$(printf '%s' "$_dd_verdict" | grep -oE '[0-9]+' | tail -1)
      if [ -n "$_dd_verdict" ] && [ "${_dd_pairs:-0}" -gt 0 ]; then
        echo "PASS  directional_diff_gate.sh --self-test ($_dd_verdict)"
      else
        echo "FAIL  directional_diff_gate.sh: --self-test exited 0 without a non-zero calibration verdict (dispatcher missing, or every lane deleted?)"
        _show_failure "$_out"
        fail=1
      fi ;;
    *)
      echo "FAIL  directional_diff_gate.sh: --self-test failed (exit $_dd_rc)"
      _show_failure "$_out"
      fail=1 ;;
  esac
fi

# prepublish_scope_note — an embedded --self-test subject lane_runner_check.sh flagged as having
# no dispatcher anywhere (2026-09-03): its own 7-lane known-pair (does validate.yml still call
# selfcheck.sh — known-positive/negative, missing-workflow, commented-out call, real call beside a
# stale commented one, the real `run: |` block-scalar shape, and echo-mention-is-not-a-call) lives
# behind `--self-test`, and nothing runs it. It IS invoked at publish time (package.json
# `prepublishOnly`) — but that is `check()`, the gate's default argument-less mode, running for
# real; it never exercises the gate's OWN calibration. Not in the `for _subj in ...` loop above:
# its terminal line is `── N pass / M fail`, never 캘리브레이션, same reason capability_registry_check
# and capability_effect_probe were pulled out of that loop. Direct dispatch instead, same shape as
# capability_effect_probe.sh above — whole-line terminal verdict with a non-zero PASS count, so an
# emptied suite cannot certify itself. Ships via package.json files[], so absence is FAIL, not SKIP.
if [ ! -f scripts/prepublish_scope_note.sh ]; then
  echo "FAIL  prepublish_scope_note.sh: missing — it ships via package.json files[], so absence is deletion, not package mode"
  fail=1
elif _out=$(bash scripts/prepublish_scope_note.sh --self-test < /dev/null 2>&1) \
     && printf '%s\n' "$_out" | grep -qE '^  ── [1-9][0-9]* pass / 0 fail$'; then
  echo "PASS  prepublish_scope_note.sh --self-test ($(printf '%s\n' "$_out" | grep -oE '[0-9]+ pass / [0-9]+ fail' | tail -1))"
else
  echo "FAIL  prepublish_scope_note.sh: --self-test failed or produced no terminal verdict line"
  _show_failure "$_out"
  fail=1
fi

# memory-link-check — the memory store is a GRAPH (memory_intent_recall.md: nodes=files,
# edges=[[links]], recall walks one hop). Measured 2026-07-28: 50 of 872 edges pointed at a note
# that existed under a different separator and 22 at nothing — a dead edge returns nothing and is
# indistinguishable from "nothing is related", so the doctrine degraded silently. The checker's
# --fix-separators path WRITES, so its anchor runs here rather than being invoked by hand.
# Package/other-machine mode: the checker self-SKIPs when no memory dir exists.
if [ -f scripts/test_memory_link_check.sh ] && [ -f scripts/memory_link_check.py ]; then
  if ! bash scripts/test_memory_link_check.sh; then
    fail=1
  fi
elif [ -f scripts/memory_link_check.py ]; then
  echo "FAIL  memory-link-check: checker present but scripts/test_memory_link_check.sh missing"
  fail=1
fi

# session-close gate lanes (② harvest-loop discharge + ⑤ card-last) and the ⑤-b card-drift probe.
# Both anchors calibrate scripts/session_close_check.sh, which the pre-push hook runs on every push
# — an uncalibrated instrument there produces exactly the false verdicts the close chain exists to
# prevent. test_card_drift_probe.sh had shipped with ZERO callers since it was written; wiring it
# here closes that, and the anchors are added to files[] in the same change so package mode runs
# them too rather than reporting a deleted anchor.
# consent-class registry floor. Its subject decides whether standing consent may skip an approval
# prompt, so an uncalibrated instrument there hands out autonomy the operator never granted. The
# anchor was written into tests/ with ZERO callers first — the same defect this file already
# records twice above; wiring it here is the fix, not a note about the fix.
if [ ! -f scripts/consent_registry_check.sh ]; then
  _absent_subject_verdict "test_consent_registry.sh" "scripts/consent_registry_check.sh" || fail=1
elif [ -f scripts/test_consent_registry.sh ]; then
  if ! bash scripts/test_consent_registry.sh; then
    fail=1
  fi
else
  echo "FAIL  test_consent_registry.sh: consent_registry_check.sh present but its anchor is missing"
  fail=1
fi

# sidecar_wait stdin plumbing. Its subject is dispatched by auto-decorrelation / steel-quench /
# sim-conductor / AGENTS.md as the REQUIRED wait form, so a regression there silently empties every
# cross-family verification. The anchor's first version shipped in tests/ with ZERO callers — the
# same defect the comment above records for test_card_drift_probe.sh, repeated one file later.
if [ ! -f scripts/sidecar_wait.sh ]; then
  _absent_subject_verdict "test_sidecar_wait_stdin.sh" "scripts/sidecar_wait.sh" || fail=1
elif [ -f scripts/test_sidecar_wait_stdin.sh ]; then
  if ! bash scripts/test_sidecar_wait_stdin.sh; then
    fail=1
  fi
else
  echo "FAIL  test_sidecar_wait_stdin.sh: sidecar_wait.sh present but its anchor is missing"
  fail=1
fi

# fh_node_check.sh gets the same treatment, and for the same reason: three adversarial rounds on it
# produced defects that were ALL negative legs (floor N/A on a non-git install · another framework's
# hooks counted as ours · the Mode D applicability gate silencing its own flagship case), and each
# round's fix reverted a previous one because no anchor pinned it. Subject-present-but-anchor-absent
# is a FAIL, not a skip — that is how an anchor gets quietly dropped.
# Same treatment for the sidecar calibrator, same reason: its verdicts are all distinctions between
# states that look identical from outside ("the sidecar ran" vs "the model I pinned answered",
# "absent" vs "unmeasured"), and its lanes are hermetic stubs, so running them costs nothing.
if [ ! -f scripts/sidecar_calibrate.sh ]; then
  _absent_subject_verdict "test_sidecar_calibrate_lanes.sh" "scripts/sidecar_calibrate.sh" || fail=1
elif [ -f scripts/test_sidecar_calibrate_lanes.sh ]; then
  if ! bash scripts/test_sidecar_calibrate_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_sidecar_calibrate_lanes.sh: sidecar_calibrate.sh present but its anchor is missing"
  fail=1
fi

# The isolated sim runner gets the same treatment, and it earned it in one session. Its lanes are
# hermetic (the `claude` CLI is stubbed), so running them is free — and L8a is the lane that caught
# a FALSE FINDING already published into CLAUDE.md: the runner still passed `--restricted` at the
# call site after the flag had been "made opt-in" in the tools array, so every arm ran with the
# project CLAUDE.md removed, and a two-variable comparison was written up as one. Without this
# suite that retraction does not happen. Subject-present-but-anchor-absent is a FAIL, not a skip.
if [ ! -f scripts/sim_isolated_run.sh ]; then
  _absent_subject_verdict "test_sim_isolated_run_lanes.sh" "scripts/sim_isolated_run.sh" || fail=1
elif [ -f scripts/test_sim_isolated_run_lanes.sh ]; then
  if ! bash scripts/test_sim_isolated_run_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_sim_isolated_run_lanes.sh: sim_isolated_run.sh present but its anchor is missing"
  fail=1
fi

# ⓕ 되돌림 범용 프로브 (six_axis_review_2026-09-04 강화 #2) — 15+ 손짜기 되돌림 스크립트를
# 대체하는 계기다. 자기 자신을 known-pair 로 검증한다(장식 앵커→1, 실물 앵커→0, 복원 보장) —
# 앵커가 아니라 그 앵커를 검증하는 계기이므로 반드시 실행돼야 한다.
if [ ! -f scripts/revert_probe.sh ]; then
  _absent_subject_verdict "test_revert_probe_lanes.sh" "scripts/revert_probe.sh" || fail=1
elif [ -f scripts/test_revert_probe_lanes.sh ]; then
  if ! bash scripts/test_revert_probe_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_revert_probe_lanes.sh: revert_probe.sh present but its anchor is missing"
  fail=1
fi

# 무효 워터마크 — 무효 회차의 «숫자 줄»이 자기 무효를 나르는가.
# 🟥 회차 3 은 자기 게이트가 VOID 를 찍고도 그 숫자만 기록으로 넘어갔다(VOID 낱말은 0회).
#    판정이 표 «밖»에 있었고 사람은 표를 복사하기 때문이다. 그 채널을 닫은 배선의 앵커다.
#    subject-present-but-anchor-absent 는 SKIP 이 아니라 FAIL 이다 — 여기서도 같은 형태를 쓴다.
if [ ! -f scripts/context_continuity_score.sh ]; then
  _absent_subject_verdict "test_verdict_watermark_lanes.sh" "scripts/context_continuity_score.sh" || fail=1
elif [ -f scripts/test_verdict_watermark_lanes.sh ]; then
  if ! bash scripts/test_verdict_watermark_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_verdict_watermark_lanes.sh: context_continuity_score.sh present but its anchor is missing"
  fail=1
fi

# 무효 워터마크 — 무효 회차의 «숫자 줄»이 자기 무효를 나르는가.
# 🟥 회차 3 은 자기 게이트가 VOID 를 찍고도 그 숫자만 기록으로 넘어갔다(VOID 낱말은 0회).
#    판정이 표 «밖»에 있었고 사람은 표를 복사하기 때문이다. 그 채널을 닫은 배선의 앵커다.
#    subject-present-but-anchor-absent 는 SKIP 이 아니라 FAIL 이다 — 여기서도 같은 형태를 쓴다.
if [ ! -f scripts/context_continuity_score.sh ]; then
  _absent_subject_verdict "test_verdict_watermark_lanes.sh" "scripts/context_continuity_score.sh" || fail=1
elif [ -f scripts/test_verdict_watermark_lanes.sh ]; then
  if ! bash scripts/test_verdict_watermark_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_verdict_watermark_lanes.sh: context_continuity_score.sh present but its anchor is missing"
  fail=1
fi

# 무효 워터마크 — 무효 회차의 «숫자 줄»이 자기 무효를 나르는가.
# 🟥 회차 3 은 자기 게이트가 VOID 를 찍고도 그 숫자만 기록으로 넘어갔다(VOID 낱말은 0회).
#    판정이 표 «밖»에 있었고 사람은 표를 복사하기 때문이다. 그 채널을 닫은 배선의 앵커다.
#    subject-present-but-anchor-absent 는 SKIP 이 아니라 FAIL 이다 — 여기서도 같은 형태를 쓴다.
if [ ! -f scripts/context_continuity_score.sh ]; then
  _absent_subject_verdict "test_verdict_watermark_lanes.sh" "scripts/context_continuity_score.sh" || fail=1
elif [ -f scripts/test_verdict_watermark_lanes.sh ]; then
  if ! bash scripts/test_verdict_watermark_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_verdict_watermark_lanes.sh: context_continuity_score.sh present but its anchor is missing"
  fail=1
fi

# daily_report — 어제 커밋 집계. 자기검사 10 레인(known-pair · 멱등 · 부재 표기).
# 🟥 이 파일은 «한 번 존재했다가 사라진» 이력이 있다 — 2026-08-29 에 산출물 1건을 내고
#    git 에 커밋된 적 없이 없어졌고, 로컬 바인딩만 「설치됨」이라 적고 있었다. 배선이
#    그 재발을 막는 층이다. subject 있는데 anchor 없으면 FAIL 이지 skip 이 아니다.
if [ ! -f scripts/daily_report.sh ]; then
  _absent_subject_verdict "daily_report --self-test" "scripts/daily_report.sh" || fail=1
else
  if ! bash scripts/daily_report.sh --self-test >/dev/null 2>&1; then
    echo "FAIL  daily_report --self-test"
    bash scripts/daily_report.sh --self-test 2>&1 | grep '❌' | head -5
    fail=1
  else
    echo "PASS  daily_report --self-test (10 lanes)"
  fi
fi

# launchd_wiring_check — 주기 실행(frontier-digest)이 실제로 배선됐나. 자기검사 10 레인.
# 🟥 이 검사가 없던 동안, 추적본 plist 는 «템플릿»(/path/to/ 플레이스홀더)인데 «바꿨는지»도
#    «걸렸는지»도 보는 것이 0개였다. 소비자는 digest 가 돈다고 믿으면서 한 번도 안 도는 상태로
#    지낼 수 있었고 그 부재는 아무 신호도 안 냈다. subject 있는데 anchor 없으면 FAIL 이다.
if [ ! -f scripts/launchd_wiring_check.sh ]; then
  _absent_subject_verdict "launchd_wiring_check --self-test" "scripts/launchd_wiring_check.sh" || fail=1
else
  if ! bash scripts/launchd_wiring_check.sh --self-test >/dev/null 2>&1; then
    echo "FAIL  launchd_wiring_check --self-test"
    bash scripts/launchd_wiring_check.sh --self-test 2>&1 | grep '❌' | head -5
    fail=1
  else
    echo "PASS  launchd_wiring_check --self-test (10 lanes)"
  fi
fi

# context_continuity_score — 「압축 후 모델이 여전히 답하나」의 격리 채점기. 자기검사 12 레인.
# 🟥 이 배선이 없으면 채점 규칙이 조용히 썩는다. 실제로 이 스크립트는 만든 당일에 세 번 틀렸고
#    (positive 오채점 · 거절 어휘 누락 · negative 게이트 구멍) 셋 다 이 레인들이 잡는 형태다.
#    subject 는 있는데 anchor 가 없으면 FAIL 이지 skip 이 아니다.
if [ ! -f scripts/context_continuity_score.sh ]; then
  _absent_subject_verdict "context_continuity_score --self-test" "scripts/context_continuity_score.sh" || fail=1
else
  if ! bash scripts/context_continuity_score.sh --self-test >/dev/null 2>&1; then
    echo "FAIL  context_continuity_score --self-test"
    bash scripts/context_continuity_score.sh --self-test 2>&1 | grep '❌' | head -5
    fail=1
  else
    echo "PASS  context_continuity_score --self-test (12 lanes)"
  fi
fi

# marker axes-run lanes — 훅의 축 자기대조 형식 검사(§CLAUDE.md 3층 자기 대조)의 앵커.
# ⚠️ 「4축」이라고 적혀 있었는데 2026-08-17 부로 **6축 분기가 생겼다**(마커 날짜 >= 그 날이면
#    기호 키 ⓐ~ⓕ 요구, 미만이면 옛 ASCII 넷). 산문 층이라 레인은 안 깨지고 조용히 stale 이었다.
# subject 부재를 FAIL 로 두는 이유는 branch-claim 블록과 같다: 이 subject 는 pre-commit 훅
# 자체이고 files[] 에 있으므로 «정당한 부재» 가 없다. 부재 = 삭제다.
if [ ! -f templates/.git-hooks/pre-commit ]; then
  echo "FAIL  templates/.git-hooks/pre-commit absent — the gate itself is missing, not a skip"
  fail=1
elif [ -f scripts/test_marker_axes_run_lanes.sh ]; then
  if ! bash scripts/test_marker_axes_run_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_marker_axes_run_lanes.sh: pre-commit present but its axes-run anchor is missing"
  fail=1
fi

# branch-claim lanes — shipped with 27 hand-run lanes and ZERO callers, so CI never ran one of them.
# That is the exact defect its own subject guards against (a gate nobody claims against passes
# forever), reproduced one layer up in its anchor. Same SKIP/FAIL shape: a missing subject is a
# legitimate skip, a present subject with a missing anchor is a real failure. The lanes are hermetic
# (per-PID temp dirs under the worktree, no network, no API spend), so running them here costs
# nothing.
#
# ⚠️ Subject-absent is a FAIL here, NOT the SKIP the sibling blocks use. The SKIP arm exists for
# subjects that may legitimately not ship; branch_claim.sh IS in package.json files[], so it is
# present in the source checkout AND in the published package — absence can only mean deletion.
# Nothing else catches that: measured 2026-08-09, package_coverage_check.sh returns PASS on a
# files[] entry whose file does not exist, so a deleted subject would be green on every surface
# (SKIP here + PASS there + npm silently omitting it). A cross-family reviewer caught this; the
# first draft of this block used SKIP and would have silenced exactly that case.
# Named residual, deliberately not fixed here: the sibling blocks above carry the same hole for
# their own shipped subjects. Fixing them is a separate change with its own verification.
if [ ! -f scripts/branch_claim.sh ]; then
  echo "FAIL  scripts/branch_claim.sh is in package.json files[] but absent — a deleted subject, not a skip"
  fail=1
elif [ -f scripts/test_branch_claim_lanes.sh ]; then
  if ! bash scripts/test_branch_claim_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_branch_claim_lanes.sh: branch_claim.sh present but its anchor is missing"
  fail=1
fi

# prless_delta_scan lanes — «커밋은 됐는데 착륙 경로에 안 올라간» 델타를 세는 advisory 계기.
# 🟥 레인의 하중선은 **음성 컨트롤**이다: 이 레포는 squash 머지를 쓰므로 순진한 per-commit 비교는
#    이미 착륙한 브랜치를 «미착륙» 으로 읽는다(2026-09-19 실측: naive 33/40 · git cherry 17/40 ·
#    이 계기 10/40). 양성만 있는 레인은 «전부 고발하는 계기» 도 통과시킨다.
if [ -f scripts/prless_delta_scan.sh ]; then
  if [ -f scripts/test_prless_delta_scan_lanes.sh ]; then
    if ! bash scripts/test_prless_delta_scan_lanes.sh; then
      fail=1
    fi
  else
    echo "FAIL  test_prless_delta_scan_lanes.sh: prless_delta_scan.sh present but its anchor is missing"
    fail=1
  fi
fi

# temper_check lanes — 🟥 `templates/temper_check.sh` 는 게이트체인 7경로 중 **유일하게 실행 레인이
# 0** 이었다(2026-09-14 frontier-digest 후보 #1 → 독립 재현으로 참 판정). 참조 6곳이 전부 비실행
# 이었고, 그중 하나가 **바로 이 파일의 `bash -n` 목록**이다 — 즉 selfcheck 자신이 «구문 검사» 를
# «검증» 으로 세고 있었다. steel-quench SKILL.md 의 T-1 행은 그 사이 그것을 `measured` 로 선언한다.
# `bash -n` 은 계기가 아니다([[feedback_gate_verification_must_execute]]) — 아래가 그 자리를 실행으로
# 바꾼다. 레인은 격리 픽스처(실물 내용 · 실제 두 커밋 쌍)를 쓰고 네트워크·API 를 안 탄다.
#
# ⚠️ Subject-absent 는 여기서도 FAIL 이다(SKIP 아님) — `templates/temper_check.sh` 는 package.json
# files[] 에 있으므로 부재는 «정당한 미출하» 가 아니라 삭제다. 위 branch_claim 블록과 같은 근거.
# rc=2 는 계기 오류(HARNESS-ERROR)라 rc=1 과 같이 fail 로 접는다 — «못 쟀다» 는 «통과» 가 아니다.
if [ ! -f templates/temper_check.sh ]; then
  echo "FAIL  templates/temper_check.sh is in package.json files[] but absent — a deleted subject, not a skip"
  fail=1
elif [ -f scripts/test_temper_check_lanes.sh ]; then
  if ! bash scripts/test_temper_check_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_temper_check_lanes.sh: temper_check.sh present but its anchor is missing"
  fail=1
fi

# listing_watch — 같은 형태. 🟥 이 블록을 «레인을 짓는 같은 커밋에서» 붙인다: 바로 위 주석이
# 기록한 「레인은 지었는데 selfcheck 배선을 안 했다」 재발이 **최소 다섯 번**이고, 그 다섯 다 CI 나
# lane-runner 가 뒤늦게 잡았다. 순서를 바꾸는 것이 유일한 처방이라 여기서 그렇게 한다.
# 🟥 **2026-08-23 정정 — 「세 번」은 stale 이었다. 그리고 숫자보다 함의가 중요하다**: 네 번째는
# 2026-08-22 에 났는데, **이 주석 바로 아래에서** 났다. 처방이 «읽히는 자리»에 있었는데도 재발한
# 것이다. ⇒ 「주석을 더 잘 쓰자」가 아니라 **「살리언스로는 이 축이 안 닫힌다」**의 근거다.
# (회수 경위: 그 관측은 폐기된 워크트리 사본에만 있었고, peer 세션이 버리기 전에 건져 올렸다.)
# 🟥 **다섯 번째 = 바로 이 주석을 「네 번」으로 고친 그 커밋 자신이다** (2026-08-23, CI 적발).
#    그 델타가 `:546` 의 `chamber_candidate_collect.sh|test_chamber_sig_lanes.sh` 배선 한 줄을
#    **의도 없이 지웠고**, 레인 파일은 그대로 남아 lane-runner 가 「돌리는 게 없는 스위트」로
#    잡았다. 즉 **이 축을 서술하는 행위가 이 축의 다음 사례를 생산했다.** 자력 적발 0.
#    ⚠️ 이번 것은 앞의 넷과 형태가 하나 다르다 — 앞의 넷은 «배선을 안 붙였다»(누락)인데
#    이건 «있던 배선을 지웠다»(회귀)다. 같은 계기(lane-runner)가 둘 다 잡았으므로 한 카운터에
#    두지만, 처방이 갈릴 수 있다는 뜻이므로 접지 말고 여기 적어 둔다.
if [ ! -f scripts/listing_watch.sh ]; then
  echo "FAIL  scripts/listing_watch.sh is in package.json files[] but absent — a deleted subject, not a skip"
  fail=1
elif [ -f scripts/test_listing_watch_lanes.sh ]; then
  if ! bash scripts/test_listing_watch_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_listing_watch_lanes.sh: listing_watch.sh present but its anchor is missing"
  fail=1
fi

# residency_admission — 같은 형태. 🟥 그리고 **또 같은 얼굴이었다**: 2026-08-20 에 이 레인을
# 지으면서 여기 배선을 안 했고, `lane-runner` 가 «돌리는 게 없는 스위트는 산문이다» 로 CI 에서
# 잡았다. 아래 target_freeze 주석이 기록한 그 형태의 **최소 세 번째**다 — 즉 이건 개인 부주의가
# 아니라 «레인을 짓는 사람이 배선 지점을 안 본다» 는 구조적 순서 문제다.
# ⚠️ 2026-08-23: 그 뒤 **네 번째**가 났다(위 listing_watch 주석 참조). 이 «세 번째»는 시점 기록이라
# 그대로 두고, 누계는 위 주석이 갱신본이다 — 두 곳에 같은 카운터를 두면 하나는 반드시 낡는다.
if [ ! -f scripts/residency_admission_check.sh ]; then
  echo "FAIL  scripts/residency_admission_check.sh is wired into pre-commit but absent — a deleted subject, not a skip"
  fail=1
elif [ -f scripts/test_residency_admission_lanes.sh ]; then
  if ! bash scripts/test_residency_admission_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_residency_admission_lanes.sh: residency_admission_check.sh present but its anchor is missing"
  fail=1
fi

# target_freeze — 같은 형태(shipped subject + 자기 앵커). 🟥 이 블록이 없어서 `lane-runner`
# 검사가 «돌리는 게 없는 스위트는 산문이다» 로 정확히 걸었다. 지은 사람이 자기 레인을 안
# 배선한 것이고, 오늘 이 세션이 남의 코드에서 네 번 잡은 그 얼굴이다.
if [ ! -f scripts/target_freeze.sh ]; then
  echo "FAIL  scripts/target_freeze.sh is in package.json files[] but absent — a deleted subject, not a skip"
  fail=1
elif [ -f scripts/test_target_freeze_lanes.sh ]; then
  if ! bash scripts/test_target_freeze_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_target_freeze_lanes.sh: target_freeze.sh present but its anchor is missing"
  fail=1
fi

# And the ablation calibrator, for the third instance of the same reason. Its whole job is telling
# apart states that look identical from outside — "the arm could not answer" vs "the runner is dead"
# vs "the runner read the answer off disk" — and round 2 of its own adversarial review found that
# three of its round-1 fixes had no discriminating lane at all. Unwired lanes are green on one
# machine and nowhere else, which is the case this file exists to prevent. Stub runners, no API
# spend, so running them here costs nothing. Not hermetic w.r.t. the filesystem: two isolation lanes
# create and remove an empty, per-PID, git-invisible directory in the worktree.
# probe-scope lanes — the subject had 15 sibling checkers with an anchor and none of its own, so
# three repairs shipped on 2026-08-03 that could each be reverted with nothing turning red. Same
# SKIP/FAIL shape as the blocks above: a missing SUBJECT is a legitimate skip, a present subject with
# a missing anchor is a real failure.
if [ ! -f scripts/probe_scope_check.sh ]; then
  echo "SKIP  test_probe_scope_lanes.sh (subject scripts/probe_scope_check.sh absent)"
elif [ -f scripts/test_probe_scope_lanes.sh ]; then
  if ! bash scripts/test_probe_scope_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_probe_scope_lanes.sh: probe_scope_check.sh present but its anchor is missing"
  fail=1
fi

# gate-pathspec anchor — wired here 2026-08-04. It was reachable ONLY from templates/.git-hooks/
# pre-commit, i.e. only in a clone where the operator had run `git config core.hooksPath`. Every
# other clone, every CI run, and the npm package carried the anchor file and never executed it —
# the built-but-not-wired shape, one layer up: the anchor for the gate had no anchor of its own.
# That mattered the same day: PR #254 added five known-pairs to it, all of which would have been
# unexecuted outside the author's machine.
# Subject = the two implementations it reads (the hook's HEAVY term and the guard's GUARD_PATHSPEC).
# Absent subject → package/partial surface → legitimate SKIP; present subject with the anchor gone
# → FAIL, same shape as every block above.
# NAMED RESIDUAL (cross-family, gpt-5.5, 2026-08-04): if a distribution that SHOULD be complete
# accidentally drops one subject, this reports SKIP, not FAIL — silent non-coverage. Measured the
# same day: removing `templates/.git-hooks` from package.json `files[]` and running
# scripts/package_coverage_check.sh still PASSED, so no existing anchor catches that omission
# either. Deliberately NOT patched with a stricter branch: the only discriminator available
# ("templates/ exists but the hook does not") would be built on an UNMEASURED assumption about how
# a narrower package is shaped, and this repo's rule is not to build before the constraint is
# measured. What is cheap and honest is naming WHICH subject is missing, so a SKIP is diagnosable
# instead of opaque. Revisit when a real partial distribution is observed.
_gps_missing=""
[ -f templates/.git-hooks/pre-commit ] || _gps_missing="templates/.git-hooks/pre-commit"
[ -f templates/regression_guard.sh ] || _gps_missing="${_gps_missing:+$_gps_missing, }templates/regression_guard.sh"
if [ -n "$_gps_missing" ]; then
  _absent_subject_verdict "gate_pathspec_check.sh" "$_gps_missing" || fail=1
elif [ -f scripts/gate_pathspec_check.sh ]; then
  if ! bash scripts/gate_pathspec_check.sh; then
    fail=1
  fi
else
  echo "FAIL  gate_pathspec_check.sh: the gate implementations are present but their coverage anchor is missing"
  fail=1
fi

if [ ! -f scripts/ablation_calibrate.sh ]; then
  echo "SKIP  test_ablation_calibrate_lanes.sh (subject scripts/ablation_calibrate.sh absent)"
elif [ -f scripts/test_ablation_calibrate_lanes.sh ]; then
  if ! bash scripts/test_ablation_calibrate_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_ablation_calibrate_lanes.sh: ablation_calibrate.sh present but its anchor is missing"
  fail=1
fi

if [ ! -f scripts/fh_node_check.sh ]; then
  _absent_subject_verdict "test_node_check_lanes.sh" "scripts/fh_node_check.sh" || fail=1
elif [ -f scripts/test_node_check_lanes.sh ]; then
  if ! bash scripts/test_node_check_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_node_check_lanes.sh: fh_node_check.sh present but its anchor is missing"
  fail=1
fi

# The codex-doctor root gate. Its subject is a SHIPPED npm binary (package.json files[]), and the
# lane exists because that binary refused every non-npm consumer: it gated on a package.json it
# never reads, so a consumer harness holding every surface the doctor actually audits — AGENTS.md,
# plugins/, SKILL.md — got exit 11 instead of a report. The lanes pin both directions at once:
# the non-npm root now audits, and an empty plugins/ still fails closed rather than printing a
# confident "Skills scanned: 0". Same pairing rule as above — the lane exists only because the
# binary does, so its absence beside a present subject is a FAIL, not a skip.
if [ ! -f bin/fh-codex-doctor.js ]; then
  _absent_subject_verdict "test_codex_doctor_root_lanes.sh" "bin/fh-codex-doctor.js" || fail=1
elif [ -f scripts/test_codex_doctor_root_lanes.sh ]; then
  if ! bash scripts/test_codex_doctor_root_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_codex_doctor_root_lanes.sh: fh-codex-doctor.js present but its anchor is missing"
  fail=1
fi

# Claim propagation — the scanner that catches a RETRACTED NUMBER surviving in another file after the
# canon withdrew it. halffix_propagation_scan.sh cannot: its token rule needs a letter-start ≥10-char
# identifier or a path, so «2.7 %» / «다섯 팔» are structurally invisible to it (measured 2026-09-17:
# a withdrawn five-arm figure sat in CLAUDE.md twice after the canon retracted it, and the scanner's
# output did not list CLAUDE.md at all). Numeric-retraction only, advisory (rc=2 = LIVE), never a
# commit gate — prose-claim retractions are OUT OF SCOPE by measurement (0/8 precision). Same pairing
# rule: the lane exists only because the scanner does, so its absence beside a present subject is a
# FAIL, not a skip.
if [ ! -f scripts/claim_propagation_scan.py ]; then
  _absent_subject_verdict "test_claim_propagation_lanes.sh" "scripts/claim_propagation_scan.py" || fail=1
elif [ -f scripts/test_claim_propagation_lanes.sh ]; then
  if ! bash scripts/test_claim_propagation_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_claim_propagation_lanes.sh: claim_propagation_scan.py present but its anchor is missing"
  fail=1
fi

# Stale-reference lanes — «this value is alive and resolves, but the world it points at moved».
# Added 2026-09-20 after CITATION.cff and four paper versions were found citing the SUPERSEDED v1.0
# record of this very work: every «does this DOI resolve» check passed, because the DOI does resolve.
# 🟥 The suite exists because `new-code-anchor` caught the scanner shipping with no lane that RUNS
# it — the author's own commit said «배선 안 함» and the gate turned that sentence red. A tool's own
# `--self-check` is not an anchor until a runner surface executes it. Same pairing rule as above:
# the lane exists only because the scanner does, so its absence beside a present subject is a FAIL.
if [ ! -f scripts/stale_ref_scan.py ]; then
  _absent_subject_verdict "test_stale_ref_scan_lanes.sh" "scripts/stale_ref_scan.py" || fail=1
elif [ -f scripts/test_stale_ref_scan_lanes.sh ]; then
  if ! bash scripts/test_stale_ref_scan_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_stale_ref_scan_lanes.sh: stale_ref_scan.py present but its anchor is missing"
  fail=1
fi

# Paper-integrity lanes — the three checks the governor ran BY HAND with ad-hoc python on 2026-09-17
# (paper 2; the sister paper had just been rejected by arXiv for 11/17 reference mismatches): every
# cited key defined and every defined key cited (+ NEAR-MISS pairing for [MF24]↔[MF22]-style
# renumbering), no number dropped or invented by a shrink pass, every §N / Sec. N / 섹션 N / 부록 N
# cross-reference resolving to a heading. One suite for three subjects; the pairing key is
# citation_key_check.py and the suite itself asserts numeric_token_diff.py and section_ref_check.py
# exist (L1, FAIL not skip). Same pairing rule as above: the lane exists only because the scanner
# does, so its absence beside a present subject is a FAIL, not a skip.
if [ ! -f scripts/citation_key_check.py ]; then
  _absent_subject_verdict "test_paper_integrity_lanes.sh" "scripts/citation_key_check.py" || fail=1
elif [ -f scripts/test_paper_integrity_lanes.sh ]; then
  if ! bash scripts/test_paper_integrity_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_paper_integrity_lanes.sh: citation_key_check.py present but its anchor is missing"
  fail=1
fi

# session_checklist.py — the «request checklist close report» (2026-09-18): every operator utterance of a
# session (raw transcript + the compaction summaries, two channels) gets a row with an achievement status,
# reason and carry-over proposal, so a human can review the session like a TC report. `check` is FORM only
# (closed status enum · non-empty reason/proposal · evidence on DONE) — it never judges whether a status is
# true. Same pairing rule as above: the lane exists only because the subject does.
if [ ! -f scripts/session_checklist.py ]; then
  _absent_subject_verdict "test_session_checklist_lanes.sh" "scripts/session_checklist.py" || fail=1
elif [ -f scripts/test_session_checklist_lanes.sh ]; then
  if ! bash scripts/test_session_checklist_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_session_checklist_lanes.sh: session_checklist.py present but its anchor is missing"
  fail=1
fi

# The infra-delta half of the same subject. Separate suite, same pairing rule: it exists only because
# fh_node_check.sh does, so its absence beside a present subject is a FAIL, not a skip.
if [ ! -f scripts/fh_node_check.sh ]; then
  _absent_subject_verdict "test_node_infra_delta_lanes.sh" "scripts/fh_node_check.sh" || fail=1
elif [ -f scripts/test_node_infra_delta_lanes.sh ]; then
  if ! bash scripts/test_node_infra_delta_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_node_infra_delta_lanes.sh: fh_node_check.sh present but its anchor is missing"
  fail=1
fi

# SessionStart multi-hook + install-wizard snippet merge. Subject for both = the shipped settings
# snippets; a clone without them is a legitimate SKIP, a clone with them and no anchor is not —
# UNLESS the anchor itself is declared package-mode-optional. test_sessionstart_multihook_lanes.sh
# is exactly that. Its ACCEPTED_ABSENT entry (package_coverage_check.sh) covers a DIFFERENT case than
# this loop implements: that entry's "selfcheck reports it NOT EXERCISED (exit 2)" describes the
# anchor RUNNING and finding no CLI (handled a few lines below, rc==2). It never claimed anything
# about the anchor FILE being missing — a real consumer never gets to run it at all (the file is not
# in files[]), and this loop had no branch for that at all, so every installed package hit
# "FAIL … anchor is missing" (corrected 2026-08-12, cross-family review — an earlier revision of this
# comment miscited the ACCEPTED_ABSENT entry as already covering the missing-file case). Fixed by
# consulting the SAME declaration (`_pkg_accepted_absent`, loaded once near the top of this file) that
# the ref-path block above uses — not an environment predicate — because `.git` presence answers "is
# there a git repo here", not "was this anchor declared intentionally unshipped", and the two diverge
# in a git-tracked vendored tree (a monorepo committing node_modules, `git init` after install): `.git`
# exists there too, which would silently reproduce the original FAIL.
# test_wizard_snippet_merge_lanes.sh is NOT in ACCEPTED_ABSENT (it does ship, per files[]) — routing
# both anchors through the same declaration lookup, rather than hardcoding one as always-FAIL, means a
# future person who genuinely needs to exempt it does so by editing ONE list, not by finding this loop.
for _pair in \
  "templates/settings.SessionStart.snippet.json|scripts/test_sessionstart_multihook_lanes.sh|package-optional" \
  "templates/settings.SessionStart.snippet.json|scripts/test_wizard_snippet_merge_lanes.sh|always-shipped"
do
  _subj="${_pair%%|*}"; _rest="${_pair#*|}"; _anc="${_rest%%|*}"; _mode="${_rest#*|}"
  # This mode field is a gate-verdict policy value (does a missing anchor FAIL or SKIP), not free
  # text — an unrecognized value must not silently fall through to whichever branch string-matching
  # happens to miss. Cross-family review, 2026-08-12: the two known values were previously the only
  # ones exercised, so a typo (e.g. "alway-shipped") landed in the `else` FAIL branch by accident of
  # string mismatch rather than by a checked policy — fail-closed in practice, but undeclared.
  case "$_mode" in
    package-optional|always-shipped) ;;
    *)
      echo "FAIL  ${_anc##*/}: unrecognized pair mode '$_mode' (expected package-optional or always-shipped)"
      fail=1
      continue
      ;;
  esac
  if [ ! -f "$_subj" ]; then
    _absent_subject_verdict "${_anc##*/}" "$_subj" || fail=1
  elif [ -f "$_anc" ]; then
    # THREE-valued, like the session-close anchors above — and for a third reason they do not have.
    # test_sessionstart_multihook_lanes.sh measures what the LIVE `claude` CLI does with several
    # SessionStart hooks on one matcher. It declares exit 2 = NOT EXERCISED (no CLI / no auth /
    # opt-out). A CI runner structurally cannot have that CLI, so collapsing 2 into fail=1 makes
    # every Linux run red forever — over-blocking, which is how a red CI stops being read at all
    # (the same reasoning that keeps the session-close check advisory on ordinary pushes).
    # 2 does NOT set fail, and it prints a line that cannot be misread as a pass. On a machine that
    # DOES have the CLI the suite runs in full and a real failure still exits 1.
    # Measured 2026-08-04: the first draft of this wiring flattened 2 into fail=1 and turned CI red
    # while the suite had correctly reported "NOT EXERCISED — the `claude` CLI is not on PATH" —
    # i.e. it rebuilt, ten lines below the comment warning against it, the exact flattening defect.
    bash "$_anc"; _rc=$?
    if [ "$_rc" -eq 2 ]; then
      echo "NOT EXERCISED  ${_anc##*/}: this environment cannot run the measurement (exit 2 — never a pass)"
    elif [ "$_rc" -ne 0 ]; then
      fail=1
    fi
  elif [ "$_mode" = "package-optional" ] && _pkg_accepted_absent "$_anc"; then
    echo "SKIP  ${_anc##*/} (declared ACCEPTED_ABSENT — CLI/cost-gated, see package_coverage_check.sh)"
  else
    echo "FAIL  ${_anc##*/}: $_subj present but its anchor is missing"
    fail=1
  fi
done

# Two guards that read the AUTHOR's own actions rather than the repo's files. Both were added
# 2026-07-31; the pipe-verdict lane shipped in PR #209 WITHOUT this wiring, which is itself the
# half-fix class the second guard exists to catch — found by running that guard on this repo.
if [ ! -f scripts/sidecar_calibrate.sh ]; then
  _absent_subject_verdict "test_ollama_panel_lanes.sh" "scripts/sidecar_calibrate.sh" || fail=1
elif [ -f scripts/test_ollama_panel_lanes.sh ]; then
  if ! bash scripts/test_ollama_panel_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_ollama_panel_lanes.sh: sidecar_calibrate.sh present but its ollama-leg anchor is missing"
  fail=1
fi

# prior-art prompt (T2) — 새 메커니즘을 쓰기 직전 «내부·외부·숙고» 를 모델 컨텍스트에 넣는 훅.
# 레인의 다수가 **음성**이다: 이 훅의 사활은 막는 것이 아니라 소음이 아닌 것이다.
if [ ! -f scripts/prior_art_prompt.sh ]; then
  _absent_subject_verdict "test_prior_art_prompt_lanes.sh" "scripts/prior_art_prompt.sh" || fail=1
elif [ -f scripts/test_prior_art_prompt_lanes.sh ]; then
  if ! bash scripts/test_prior_art_prompt_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_prior_art_prompt_lanes.sh: prior_art_prompt.sh present but its anchor is missing"
  fail=1
fi

# outbound-query 위생 린트 — 나가는 질의에 내부 토큰이 실렸는지. 레인이 이 가드의 fail-closed
# 분기 4개(라이브러리 부재·defaults 부재·override 부재·상태값 오염)에 각각 짝을 갖는다.
if [ ! -f scripts/outbound_query_guard.sh ]; then
  _absent_subject_verdict "test_outbound_query_lanes.sh" "scripts/outbound_query_guard.sh" || fail=1
elif [ -f scripts/test_outbound_query_lanes.sh ]; then
  if ! bash scripts/test_outbound_query_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_outbound_query_lanes.sh: outbound_query_guard.sh present but its anchor is missing"
  fail=1
fi

# outbound-query PreToolUse 훅 — 위 CLI 가드의 **배선**(WebSearch|WebFetch). 레인이 두 층 판정
# (override→deny · defaults→advisory), 미측정 4가지→deny, N/A 무음, 그리고 🟥 «출력에 토큰 값이
# 없다»(H10)를 각각 짝으로 잡는다. 마지막 것이 이 훅의 존재 이유를 지키는 레인이다.
if [ ! -f scripts/outbound_query_hook.sh ]; then
  _absent_subject_verdict "test_outbound_query_hook_lanes.sh" "scripts/outbound_query_hook.sh" || fail=1
elif [ -f scripts/test_outbound_query_hook_lanes.sh ]; then
  if ! bash scripts/test_outbound_query_hook_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_outbound_query_hook_lanes.sh: outbound_query_hook.sh present but its anchor is missing"
  fail=1
fi

if [ ! -f scripts/pipe_verdict_guard.sh ]; then
  _absent_subject_verdict "test_pipe_verdict_guard_lanes.sh" "scripts/pipe_verdict_guard.sh" || fail=1
elif [ -f scripts/test_pipe_verdict_guard_lanes.sh ]; then
  if ! bash scripts/test_pipe_verdict_guard_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_pipe_verdict_guard_lanes.sh: pipe_verdict_guard.sh present but its anchor is missing"
  fail=1
fi

if [ ! -f scripts/backtick_guard.sh ]; then
  _absent_subject_verdict "test_backtick_guard_lanes.sh" "scripts/backtick_guard.sh" || fail=1
elif [ -f scripts/test_backtick_guard_lanes.sh ]; then
  if ! bash scripts/test_backtick_guard_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_backtick_guard_lanes.sh: backtick_guard.sh present but its anchor is missing"
  fail=1
fi

if [ ! -f scripts/halffix_propagation_scan.sh ]; then
  _absent_subject_verdict "test_halffix_lanes.sh" "scripts/halffix_propagation_scan.sh" || fail=1
elif [ -f scripts/test_halffix_lanes.sh ]; then
  if ! bash scripts/test_halffix_lanes.sh; then
    fail=1
  fi
else
  echo "FAIL  test_halffix_lanes.sh: halffix_propagation_scan.sh present but its anchor is missing"
  fail=1
fi

# hook_source_gate 는 subject 가 session_close_check.sh 가 아니라 별도로 돌린다 — 위 루프의
# SKIP 조건(subject 부재)에 얹으면 엉뚱한 이유로 건너뛴다.
if [ -f scripts/test_hook_source_gate_lanes.sh ]; then
  if bash scripts/test_hook_source_gate_lanes.sh >/dev/null 2>&1; then
    echo "PASS  test_hook_source_gate_lanes.sh"
  else
    echo "FAIL  test_hook_source_gate_lanes.sh (SessionStart source 게이트 쌍 붕괴)"
    bash scripts/test_hook_source_gate_lanes.sh 2>&1 | grep "❌" | head -5 | sed "s/^/      /"
    fail=1
  fi
fi

for _anchor in scripts/test_session_close_lanes.sh scripts/test_card_drift_probe.sh scripts/test_session_close_chain_lanes.sh; do
  if [ ! -f scripts/session_close_check.sh ]; then
    _absent_subject_verdict "${_anchor##*/}" "scripts/session_close_check.sh" || fail=1
  elif [ -f "$_anchor" ]; then
    # Preserve the anchor's two failure CLASSES instead of flattening them into one `fail=1`.
    # An anchor exits 3 when a fixture's premise never obtained — "this run's verdicts prove
    # nothing" — which is a different instruction to whoever reads the CI summary than exit 1's
    # "the gate is broken". Collapsing them here would rebuild, at the only wired caller, the
    # triage ambiguity the anchors' own exit codes exist to remove (Wave-3, 2026-08-02).
    # 3 and not 2: bash itself returns 2 on a syntax error, so a rotted anchor must not be able to
    # impersonate a fixture premise failure. Both classes still set fail=1 — a fixture error is a
    # failed run, it is just a differently-diagnosed one.
    bash "$_anchor"; _rc=$?
    if [ "$_rc" -eq 3 ]; then
      echo "FIXTURE ERROR  ${_anchor##*/}: a lane premise never obtained — its verdicts prove nothing (exit 3, not a gate failure)"
      fail=1
    elif [ "$_rc" -ne 0 ]; then
      fail=1
    fi
  else
    # subject present, anchor gone => the calibration was deleted. Real failure, not a skip.
    echo "FAIL  ${_anchor##*/}: session_close_check.sh present but its anchor is missing"
    fail=1
  fi
done

# sync_guard_check.sh — same anchor contract, wired here for the first time (2026-08-02). It had NO
# automated caller at all: a known-pair anchor for the destination-newer guard that only ever ran
# when a human remembered to type it. "A guard nobody re-tests degrades into a comment" is that
# file's own opening argument, and it applied to the anchor itself. Guarded on its subject's
# presence because the npm package ships a narrower surface than the source tree.
if [ ! -f scripts/sync-to-be.sh ]; then
  echo "SKIP  sync_guard_check.sh (subject scripts/sync-to-be.sh absent)"
elif [ -f scripts/sync_guard_check.sh ]; then
  bash scripts/sync_guard_check.sh; _rc=$?
  if [ "$_rc" -eq 3 ]; then
    echo "FIXTURE ERROR  sync_guard_check.sh: a lane premise never obtained — its verdicts prove nothing (exit 3, not a guard failure)"
    fail=1
  elif [ "$_rc" -ne 0 ]; then
    fail=1
  fi
else
  echo "FAIL  sync_guard_check.sh: sync-to-be.sh present but its anchor is missing"
  fail=1
fi

# probe_scope_check.sh — its known-pair controls. Wired here because the probe set is hand-maintained and
# nothing else enforces its own anti-stale rule: a heading-direction
# bug scored a 7-probe section as UNMEASURED (98% vs the true 51%), then a narrow anchor regex reported
# 7 live probe scopes as stale. Control B now walks EVERY scope in probes.md and fails closed (exit 3,
# number withheld) when one no longer resolves — which is also the anti-stale rule probes.md already
# states for itself, finally given a checker.
# Package mode is decided by the SUBJECT's own absence. The first draft used `.claude/rules` as the
# discriminator on the belief that it does not ship — measured false: package.json files[] carries
# `.claude/rules/fh_4axis_gate.md`, so in an installed package `.claude/rules` EXISTS while the probe
# corpus does not. That made the SKIP arm unreachable and every consumer's `npm test` hard-fail with a
# message misdiagnosing the package as a source tree. Avoiding a silent pass is not a licence to
# over-block the normal case. `scripts/probe_scope_check.sh` is ACCEPTED_ABSENT and genuinely never
# ships, so its absence is the one honest package signal here.
if [ ! -f scripts/probe_scope_check.sh ] && [ ! -f .claude/regression/probes.md ]; then
  echo "SKIP  probe_scope_check.sh (package mode: neither the instrument nor its corpus ships)"
elif [ ! -f .claude/regression/probes.md ]; then
  echo "FAIL  probe_scope_check.sh: source tree but .claude/regression/probes.md is missing — the check cannot run — UNVERIFIED, not clean"
  fail=1
elif [ -f scripts/probe_scope_check.sh ]; then
  bash scripts/probe_scope_check.sh --self-test >/dev/null 2>&1; _rc=$?
  if [ "$_rc" -eq 3 ]; then
    echo "FAIL  probe_scope_check.sh: CONTROL FAILED — a probe Scope no longer resolves to a section (the probe set no longer says what it defends)"
    bash scripts/probe_scope_check.sh --self-test 2>&1 | grep -E "STALE|NOFILE|control" | head -12
    fail=1
  elif [ "$_rc" -ne 0 ]; then
    echo "FAIL  probe_scope_check.sh: self-test exited $_rc"
    fail=1
  else
    echo "PASS  probe_scope_check.sh (known-pair + scope-resolution controls hold)"
  fi
else
  echo "FAIL  probe_scope_check.sh: probe set present but the scope checker is missing"
  fail=1
fi

# utterance landing check — the close chain's CONTENT anchor, and it has to be RUN, not parsed.
# Measured 2026-08-08 on the branch that introduced it: the only thing in this file that touched
# `scripts/utterance_landing_check.sh` was the `bash -n` syntax sweep at the top. Syntax passing is
# not the instrument working, so the script shipped via files[] with zero behavioural callers —
# [[feedback_built_but_not_wired]] in its purest form, where the sole caller is prose in CLAUDE.md.
#
# What the self-test defends is specifically the DEGRADE DIRECTION. This checker's whole reason for
# existing is that a dead grep prints zero hits and zero hits read as "nothing landed" — a fail-open
# that manufactures a clean verdict out of a broken instrument. Its known-pair set pins that apart:
# a genuine miss must exit 1 while a dead control must exit 10, and 10 must never collapse into 1.
# Left unrun, the file rots exactly where it is load-bearing and nothing here would notice.
# Absence is a FAIL, not a SKIP — and the distinction is mechanical, not stylistic. The SKIP arm
# above for `probe_scope_check.sh` is correct because that file genuinely never ships; this one is
# listed in package.json files[], so in BOTH a source tree and an installed package it must be here.
# A SKIP would convert `rm scripts/utterance_landing_check.sh` into a green run — deletion as a
# clean bill of health, which is the same fail-open shape the checker itself exists to refuse.
if [ ! -f scripts/utterance_landing_check.sh ]; then
  echo "FAIL  utterance_landing_check.sh: missing — it ships via package.json files[], so absence is deletion, not package mode"
  fail=1
elif _out=$(bash scripts/utterance_landing_check.sh --self-test 2>&1); then
  echo "PASS  utterance_landing_check.sh (known-pair self-test: control-death → 10, target-miss → 1)"
else
  echo "FAIL  utterance_landing_check.sh: self-test failed — the close-chain content anchor cannot be trusted"
  _show_failure "$_out"
  fail=1
fi

# tag/version consistency guard. Wired with the guard it anchors: the guard exists because a wrong
# tag reached the remote and a publish from the wrong tree was stopped only by npm's own collision
# check, so an unrun anchor here would be the same luck-as-floor arrangement one layer up.
if [ ! -f templates/.git-hooks/pre-push ]; then
  _absent_subject_verdict "test_tag_version_lanes.sh" "templates/.git-hooks/pre-push" || fail=1
elif [ -f scripts/test_tag_version_lanes.sh ]; then
  # RUN-ONCE, CAPTURE (2026-08-05) — rationale in the sync_from_be_lanes block later in this file.
  if _out=$(bash scripts/test_tag_version_lanes.sh 2>&1); then
    echo "PASS  test_tag_version_lanes.sh (mismatch blocks · match silent · scope · override)"
  else
    echo "FAIL  test_tag_version_lanes.sh: the tag/version guard would mis-route"
    _show_failure "$_out"
    fail=1
  fi
else
  echo "FAIL  test_tag_version_lanes.sh: pre-push present but its anchor is missing"
  fail=1
fi

# Shipped-manifest version lockstep. Distinct from the tag lane above: that one compares the git TAG
# to package.json; this one compares package.json to every version string it SHIPS — including the
# per-plugin entries inside marketplace.json, which the tag lane never opens. Measured 2026-08-06:
# a bump left the second marketplace entry behind and the tag lane passed 8/8 straight through it.
if [ ! -f scripts/version_lockstep_check.sh ]; then
  _absent_subject_verdict "test_version_lockstep_lanes.sh" "scripts/version_lockstep_check.sh" || fail=1
elif [ -f scripts/test_version_lockstep_lanes.sh ]; then
  if _out=$(bash scripts/test_version_lockstep_lanes.sh 2>&1); then
    echo "PASS  test_version_lockstep_lanes.sh (drift blocks · aligned silent · unreadable = exit 2, not pass)"
  else
    echo "FAIL  test_version_lockstep_lanes.sh: the shipped-manifest lockstep guard would mis-route"
    _show_failure "$_out"
    fail=1
  fi
else
  echo "FAIL  test_version_lockstep_lanes.sh: version_lockstep_check.sh present but its anchor is missing"
  fail=1
fi

# ④-e dispatch-log reconciliation + its tally hook. Wired in the same commit that ships them: the
# obligation they mechanize lost 20/20 in a single session, so leaving the checker itself unrun
# would be the same defect one layer up.
if [ -f scripts/test_dispatch_log_lanes.sh ]; then
  if _out=$(bash scripts/test_dispatch_log_lanes.sh 2>&1); then
    echo "PASS  test_dispatch_log_lanes.sh (date-spelling + verdict + tally-hook lanes)"
  else
    echo "FAIL  test_dispatch_log_lanes.sh: the dispatch-log reconciliation would mis-report"
    _show_failure "$_out"
    fail=1
  fi
fi

# selfcheck's own subject-presence discriminators. Every other guard under scripts/ has a lane suite;
# this decision had none, and it shipped two mis-routings in one session — a two-arm form that fell
# through in silence, then a package discriminator keyed on a file that actually ships. Both are
# known-POSITIVEs in the suite, so neither can come back green.
if [ -f scripts/test_selfcheck_state_lanes.sh ]; then
  if _out=$(bash scripts/test_selfcheck_state_lanes.sh 2>&1); then
    echo "PASS  test_selfcheck_state_lanes.sh (four input states + both shipped mis-routings)"
  else
    echo "FAIL  test_selfcheck_state_lanes.sh: a subject-presence discriminator would mis-route"
    _show_failure "$_out"
    fail=1
  fi
fi

# test_lane_runner_lanes.sh — the checker that finds unrun suites had no suite of its own until
# 2026-08-13. It guarded its own WIRING (it fails when selfcheck stops calling it) and nothing
# measured its BEHAVIOUR — the shape it exists to catch, one level up. Wired in the same change
# that ships it, for the reason the block above states: a suite landed unwired is the defect.
if [ -f scripts/test_lane_runner_lanes.sh ]; then
  if _out=$(bash scripts/test_lane_runner_lanes.sh 2>&1); then
    echo "PASS  test_lane_runner_lanes.sh (org seam: no-op · suppression · fail-closed · controls)"
  else
    echo "FAIL  test_lane_runner_lanes.sh: the unrun-suite detector's own verdicts have drifted"
    _show_failure "$_out"
    fail=1
  fi
elif _ships_per_files "scripts/test_lane_runner_lanes.sh"; then
  echo "FAIL  test_lane_runner_lanes.sh is DECLARED SHIPPED but absent — deletion or broken install"
  fail=1
fi

# test_outbound_pr_gate_lanes.sh — 발신 전 3프로브의 앵커. 출하물이므로 파일이 있으면 무조건 돈다.
# 🟥 배선을 같은 변경에서 한다: 실측 — 비소유 레포 outbound 9건 중 기술 결함 지적 3건이 전부 같은
# 형태(우리 가드·테스트가 «대상의 모형» 위에서 돌았고 메인테이너의 증거는 우리가 한 번도 안 돌린
# 실행)였고, 규칙은 CLAUDE.md 에 이미 있었으나 배선이 없었다. 산문으로 둔 채 올리면 그 1/15 를 반복한다.
if [ -f scripts/test_outbound_pr_gate_lanes.sh ]; then
  if _out=$(bash scripts/test_outbound_pr_gate_lanes.sh 2>&1); then
    echo "PASS  test_outbound_pr_gate_lanes.sh (trigger scoping · standpoint rung · enum class-closing · revert probe)"
  else
    echo "FAIL  test_outbound_pr_gate_lanes.sh: the outbound-PR gate's verdicts have drifted"
    _show_failure "$_out"
    fail=1
  fi
elif _ships_per_files "scripts/test_outbound_pr_gate_lanes.sh"; then
  echo "FAIL  test_outbound_pr_gate_lanes.sh is DECLARED SHIPPED but absent — deletion or broken install"
  fail=1
fi

# test_publish_verify_poll_lanes.sh — 발행 «확인» 예산의 앵커. 주체가 ACCEPTED_ABSENT(이 레포의
# 릴리스 파이프라인 부품, 소비자 호출부 없음)이므로 소비자 install 에서는 파일이 없고 SKIP 이다.
# 🟥 SKIP 은 PASS 가 아니다 — 이 레포에서 실제로 도는 것이 이 배선의 유일한 검증면이다.
# 무엇을 지키나: 「전파 지연」과 「발행 실패」가 같은 종료코드로 접히지 않는 것. 접혔던 실측이
# v3.12.0 이고, 그 빨강이 v3.2.0·v3.4.0 에서 손 발행을 훈련시켜 OIDC 경로를 깨뜨렸다.
if [ -f scripts/test_publish_verify_poll_lanes.sh ]; then
  if _out=$(bash scripts/test_publish_verify_poll_lanes.sh 2>&1); then
    echo "PASS  test_publish_verify_poll_lanes.sh (not_yet_visible != failure · instrument-error stays distinct · calibration)"
  else
    echo "FAIL  test_publish_verify_poll_lanes.sh: propagation-lag and publish-failure have been conflated again"
    _show_failure "$_out"
    fail=1
  fi
elif _ships_per_files "scripts/test_publish_verify_poll_lanes.sh"; then
  echo "FAIL  test_publish_verify_poll_lanes.sh is DECLARED SHIPPED but absent — deletion or broken install"
  fail=1
fi

# sync_from_be_lanes.sh — the RETURN path's anchor. Wired in the same change that ships it: the
# script had an operator-side caller (a SessionStart hook outside this repo) while its 70 lanes had
# NO caller anywhere, which is the shape this repo keeps re-finding — a transport that writes into
# the hub, guarded by a suite nothing runs. Same subject-presence idiom as the block above: the
# subject is operator-private and does not ship, so package mode legitimately skips; subject present
# with the anchor gone is a deleted calibration, not a skip.
if [ ! -f scripts/sync-from-be.sh ]; then
  echo "SKIP  sync_from_be_lanes.sh (subject scripts/sync-from-be.sh absent)"
elif [ -f scripts/sync_from_be_lanes.sh ]; then
  # ── RUN-ONCE, CAPTURE — canonical note for the four LANE BLOCKS (2026-08-05) ─────────────────
  # SCOPE, stated precisely because the first draft of this note over-claimed: this covers the four
  # lane blocks only (tag-version · dispatch-log · selfcheck-state · sync_from_be). The same
  # evidence-discarding shape SURVIVES in `check()` at the top of this file and in the
  # probe_scope_check caller — both named there, both deliberately out of scope, both still open.
  # An adversarial round caught the original "all four sites in this file" wording as a false
  # completion claim: it would have stopped the next reader from re-searching. Half a fix with a
  # done-label on it is worse than half a fix.
  # The old form ran the suite twice: once discarded to /dev/null to decide, once re-run to print.
  # For a DETERMINISTIC suite that is merely wasteful. For a non-deterministic one it destroys the
  # evidence: the failing run's output goes to /dev/null and the reader is shown the SECOND run,
  # which may pass. Measured here 2026-08-04 (run 30955950695) — CI printed
  #     FAIL  sync_from_be_lanes.sh: return-path lanes failed
  #     ════ lanes: 70 passed · 0 failed ════
  # i.e. a FAIL verdict over a PASSING transcript, and the actual failure was never recorded
  # anywhere. That is why this lane sat "flaky, cause unknown" on the session card for two days:
  # the instrument was discarding the only evidence that could close it. Reproduced as a known
  # pair before this edit (arm A run-twice → evidence lost + self-contradiction; arm B run-once →
  # evidence preserved), so the fix is anchored, not asserted.
  # NOTE this does NOT make the suite deterministic — the underlying non-determinism is still
  # UNDIAGNOSED and stays an open item. It makes the next occurrence diagnosable instead of
  # self-erasing. Do not read a green CI after this change as the flake being fixed.
  if _out=$(bash scripts/sync_from_be_lanes.sh 2>&1); then
    echo "PASS  sync_from_be_lanes.sh (return-path lanes)"
  else
    echo "FAIL  sync_from_be_lanes.sh: return-path lanes failed"
    _show_failure "$_out"
    fail=1
  fi
else
  echo "FAIL  sync_from_be_lanes.sh: sync-from-be.sh present but its anchor is missing"
  fail=1
fi

# sync_to_be_lanes.sh — the FORWARD path's anchor (added 2026-08-14, pmh-dev#69). Same shape as the
# return-path block above and wired in the same change that ships it, for the same reason: a
# transport with real reported defects (a real add/add conflict, a real hard-abort risk) had no
# suite exercising it at all before this. Same subject-presence idiom, same run-once-capture
# discipline (a lane suite discarded to /dev/null on a non-deterministic run already burned two days
# on this exact file's sibling).
if [ ! -f scripts/sync-to-be.sh ]; then
  echo "SKIP  sync_to_be_lanes.sh (subject scripts/sync-to-be.sh absent)"
elif [ -f scripts/sync_to_be_lanes.sh ]; then
  if _out=$(bash scripts/sync_to_be_lanes.sh 2>&1); then
    echo "PASS  sync_to_be_lanes.sh (forward-path lanes)"
  else
    echo "FAIL  sync_to_be_lanes.sh: forward-path lanes failed"
    _show_failure "$_out"
    fail=1
  fi
else
  echo "FAIL  sync_to_be_lanes.sh: sync-to-be.sh present but its anchor is missing"
  fail=1
fi

# Referenced-path existence is a source-tree check. The npm package intentionally
# ships a narrower runtime surface, so package-mode selfcheck skips this section.
# ⚠️ The skip predicate used to be `[ -d ".claude/rules" ]`. That broke the moment the tarball
# started shipping PART of that directory (`.claude/rules/fh_4axis_gate.md` etc. are in files[]) —
# the directory then exists in BOTH source and package mode, so the check never skipped in package
# mode at all. It ran, extracted refs from the shipped CLAUDE.md/.claude/rules/*.md (which still
# name `.claude/regression/ablation_verdicts.md` and `scripts/probe_scope_check.sh` — both
# deliberately unshipped, per package_coverage_check.sh's own ACCEPTED_ABSENT list), then tried
# `git check-ignore` against a tree with no `.git` at all (a real `npm pack` tarball is not a git
# repo) — that call errors rather than confirming "ignored", so the fallback `[ -f "$p" ]` ran and
# correctly found nothing, and the check reported FAIL on two paths it had already been told, one
# check over, were fine to omit. First fix (2026-08-12, reship axis, card §🔱⑮ G/D) swapped the skip
# predicate for `[ -e .git ]`. Cross-family review then caught that `[ -e .git ]` answers a different
# question than the one this check needs: a git-TRACKED vendor of this package (a monorepo that
# commits node_modules, or a consumer who runs `git init` after installing) has `.git` and IS a
# legitimate package consumer, yet the predicate would route it into the full extraction and
# reproduce the exact original FAIL on these same two paths — the fix would have closed the bug only
# in the one shape it was tested against. The general fix is not a better environment predicate; it
# is not re-deriving "is this legitimately absent" from the environment at all when a DECLARED answer
# already exists — `_pkg_accepted_absent()`, loaded once near the top of this file — this block now
# consults it before FAILing, so the two paths render SKIP regardless of how `.git` happens to be
# shaped in the consumer's tree. `[ -e .git ]` still gates whether the SCAN runs at all (an installed
# package with no tracked-refs source to lint), which is a real and unrelated question.
if [ -e ".git" ]; then
  # Backtick-quoted repo-relative file refs in the always-loaded governance surface
  # (CLAUDE.md + .claude/rules/*.md) must exist. Phantom-reference class recurred
  # N>=3 in the 2026-06-11 audit window — instrument-not-habit.
  # Extract first, then count. Streaming the extractor straight into the loop meant an
  # extractor that produced nothing (CLAUDE.md absent, 2>/dev/null swallowing a grep error,
  # the backtick convention changing) ran the loop zero times, printed nothing, and left
  # fail=0 → SELFCHECK: PASS. The check would have silently ceased to exist while still
  # reporting a pass — the same shape count_check.sh:71 already guards against with its
  # impossible-zero rule. fh-meta always has refs; zero means the instrument broke.
  # EXTRACTION MOVED OFF grep (2026-07-31, measured on the first real CI run). The pipeline used to
  # be `grep -hoE` + sed + `grep -E` over CLAUDE.md, which is Korean-heavy. On macOS (BSD grep, UTF-8
  # locale) it returned ~50 refs; on the ubuntu runner (GNU grep, LANG unset => C locale) it returned
  # ZERO, and the impossible-zero guard below is the only reason that surfaced as a failure instead
  # of "no refs, all clean". Same root cause as the card-drift probe failing its positive lanes in
  # the same run: multibyte text through locale-dependent grep.
  # python3 reads the files as UTF-8 explicitly, so this extractor no longer has a locale at all.
  # It is already a hard dependency of selfcheck (validate_plugins/marketplace, memory_link_check),
  # so this adds nothing to the requirement set. The regex is the same one, transcribed.
  _refs=$(python3 - <<'REFPY' 2>/dev/null
import re, glob
pat = re.compile(r'^(knowledge|templates|scripts|docs|plugins|\.claude)/[^*{}<>$]+\.(md|sh|ya?ml|jsonc|json)$')
seen = set()
for f in ['CLAUDE.md'] + sorted(glob.glob('.claude/rules/*.md')):
    try:
        text = open(f, encoding='utf-8', errors='replace').read()
    except OSError:
        continue
    for tok in re.findall(r'`([^` ]+)`', text):
        if pat.match(tok):
            seen.add(tok)
for p in sorted(seen):
    print(p)
REFPY
)
  if [ -z "$_refs" ]; then
    echo "FAIL  ref-path: extractor produced 0 refs — the scan broke, it did not pass"
    fail=1
  else
    while IFS= read -r p; do
      [ -z "$p" ] && continue
      if git check-ignore -q "$p" 2>/dev/null; then
        echo "SKIP  ref-path (gitignored): $p"
      elif [ -f "$p" ]; then
        echo "PASS  ref-path: $p"
      elif _pkg_accepted_absent "$p"; then
        echo "SKIP  ref-path (declared ACCEPTED_ABSENT — see package_coverage_check.sh): $p"
      else
        echo "FAIL  ref-path: $p — referenced in CLAUDE.md/.claude/rules but missing"
        fail=1
      fi
    done <<REFS
$_refs
REFS
  fi
else
  echo "SKIP  ref-path (package mode: no .git at package root — source-tree-only check)"
fi

if [ "$fail" -ne 0 ]; then
  echo "SELFCHECK: FAIL"
  exit 1
fi
echo "SELFCHECK: PASS"
