@chrono-meta/fh-gate 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/.claude/regression/probes_live.yaml +137 -0
  2. package/.claude/rules/.residency-patterns.defaults +7 -0
  3. package/.claude/rules/fh_4axis_gate.md +50 -1
  4. package/.claude-plugin/marketplace.json +8 -2
  5. package/AGENTS.md +27 -0
  6. package/CATALOG.md +17 -0
  7. package/CLAUDE.md +12 -2
  8. package/README.ja.md +51 -7
  9. package/README.ko.md +48 -7
  10. package/README.md +37 -5
  11. package/README.zh.md +45 -8
  12. package/docs/STANDARDS_ALIGNMENT.md +61 -0
  13. package/docs/USER_GUIDE.md +3 -0
  14. package/docs/USE_CASES.md +50 -0
  15. package/docs/model_tier_expectations.md +60 -0
  16. package/knowledge/shared/harness-core/fh_three_layer_canon.md +30 -10
  17. package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +8 -0
  18. package/knowledge/shared/harness-core/harness_incubator_doctrine.md +9 -0
  19. package/knowledge/shared/harness-core/iso_ai_standards_crosswalk.md +139 -0
  20. package/knowledge/shared/harness-core/measurement-integrity-checklist.md +20 -0
  21. package/knowledge/shared/learnings/subagent_invocations_log.yaml +441 -4
  22. package/package.json +40 -2
  23. package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
  24. package/plugins/fh-commons/skills/preprep/README.md +4 -1
  25. package/plugins/fh-commons/skills/preprep/SKILL.md +99 -3
  26. package/plugins/fh-commons/skills/preprep/diagram_from_json.py +154 -0
  27. package/plugins/fh-commons/skills/preprep/fixtures/fixture_R3_negative.pptx +0 -0
  28. package/plugins/fh-commons/skills/preprep/fixtures/fixture_R3_positive.pptx +0 -0
  29. package/plugins/fh-commons/skills/preprep/fixtures/mk_slide_fixtures.py +179 -0
  30. package/plugins/fh-commons/skills/preprep/interslide_deps.py +98 -9
  31. package/plugins/fh-commons/skills/preprep/lane_adjacent_dup.py +4 -1
  32. package/plugins/fh-commons/skills/preprep/lane_diagram.py +95 -0
  33. package/plugins/fh-commons/skills/preprep/lane_geometry.py +181 -0
  34. package/plugins/fh-commons/skills/preprep/lane_promise.py +4 -1
  35. package/plugins/fh-commons/skills/preprep/lane_slide_refs.py +134 -0
  36. package/plugins/fh-commons/skills/preprep/lane_slide_relations.py +300 -0
  37. package/plugins/fh-commons/skills/preprep/preprep.py +129 -8
  38. package/plugins/fh-commons/skills/preprep/preprep_wire.py +210 -0
  39. package/plugins/fh-commons/skills/preprep/presentation_checklist.md +4 -0
  40. package/plugins/fh-commons/skills/preprep/surfaces.example.yaml +18 -0
  41. package/plugins/fh-commons/skills/preprep/test_preprep_lanes_rp.py +184 -0
  42. package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
  43. package/plugins/fh-meta/CHANGELOG.md +43 -1
  44. package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +87 -2
  45. package/plugins/fh-meta/skills/frontier-digest/SKILL.md +2 -2
  46. package/plugins/fh-meta/skills/frontier-digest/SKILL_detail.md +25 -5
  47. package/plugins/fh-meta/skills/hub-cc-pr-reviewer/SKILL.md +11 -0
  48. package/plugins/fh-meta/skills/hub-cc-pr-reviewer/SKILL_detail.md +3 -0
  49. package/plugins/fh-qp/.claude-plugin/plugin.json +22 -0
  50. package/plugins/fh-qp/README.md +71 -0
  51. package/plugins/fh-qp/fixtures/evidence_known_clean.txt +3 -0
  52. package/plugins/fh-qp/fixtures/evidence_known_dirty.txt +6 -0
  53. package/plugins/fh-qp/fixtures/reach_known_partial.tsv +4 -0
  54. package/plugins/fh-qp/fixtures/reach_known_reached.tsv +4 -0
  55. package/plugins/fh-qp/fixtures/reach_known_wall.tsv +3 -0
  56. package/plugins/fh-qp/fixtures/verdicts_known_bad_branch.tsv +2 -0
  57. package/plugins/fh-qp/fixtures/verdicts_known_bad_vacuous_machine.tsv +2 -0
  58. package/plugins/fh-qp/fixtures/verdicts_known_good.tsv +4 -0
  59. package/plugins/fh-qp/fixtures/verdicts_verify_only.tsv +3 -0
  60. package/plugins/fh-qp/qp_profile.example.yaml +29 -0
  61. package/plugins/fh-qp/scripts/qp_tools.sh +216 -0
  62. package/plugins/fh-qp/skills/qp/SKILL.md +65 -0
  63. package/plugins/fh-qp/skills/qp-plan/SKILL.md +42 -0
  64. package/plugins/fh-qp/skills/qp-regress/SKILL.md +45 -0
  65. package/plugins/fh-qp/skills/qp-run/SKILL.md +49 -0
  66. package/scripts/chamber_run.sh +14 -5
  67. package/scripts/com.forge-harness.live-eval.plist +84 -0
  68. package/scripts/compaction_probe.sh +9 -35
  69. package/scripts/directional_diff_gate.sh +14 -2
  70. package/scripts/frontier_digest_autopilot.sh +4 -1
  71. package/scripts/map_postprocess.py +90 -0
  72. package/scripts/outbound_query_guard.sh +131 -0
  73. package/scripts/outbound_query_hook.sh +373 -0
  74. package/scripts/package_coverage_check.sh +55 -16
  75. package/scripts/pipe_verdict_guard.sh +41 -1
  76. package/scripts/probe_live_eval.sh +240 -0
  77. package/scripts/probe_live_eval_lib.py +579 -0
  78. package/scripts/proposal_hook.sh +120 -17
  79. package/scripts/push_zone_check.sh +78 -0
  80. package/scripts/residency_closure_scan.py +252 -0
  81. package/scripts/selfcheck.sh +41 -1
  82. package/scripts/session_close_check.sh +100 -0
  83. package/scripts/sim_isolated_run.sh +98 -2
  84. package/scripts/test_action_yml_lanes.sh +97 -0
  85. package/scripts/test_fh_qp_lanes.sh +105 -0
  86. package/scripts/test_gate_two_verdicts_lanes.sh +139 -0
  87. package/scripts/test_map_postprocess_lanes.sh +143 -0
  88. package/scripts/test_marker_affected_lanes.sh +93 -0
  89. package/scripts/test_marker_crossfamily_lanes.sh +90 -6
  90. package/scripts/test_marker_oracle_lanes.sh +136 -0
  91. package/scripts/test_outbound_query_hook_lanes.sh +433 -0
  92. package/scripts/test_outbound_query_lanes.sh +87 -0
  93. package/scripts/test_pipe_verdict_guard_lanes.sh +26 -0
  94. package/scripts/test_preprep_diagram_lanes.sh +87 -0
  95. package/scripts/test_preprep_drift_anchor.sh +3 -3
  96. package/scripts/test_preprep_slide_refs_lanes.sh +169 -0
  97. package/scripts/test_probe_live_eval_lanes.sh +437 -0
  98. package/scripts/test_proposal_hook_lanes.sh +22 -1
  99. package/scripts/test_push_zone_lanes.sh +304 -0
  100. package/scripts/test_residency_closure_lanes.sh +70 -0
  101. package/scripts/test_sim_isolated_run_lanes.sh +119 -0
  102. package/scripts/test_utterance_intake_lanes.sh +414 -0
  103. package/scripts/test_worktree_reclaim_lanes.sh +70 -0
  104. package/scripts/transcript_utterances.py +222 -0
  105. package/scripts/utterance_intake.sh +424 -0
  106. package/scripts/validate_yaml.sh +27 -0
  107. package/scripts/worktree_reclaim.sh +95 -0
  108. package/templates/.git-hooks/pre-commit +353 -1
  109. package/templates/.git-hooks/pre-push +91 -0
  110. package/templates/RED_TEAM_REPORT.md +49 -0
  111. package/templates/settings.PreToolUse.snippet.json +65 -1
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env bash
2
+ # probe_live_eval.sh — the LIVE twin of /prompt-regression's static probe check.
3
+ #
4
+ # WHY THIS EXISTS. `plugins/fh-meta/skills/prompt-regression/SKILL.md` says this about itself,
5
+ # verbatim, in its own §Step 4: "What it therefore cannot catch — a rule that is still present but
6
+ # has stopped FIRING (salience loss, ordering, competition from another rule) — a trigger phrase
7
+ # present in the file but shadowed by a higher-priority route — any behavior change that leaves the
8
+ # source text identical." That gap is exactly what an eval-style harness closes (CLAUDE.md
9
+ # §Autonomous Initiative Layer cites the shape: settings-changed PR + a scored run against real
10
+ # tasks, not a source grep). This script IS that harness for FH's own golden probe set
11
+ # (.claude/regression/probes.md): it sends each selected probe's literal utterance to a floor-tier
12
+ # `claude -p` inside an ISOLATED, disposable clone (via scripts/sim_isolated_run.sh — never the live
13
+ # repo, per that script's own header) and greps the ACTUAL RESPONSE, not the source.
14
+ #
15
+ # WHAT IT IS NOT. It does not replace /prompt-regression (source-correctness stays cheap and
16
+ # instant) and it does not replace a blind sim-conductor persona run (which reads a whole artifact
17
+ # for open-ended judgment). This is narrow, mechanical, known-answer, and cheap enough to run
18
+ # nightly — the same trade CLAUDE.md's Measured-Loop memory entry describes for any recurring
19
+ # measurement: sealed pre-registration (probes_live.yaml is authored BEFORE a run, not fit to one),
20
+ # one variable per probe (ARM=primary utterance, CTRL=known-negative), a scorer that runs before
21
+ # results are read, and the falsification condition (a probe whose control does not discriminate
22
+ # is not scored PASS/FAIL — see §Scoring) executed exactly as it is written.
23
+ #
24
+ # SELECTION. Not every probes.md row is live-runnable — see .claude/regression/probes_live.yaml's
25
+ # header for the 4-step mechanical rule (class filter, utterance-shape filter, INERT-ANCHOR filter,
26
+ # hand-curated CLI-event filter) and scripts/probe_live_eval_lib.py for the implementation. Run
27
+ # `--dry-run` to see the full 33-row breakdown: which probes are selected, which are excluded and
28
+ # why, and which pass the mechanical rule but have no authored expect/control yet
29
+ # (NOT-YET-AUTHORED — an honest gap, not a silent drop; G-LINT-01 is the current example, deferred
30
+ # because scoring a full /harness-doctor Step 5 run needs more than a keyword regex).
31
+ #
32
+ # COST. Each selected probe costs TWO live `claude -p` calls (primary + control). Do not run the
33
+ # full selected set casually — use --subset N or --ids P1,P2 for a spot-check, and read
34
+ # sim_isolated_run.sh's own header before running unattended (isolation guarantees, what "observe"
35
+ # mode does and does not prevent, the three-valued rc/bytes verdict for a timeout vs an empty
36
+ # answer).
37
+ #
38
+ # 🟥 «미실행 ≠ 0» — a probe whose primary or control call produced 0 bytes (timeout, rate limit,
39
+ # crash) is scored FAILED-TO-RUN, excluded from the pass-rate denominator, and its count is
40
+ # reported separately. A FAILED-TO-RUN probe is not evidence the behavior is absent — it is
41
+ # evidence nothing was measured (CLAUDE.md §Instrument-Calibration: "not found ≠ 0").
42
+ #
43
+ # SCORING (see scripts/probe_live_eval_lib.py:score_probe for the exact rule). Each probe declares
44
+ # a `polarity` in probes_live.yaml:
45
+ # present — expect_re must appear in PRIMARY and must NOT appear in CONTROL
46
+ # absent — expect_re must NOT appear in PRIMARY and MUST appear in CONTROL
47
+ # Either direction of "the control disagrees with what polarity predicts" scores that probe
48
+ # UNCALIBRATED, never PASS or FAIL — an instrument that cannot discriminate has not measured
49
+ # anything, per this repo's own instrument-calibration discipline. If ANY probe in a run is
50
+ # UNCALIBRATED, the WHOLE RUN's overall verdict is UNCALIBRATED (rc=2) — a pass rate computed
51
+ # alongside a proven-blind probe is not trustworthy just because the other rows look fine.
52
+ #
53
+ # EXIT CODES: 0 = PASS (pass_rate >= threshold, no UNCALIBRATED). 1 = FAIL (pass_rate < threshold).
54
+ # 2 = UNCALIBRATED, NO-PROBES-RAN, or a RUNNER PREFLIGHT FAILURE (sim_isolated_run.sh's own usage
55
+ # guards exit 2 before ever calling `claude` — e.g. no `claude` on PATH, or — measured 2026-09-05 —
56
+ # a launchd PATH with no `timeout(1)` on it). This script fails fast on the FIRST such rc=2 rather
57
+ # than burning the remaining clones against an environment already known broken: no verdict
58
+ # rendered either way — fix the instrument before trusting it. See sim_isolated_run.sh's own
59
+ # §timeout(1) RESOLUTION header for why that preflight can fail even when `claude` itself is fine.
60
+ #
61
+ # USAGE
62
+ # bash scripts/probe_live_eval.sh --dry-run
63
+ # bash scripts/probe_live_eval.sh --ids G-GREET-01,G-TRIG-02 --model sonnet
64
+ # bash scripts/probe_live_eval.sh --subset 3
65
+ # bash scripts/probe_live_eval.sh --subset 3 --report-out /tmp/spot.md # spot-check: keep the nightly record untouched
66
+ # bash scripts/probe_live_eval.sh # full selected set — nightly cron shape
67
+ #
68
+ # PORTABILITY: bash 3.2 (macOS) + bash 5.x (Linux CI). No associative arrays, no `${var,,}`,
69
+ # heredocs only inside functions with quoted delimiters (see [[feedback_unquoted_heredoc_backtick_executes]]
70
+ # — this script has none; the one heredoc-shaped block lives in probe_live_eval_lib.py, a real
71
+ # file, not a bash heredoc).
72
+
73
+ set -uo pipefail
74
+
75
+ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
76
+ PROBES_MD="$REPO_ROOT/.claude/regression/probes.md"
77
+ PROBES_LIVE="$REPO_ROOT/.claude/regression/probes_live.yaml"
78
+ LIB="$REPO_ROOT/scripts/probe_live_eval_lib.py"
79
+ # FH_SIM_RUNNER_BIN — same override name test_sim_isolated_run_lanes.sh already uses for the same
80
+ # purpose (point at an alternate runner build). Here it also lets test_probe_live_eval_lanes.sh
81
+ # swap in a stub runner (rc=2, no `claude` call, no network) to test the fail-fast behavior below
82
+ # without spawning a live session. No-op when unset — default behavior is unchanged.
83
+ SIM_RUNNER="${FH_SIM_RUNNER_BIN:-$REPO_ROOT/scripts/sim_isolated_run.sh}"
84
+
85
+ # ── file-header constant — the "문턱" the design brief calls for. Change here, not per-invocation. ──
86
+ THRESHOLD="0.8"
87
+
88
+ MODEL="sonnet"
89
+ # ── reps — 프로브당 반복 횟수. 기본 1(종전 행동 그대로), 무인 런은 plist 에서 3 을 준다. ──
90
+ # 🟥 왜 1 이 기본이면서 야간은 3 인가 (2026-09-06 실측): 유효 런 3 개를 재채점하니 12 프로브 중
91
+ # 5 개가 flaky 였고, **같은 코퍼스·15분 간격**의 두 런에서 4 개가 뒤집혔다. 관측 pass_rate 는
92
+ # 0.50 / 0.67 / 0.67 — 단일 rep 의 노이즈 폭이 문턱 0.80 까지의 거리보다 넓다. 즉 reps=1 짜리
93
+ # pass_rate 로는 문턱을 정할 수 없다. 채점은 과반이고, 분산은 리포트의 `reps(pass/ran)` 칸에
94
+ # 그대로 남긴다(3/3 과 2/3 은 다른 사실이다).
95
+ # ⚠️ 비용: reps=3 이면 프로브당 `claude -p` 호출이 2 → 6 이다(12 프로브 = 24 → 72).
96
+ REPS=1
97
+ DRYRUN=0
98
+ SUBSET=""
99
+ IDS=""
100
+ OUTDIR=""
101
+ REPORT_OUT="" # --report-out: where the markdown report lands (default: tracks/_meta/live_eval_<date>.md)
102
+ while [ $# -gt 0 ]; do
103
+ case "$1" in
104
+ --subset) SUBSET="${2:-}"; shift 2 ;;
105
+ --ids) IDS="${2:-}"; shift 2 ;;
106
+ --model) MODEL="${2:-sonnet}"; shift 2 ;;
107
+ --reps) REPS="${2:-1}"; shift 2 ;;
108
+ --dry-run) DRYRUN=1; shift ;;
109
+ --out) OUTDIR="${2:-}"; shift 2 ;;
110
+ --report-out) REPORT_OUT="${2:-}"; shift 2 ;; # lanes/spot-checks MUST pass this — never the live path
111
+ *) echo "unknown flag: $1" >&2; exit 2 ;;
112
+ esac
113
+ done
114
+
115
+ case "$REPS" in ''|*[!0-9]*) echo "FAIL: --reps must be a positive integer (got '$REPS')" >&2; exit 2 ;; esac
116
+ [ "$REPS" -ge 1 ] || { echo "FAIL: --reps must be >= 1 (got '$REPS')" >&2; exit 2; }
117
+
118
+ command -v python3 >/dev/null 2>&1 || { echo "FAIL: python3 required" >&2; exit 2; }
119
+ [ -f "$PROBES_MD" ] || { echo "FAIL: $PROBES_MD not found" >&2; exit 2; }
120
+ [ -f "$PROBES_LIVE" ] || { echo "FAIL: $PROBES_LIVE not found" >&2; exit 2; }
121
+ [ -f "$LIB" ] || { echo "FAIL: $LIB not found" >&2; exit 2; }
122
+
123
+ WORKDIR="$(mktemp -d "${TMPDIR:-/tmp}/fh-live-eval-XXXXXX")"
124
+ SPEC_DIR="$WORKDIR/spec"
125
+ SELECT_JSON="$WORKDIR/select.json"
126
+
127
+ SELECT_ARGS=(select --probes-md "$PROBES_MD" --probes-live "$PROBES_LIVE"
128
+ --json-out "$SELECT_JSON" --spec-dir "$SPEC_DIR")
129
+ [ -n "$SUBSET" ] && SELECT_ARGS+=(--subset "$SUBSET")
130
+ [ -n "$IDS" ] && SELECT_ARGS+=(--ids "$IDS")
131
+
132
+ python3 "$LIB" "${SELECT_ARGS[@]}"
133
+ select_rc=$?
134
+ # select_rc nonzero = a DEAD-POINTER (probes_live.yaml names an id absent from probes.md). That is
135
+ # an authoring bug in this repo's own asset, not a runtime condition — fail loudly rather than
136
+ # silently running a smaller set than intended.
137
+ if [ "$select_rc" -ne 0 ]; then
138
+ echo "" >&2
139
+ echo "❌ selection reported a dead pointer — fix .claude/regression/probes_live.yaml before running." >&2
140
+ rm -rf "$WORKDIR"
141
+ exit "$select_rc"
142
+ fi
143
+
144
+ if [ "$DRYRUN" -eq 1 ]; then
145
+ rm -rf "$WORKDIR"
146
+ exit 0
147
+ fi
148
+
149
+ [ -f "$SPEC_DIR/selected_ids.txt" ] || { echo "FAIL: selection produced no spec dir" >&2; rm -rf "$WORKDIR"; exit 2; }
150
+ SELECTED_COUNT=$(wc -l < "$SPEC_DIR/selected_ids.txt" | tr -d ' ')
151
+ if [ "$SELECTED_COUNT" -eq 0 ]; then
152
+ echo "❌ 0 probes selected after filtering — nothing to run (check --ids / --subset against the" >&2
153
+ echo " SELECTED list printed above)." >&2
154
+ rm -rf "$WORKDIR"
155
+ exit 2
156
+ fi
157
+
158
+ # `claude` presence is the REAL runner's preflight, so it is checked only on that path. Under
159
+ # FH_SIM_RUNNER_BIN the stub owns its own preflight — measured 2026-09-05 on CI (ubuntu, no
160
+ # `claude` installed): this line fired BEFORE the stub was ever called, so FF2/FF3/FF4/FF6 went
161
+ # red while FF1 passed by coincidence (both paths exit 2). Lane FF7 pins the seam; FF7b pins
162
+ # that the real path still refuses to run without `claude`.
163
+ if [ -z "${FH_SIM_RUNNER_BIN:-}" ]; then
164
+ command -v claude >/dev/null 2>&1 || { echo "FAIL: claude CLI not on PATH — cannot run live" >&2; rm -rf "$WORKDIR"; exit 2; }
165
+ fi
166
+
167
+ RUN_DATE="$(date +%Y-%m-%d)"
168
+ OUTDIR="${OUTDIR:-$WORKDIR/run}"
169
+ mkdir -p "$OUTDIR"
170
+
171
+ echo ""
172
+ echo "── live run: $SELECTED_COUNT probe(s), model=$MODEL, out=$OUTDIR ──────────────────────"
173
+
174
+ while IFS= read -r id; do
175
+ [ -z "$id" ] && continue
176
+ input_text="$(cat "$SPEC_DIR/$id.input.txt")"
177
+ control_text="$(cat "$SPEC_DIR/$id.control.txt")"
178
+ probe_out="$OUTDIR/$id"
179
+ mkdir -p "$probe_out"
180
+
181
+ echo ""
182
+ echo "▶ $id — primary"
183
+ bash "$SIM_RUNNER" --arm primary --reps "$REPS" --prompt "$input_text" \
184
+ --mode observe --model "$MODEL" --out "$probe_out" \
185
+ > "$probe_out/_runner_primary.log" 2>&1
186
+ runner_rc=$?
187
+ tail -n 6 "$probe_out/_runner_primary.log"
188
+ # 🟥 fail-fast (2026-09-05) — rc=2 from the runner means its OWN usage/preflight guard tripped
189
+ # before `claude` was ever invoked (missing --arm/--prompt, bogus --mode, no `claude` on PATH,
190
+ # or — the incident this exists for — a launchd PATH with no `timeout(1)` resolvable). That
191
+ # condition is identical for every remaining probe in this run, so continuing would just burn
192
+ # the rest of the clones (up to 2*(N-1) more `claude -p` calls) to the same FAILED-TO-RUN wall.
193
+ # Abort loudly instead of quietly producing a 12/12 FAILED-TO-RUN report with no clue why.
194
+ if [ "$runner_rc" -eq 2 ]; then
195
+ echo "" >&2
196
+ echo "❌ $id primary runner call exited 2 (preflight failure, before \`claude\` ran)." >&2
197
+ echo " Aborting the whole run rather than burning the remaining clones." >&2
198
+ echo " Runner log: $probe_out/_runner_primary.log" >&2
199
+ echo " Partial run artifacts kept at: $OUTDIR" >&2
200
+ exit 2
201
+ fi
202
+
203
+ echo "▶ $id — control"
204
+ bash "$SIM_RUNNER" --arm control --reps "$REPS" --prompt "$control_text" \
205
+ --mode observe --model "$MODEL" --out "$probe_out" \
206
+ > "$probe_out/_runner_control.log" 2>&1
207
+ runner_rc=$?
208
+ tail -n 6 "$probe_out/_runner_control.log"
209
+ if [ "$runner_rc" -eq 2 ]; then
210
+ echo "" >&2
211
+ echo "❌ $id control runner call exited 2 (preflight failure, before \`claude\` ran)." >&2
212
+ echo " Aborting the whole run rather than burning the remaining clones." >&2
213
+ echo " Runner log: $probe_out/_runner_control.log" >&2
214
+ echo " Partial run artifacts kept at: $OUTDIR" >&2
215
+ exit 2
216
+ fi
217
+ done < "$SPEC_DIR/selected_ids.txt"
218
+
219
+ echo ""
220
+ # 🟥 The live report path is the nightly RECORD. A lane or spot-check that reaches this line with the
221
+ # default overwrote a real night's distribution once (2026-09-05 10:18: test_probe_live_eval_lanes.sh FF4
222
+ # ran the real script with a stub runner and replaced the 02:30 report). Lanes pass --report-out.
223
+ REPORT_PATH="${REPORT_OUT:-$REPO_ROOT/tracks/_meta/live_eval_${RUN_DATE}.md}"
224
+ python3 "$LIB" score \
225
+ --probes-live "$PROBES_LIVE" \
226
+ --select-json "$SELECT_JSON" \
227
+ --ids-file "$SPEC_DIR/selected_ids.txt" \
228
+ --run-root "$OUTDIR" \
229
+ --threshold "$THRESHOLD" \
230
+ --model "$MODEL" \
231
+ --report-out "$REPORT_PATH" \
232
+ --run-date "$RUN_DATE" \
233
+ --reps "$REPS"
234
+ score_rc=$?
235
+
236
+ echo ""
237
+ echo "run artifacts kept at: $OUTDIR"
238
+ echo "(temp selection workspace $WORKDIR is NOT auto-deleted when --out was passed explicitly; ok to remove by hand)"
239
+
240
+ exit "$score_rc"