@chrono-meta/fh-gate 2.15.1 → 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.
- package/.claude/regression/probes_live.yaml +137 -0
- package/.claude/rules/.residency-patterns.defaults +7 -0
- package/.claude/rules/fh_4axis_gate.md +88 -1
- package/.claude-plugin/marketplace.json +8 -2
- package/AGENTS.md +27 -0
- package/CATALOG.md +17 -0
- package/CLAUDE.md +13 -3
- package/README.ja.md +51 -7
- package/README.ko.md +48 -7
- package/README.md +43 -6
- package/README.zh.md +45 -8
- package/docs/STANDARDS_ALIGNMENT.md +61 -0
- package/docs/USER_GUIDE.md +3 -0
- package/docs/USE_CASES.md +50 -0
- package/docs/model_tier_expectations.md +60 -0
- package/knowledge/shared/harness-core/fh_three_layer_canon.md +77 -10
- package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +18 -0
- package/knowledge/shared/harness-core/harness_incubator_doctrine.md +9 -0
- package/knowledge/shared/harness-core/iso_ai_standards_crosswalk.md +139 -0
- package/knowledge/shared/harness-core/measurement-integrity-checklist.md +35 -1
- package/knowledge/shared/harness-core/ship_readiness_gate.md +19 -2
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +489 -3
- package/package.json +48 -2
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-commons/skills/preprep/README.md +4 -1
- package/plugins/fh-commons/skills/preprep/SKILL.md +99 -3
- package/plugins/fh-commons/skills/preprep/diagram_from_json.py +154 -0
- package/plugins/fh-commons/skills/preprep/fixtures/fixture_R3_negative.pptx +0 -0
- package/plugins/fh-commons/skills/preprep/fixtures/fixture_R3_positive.pptx +0 -0
- package/plugins/fh-commons/skills/preprep/fixtures/mk_slide_fixtures.py +179 -0
- package/plugins/fh-commons/skills/preprep/interslide_deps.py +98 -9
- package/plugins/fh-commons/skills/preprep/lane_adjacent_dup.py +4 -1
- package/plugins/fh-commons/skills/preprep/lane_diagram.py +95 -0
- package/plugins/fh-commons/skills/preprep/lane_geometry.py +181 -0
- package/plugins/fh-commons/skills/preprep/lane_promise.py +4 -1
- package/plugins/fh-commons/skills/preprep/lane_slide_refs.py +134 -0
- package/plugins/fh-commons/skills/preprep/lane_slide_relations.py +300 -0
- package/plugins/fh-commons/skills/preprep/preprep.py +129 -8
- package/plugins/fh-commons/skills/preprep/preprep_wire.py +210 -0
- package/plugins/fh-commons/skills/preprep/presentation_checklist.md +4 -0
- package/plugins/fh-commons/skills/preprep/surfaces.example.yaml +18 -0
- package/plugins/fh-commons/skills/preprep/test_preprep_lanes_rp.py +184 -0
- package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/CHANGELOG.md +67 -0
- package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +87 -2
- package/plugins/fh-meta/skills/frontier-digest/SKILL.md +2 -2
- package/plugins/fh-meta/skills/frontier-digest/SKILL_detail.md +25 -5
- package/plugins/fh-meta/skills/hub-cc-pr-reviewer/SKILL.md +11 -0
- package/plugins/fh-meta/skills/hub-cc-pr-reviewer/SKILL_detail.md +3 -0
- package/plugins/fh-qp/.claude-plugin/plugin.json +22 -0
- package/plugins/fh-qp/README.md +71 -0
- package/plugins/fh-qp/fixtures/evidence_known_clean.txt +3 -0
- package/plugins/fh-qp/fixtures/evidence_known_dirty.txt +6 -0
- package/plugins/fh-qp/fixtures/reach_known_partial.tsv +4 -0
- package/plugins/fh-qp/fixtures/reach_known_reached.tsv +4 -0
- package/plugins/fh-qp/fixtures/reach_known_wall.tsv +3 -0
- package/plugins/fh-qp/fixtures/verdicts_known_bad_branch.tsv +2 -0
- package/plugins/fh-qp/fixtures/verdicts_known_bad_vacuous_machine.tsv +2 -0
- package/plugins/fh-qp/fixtures/verdicts_known_good.tsv +4 -0
- package/plugins/fh-qp/fixtures/verdicts_verify_only.tsv +3 -0
- package/plugins/fh-qp/qp_profile.example.yaml +29 -0
- package/plugins/fh-qp/scripts/qp_tools.sh +216 -0
- package/plugins/fh-qp/skills/qp/SKILL.md +65 -0
- package/plugins/fh-qp/skills/qp-plan/SKILL.md +42 -0
- package/plugins/fh-qp/skills/qp-regress/SKILL.md +45 -0
- package/plugins/fh-qp/skills/qp-run/SKILL.md +49 -0
- package/scripts/backtick_guard.sh +194 -0
- package/scripts/chamber_run.sh +14 -5
- package/scripts/com.forge-harness.live-eval.plist +84 -0
- package/scripts/compaction_probe.sh +9 -35
- package/scripts/context_continuity_score.sh +49 -7
- package/scripts/directional_diff_gate.sh +14 -2
- package/scripts/fh-gate.sh +3 -3
- package/scripts/files_manifest_shipping_check.sh +19 -0
- package/scripts/frontier_digest_autopilot.sh +4 -1
- package/scripts/gate_pathspec_check.sh +1 -1
- package/scripts/map_postprocess.py +90 -0
- package/scripts/outbound_query_guard.sh +131 -0
- package/scripts/outbound_query_hook.sh +373 -0
- package/scripts/package_coverage_check.sh +79 -18
- package/scripts/pipe_verdict_guard.sh +41 -1
- package/scripts/probe_live_eval.sh +240 -0
- package/scripts/probe_live_eval_lib.py +579 -0
- package/scripts/proposal_hook.sh +192 -0
- package/scripts/public_surface_scan_files.sh +11 -2
- package/scripts/push_zone_check.sh +78 -0
- package/scripts/residency_closure_scan.py +252 -0
- package/scripts/revert_probe.sh +250 -0
- package/scripts/selfcheck.sh +106 -3
- package/scripts/session_close_check.sh +100 -0
- package/scripts/sim_isolated_run.sh +195 -9
- package/scripts/test_action_yml_lanes.sh +97 -0
- package/scripts/test_backtick_guard_lanes.sh +115 -0
- package/scripts/test_degrade_scan_shell_probes.sh +7 -7
- package/scripts/test_fh_qp_lanes.sh +105 -0
- package/scripts/test_files_manifest_shipping_lanes.sh +5 -5
- package/scripts/test_gate_two_verdicts_lanes.sh +139 -0
- package/scripts/test_heavy_classifier_lanes.sh +1 -1
- package/scripts/test_lane_runner_lanes.sh +59 -33
- package/scripts/test_map_postprocess_lanes.sh +143 -0
- package/scripts/test_mapped_tracks_lanes.sh +1 -1
- package/scripts/test_marker_affected_lanes.sh +93 -0
- package/scripts/test_marker_crossfamily_lanes.sh +90 -6
- package/scripts/test_marker_oracle_lanes.sh +136 -0
- package/scripts/test_marker_soul_check_lanes.sh +24 -0
- package/scripts/test_node_check_lanes.sh +34 -34
- package/scripts/test_outbound_query_hook_lanes.sh +433 -0
- package/scripts/test_outbound_query_lanes.sh +87 -0
- package/scripts/test_package_coverage_lanes.sh +53 -27
- package/scripts/test_pipe_verdict_guard_lanes.sh +31 -5
- package/scripts/test_precommit_pointer_index_lanes.sh +33 -0
- package/scripts/test_preprep_diagram_lanes.sh +87 -0
- package/scripts/test_preprep_drift_anchor.sh +16 -7
- package/scripts/test_preprep_drift_anchor_lanes.sh +23 -0
- package/scripts/test_preprep_slide_refs_lanes.sh +169 -0
- package/scripts/test_probe_live_eval_lanes.sh +437 -0
- package/scripts/test_proposal_hook_lanes.sh +57 -0
- package/scripts/test_push_zone_lanes.sh +304 -0
- package/scripts/test_residency_closure_lanes.sh +70 -0
- package/scripts/test_revert_probe_lanes.sh +146 -0
- package/scripts/test_session_close_lanes.sh +3 -5
- package/scripts/test_sim_isolated_run_lanes.sh +136 -0
- package/scripts/test_utterance_intake_lanes.sh +414 -0
- package/scripts/test_worktree_reclaim_lanes.sh +70 -0
- package/scripts/transcript_utterances.py +222 -0
- package/scripts/utterance_intake.sh +424 -0
- package/scripts/utterance_landing_check.sh +2 -2
- package/scripts/validate_yaml.sh +27 -0
- package/scripts/worktree_reclaim.sh +95 -0
- package/templates/.git-hooks/pre-commit +380 -5
- package/templates/.git-hooks/pre-push +91 -0
- package/templates/RED_TEAM_REPORT.md +49 -0
- package/templates/settings.PreToolUse.snippet.json +101 -1
- package/plugins/fh-commons/README.md +0 -38
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# revert_probe.sh — general-purpose ⓕ revert-and-observe probe (rung 강화 #2, six_axis_review_2026-09-04).
|
|
3
|
+
#
|
|
4
|
+
# WHY THIS EXISTS. `되돌림`(revert-and-observe) has been done by hand 15+ times in this repo's own
|
|
5
|
+
# history (see 6축 실측: ⓕ none 비율 57.4%, «정확히 4레인 적색» 급 실물은 매번 손으로 짠 1회성
|
|
6
|
+
# 스크립트였다). Hand-rolled revert probes are the exact shape §Mechanize-at-repetition names —
|
|
7
|
+
# N≥3 recurrence on the SAME operation (swap a file to an older version, rerun a suite, read which
|
|
8
|
+
# lines changed color) is a mechanization trigger, not a one-off. This is that mechanization.
|
|
9
|
+
#
|
|
10
|
+
# 🟥 FRONTIER WARNING — read before citing this tool's PASS as "the suite has detection power".
|
|
11
|
+
# A single revert of a single file is ONE mutant. Mutation-testing research (arXiv 2607.22880,
|
|
12
|
+
# and the companion Meta engineering write-up cited alongside it in
|
|
13
|
+
# frontier_verification_map_2026-09-04.md §ⓕ) found that coverage/mutation SCORE loses its
|
|
14
|
+
# correlation with real fault-detection effectiveness once suite size is controlled for — a
|
|
15
|
+
# single kill is evidence the ANCHOR under test is load-bearing for THIS ONE reverted file,
|
|
16
|
+
# never a general claim that the suite "has good mutation coverage" or "catches regressions".
|
|
17
|
+
# Run this against every file you actually care about; do not average or extrapolate from one.
|
|
18
|
+
#
|
|
19
|
+
# WHAT IT DOES
|
|
20
|
+
# 1. Runs the lane suite AS-IS against the file's CURRENT (working-tree) content — the "수리 후"
|
|
21
|
+
# run.
|
|
22
|
+
# 2. Swaps ONLY the target file to its content at --baseline (default HEAD), backing up the
|
|
23
|
+
# current content first.
|
|
24
|
+
# 3. Reruns the same lane suite against that swapped-in baseline content — the "되돌린" run.
|
|
25
|
+
# 4. Restores the target file to its exact pre-probe content — ALWAYS, even if either suite run
|
|
26
|
+
# crashes, hangs past its timeout, or this script itself errors. Restore is attempted from an
|
|
27
|
+
# EXIT trap (safety net) in addition to the normal-path restore, so a `kill`-free abnormal
|
|
28
|
+
# exit still restores. Physical restore, never `git checkout <ref> -- <path>` — that stages
|
|
29
|
+
# the revert into the index ([[feedback_git_checkout_path_stages_the_revert]]); this tool
|
|
30
|
+
# writes bytes to the file only, with `git show <ref>:<path>` (never `git checkout`), and
|
|
31
|
+
# never touches the index.
|
|
32
|
+
# 5. Diffs the two runs' ✅/❌ label lines (this repo's universal `ok()`/`no()` convention —
|
|
33
|
+
# every test_*.sh / *_lanes.sh in scripts/ prints ` ✅ <label>` / ` ❌ <label>`) and reports
|
|
34
|
+
# EXACTLY which labels flipped from ✅ (current) to ❌ (baseline) — i.e. which lane actually
|
|
35
|
+
# went red when the fix was undone.
|
|
36
|
+
#
|
|
37
|
+
# WHAT IT ASSUMES (named, not hidden): the lane suite's ✅/❌ label text is STABLE across the two
|
|
38
|
+
# runs for a given lane (the suite script itself does not change between the two invocations —
|
|
39
|
+
# only the target file's content does). A suite whose pass/fail label text is dynamically built
|
|
40
|
+
# from data that changes with the target file (e.g. embeds a byte count in the label) will show
|
|
41
|
+
# as "label only in one run" rather than a flip — reported honestly as UNMATCHED, not silently
|
|
42
|
+
# dropped, and not counted toward the flip total.
|
|
43
|
+
#
|
|
44
|
+
# USAGE
|
|
45
|
+
# bash scripts/revert_probe.sh <target-file> <lane-suite-script> [--baseline <ref>] [--timeout <sec>]
|
|
46
|
+
# bash scripts/revert_probe.sh scripts/foo.sh scripts/test_foo_lanes.sh
|
|
47
|
+
# bash scripts/revert_probe.sh scripts/foo.sh scripts/test_foo_lanes.sh --baseline HEAD~1
|
|
48
|
+
#
|
|
49
|
+
# EXIT CODES (fail-closed, per CLAUDE.md §Irreversibility Surface-Class Degrade Invariant — this
|
|
50
|
+
# is a REVERSIBLE, read-then-restore surface, so the floor here is "never mis-score", not
|
|
51
|
+
# "never run"):
|
|
52
|
+
# 0 = exactly ≥1 lane flipped ✅→❌ when reverted (anchor is alive — it caught the mutant)
|
|
53
|
+
# 1 = 0 lanes flipped (anchor is DECORATIVE for this file — nothing depended on the fix)
|
|
54
|
+
# 2 = usage error (bad args, target/suite/ref not found)
|
|
55
|
+
# 10 = the probe itself is unreliable for this run — either suite run produced ZERO parseable
|
|
56
|
+
# ✅/❌ lines (harness error, not "0 lanes exist"), OR the restore step failed (the target
|
|
57
|
+
# file may still hold BASELINE content — treated as the more severe failure and reported
|
|
58
|
+
# loudly, never silently folded into a verdict)
|
|
59
|
+
#
|
|
60
|
+
# Usage in a lane test: see scripts/test_revert_probe_lanes.sh for the known-pair calibration
|
|
61
|
+
# (decorative anchor → 1, real anchor → 0, restore-guaranteed-on-suite-crash).
|
|
62
|
+
|
|
63
|
+
set -uo pipefail
|
|
64
|
+
|
|
65
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
66
|
+
|
|
67
|
+
TARGET=""; SUITE=""; BASELINE="HEAD"; TIMEOUT="120"
|
|
68
|
+
_POS=()
|
|
69
|
+
while [ $# -gt 0 ]; do
|
|
70
|
+
case "$1" in
|
|
71
|
+
--baseline) BASELINE="${2:-HEAD}"; shift 2 ;;
|
|
72
|
+
--timeout) TIMEOUT="${2:-120}"; shift 2 ;;
|
|
73
|
+
-*) echo "FAIL: unknown flag: $1" >&2; exit 2 ;;
|
|
74
|
+
*) _POS+=("$1"); shift ;;
|
|
75
|
+
esac
|
|
76
|
+
done
|
|
77
|
+
TARGET="${_POS[0]:-}"; SUITE="${_POS[1]:-}"
|
|
78
|
+
|
|
79
|
+
[ -n "$TARGET" ] || { echo "FAIL: usage: revert_probe.sh <target-file> <lane-suite-script> [--baseline <ref>]" >&2; exit 2; }
|
|
80
|
+
[ -n "$SUITE" ] || { echo "FAIL: usage: revert_probe.sh <target-file> <lane-suite-script> [--baseline <ref>]" >&2; exit 2; }
|
|
81
|
+
[ -f "$TARGET" ] || { echo "FAIL: target file not found: $TARGET" >&2; exit 2; }
|
|
82
|
+
[ -f "$SUITE" ] || { echo "FAIL: lane suite script not found: $SUITE" >&2; exit 2; }
|
|
83
|
+
|
|
84
|
+
# 🟥 physical path (`pwd -P`), not logical — macOS `/tmp` and `$TMPDIR` are symlinks into
|
|
85
|
+
# `/private/...`, and `git rev-parse --show-toplevel` always answers with the PHYSICAL path.
|
|
86
|
+
# A logical `pwd` here would make every fixture under a temp dir fail the prefix-strip below
|
|
87
|
+
# (same class of defect `sim_isolated_run.sh` already names for its own path isolation).
|
|
88
|
+
TARGET_DIR="$(cd "$(dirname "$TARGET")" && pwd -P)"
|
|
89
|
+
TARGET_ABS="$TARGET_DIR/$(basename "$TARGET")"
|
|
90
|
+
GIT_ROOT="$(cd "$TARGET_DIR" && git rev-parse --show-toplevel 2>/dev/null)"
|
|
91
|
+
[ -n "$GIT_ROOT" ] || { echo "FAIL: target file is not inside a git repository: $TARGET" >&2; exit 2; }
|
|
92
|
+
case "$TARGET_ABS" in
|
|
93
|
+
"$GIT_ROOT"/*) REL_PATH="${TARGET_ABS#"$GIT_ROOT"/}" ;;
|
|
94
|
+
*) echo "FAIL: could not compute a repo-relative path for $TARGET" >&2; exit 2 ;;
|
|
95
|
+
esac
|
|
96
|
+
|
|
97
|
+
if ! git -C "$GIT_ROOT" cat-file -e "${BASELINE}:${REL_PATH}" 2>/dev/null; then
|
|
98
|
+
echo "FAIL: $REL_PATH not found at baseline '$BASELINE' (bad ref, or the file did not exist there)" >&2
|
|
99
|
+
exit 2
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
echo "── revert_probe ────────────────────────────────────────────────────"
|
|
103
|
+
echo "target: $REL_PATH"
|
|
104
|
+
echo "suite: $SUITE"
|
|
105
|
+
echo "baseline: $BASELINE"
|
|
106
|
+
echo ""
|
|
107
|
+
echo "🟥 FRONTIER WARNING (arXiv 2607.22880): this run reverts EXACTLY ONE file — ONE mutant."
|
|
108
|
+
echo " A ✅ verdict here means the anchor caught THIS mutant, not that the suite has general"
|
|
109
|
+
echo " mutation-detection power. Coverage/mutation score decorrelates from real effectiveness"
|
|
110
|
+
echo " once suite size is controlled for — do not average or extrapolate from a single run."
|
|
111
|
+
echo "되돌린 파일(뮤턴트) 수: 1 — $REL_PATH"
|
|
112
|
+
echo ""
|
|
113
|
+
|
|
114
|
+
WORKDIR="$(mktemp -d "${TMPDIR:-/tmp}/revert_probe.XXXXXX")" || { echo "FAIL: mktemp -d failed" >&2; exit 10; }
|
|
115
|
+
BACKUP="$WORKDIR/backup"
|
|
116
|
+
RUN_A="$WORKDIR/run_current.txt"
|
|
117
|
+
RUN_B="$WORKDIR/run_baseline.txt"
|
|
118
|
+
|
|
119
|
+
cp -p "$TARGET_ABS" "$BACKUP" || { echo "FAIL: could not back up $TARGET_ABS — refusing to touch it" >&2; rm -rf "$WORKDIR"; exit 10; }
|
|
120
|
+
|
|
121
|
+
RESTORED=0
|
|
122
|
+
_restore() {
|
|
123
|
+
[ "$RESTORED" = 1 ] && return 0
|
|
124
|
+
if cp -p "$BACKUP" "$TARGET_ABS" 2>/dev/null; then
|
|
125
|
+
RESTORED=1
|
|
126
|
+
else
|
|
127
|
+
echo "🟥🟥🟥 RESTORE FAILED — $TARGET_ABS may still hold BASELINE ($BASELINE) content." >&2
|
|
128
|
+
echo " Backup of the pre-probe content is kept at: $BACKUP" >&2
|
|
129
|
+
echo " Restore it by hand: cp \"$BACKUP\" \"$TARGET_ABS\"" >&2
|
|
130
|
+
fi
|
|
131
|
+
}
|
|
132
|
+
# Safety net — fires on ANY exit path (normal, error, unbound-var under set -u), so a crash
|
|
133
|
+
# mid-probe still restores. The normal path below also calls _restore explicitly and checks its
|
|
134
|
+
# result directly, because a trap cannot hand its own success/failure back to the exit-code logic.
|
|
135
|
+
trap '_restore' EXIT
|
|
136
|
+
|
|
137
|
+
_run_suite() { # $1=output-file
|
|
138
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
139
|
+
timeout "$TIMEOUT" bash "$SUITE" > "$1" 2>&1
|
|
140
|
+
else
|
|
141
|
+
bash "$SUITE" > "$1" 2>&1
|
|
142
|
+
fi
|
|
143
|
+
return 0 # the suite's own exit code (pass/fail count) is not this function's concern
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
echo "── run 1/2: 현재(수리 후) 판 ──"
|
|
147
|
+
_run_suite "$RUN_A"
|
|
148
|
+
echo " captured $(wc -l < "$RUN_A" | tr -d ' ') lines"
|
|
149
|
+
|
|
150
|
+
if ! git -C "$GIT_ROOT" show "${BASELINE}:${REL_PATH}" > "$TARGET_ABS.new" 2>"$WORKDIR/show.err"; then
|
|
151
|
+
echo "FAIL: git show ${BASELINE}:${REL_PATH} failed:" >&2
|
|
152
|
+
cat "$WORKDIR/show.err" >&2
|
|
153
|
+
rm -f "$TARGET_ABS.new"
|
|
154
|
+
# trap restores (no-op here, file was never swapped) and exits
|
|
155
|
+
exit 10
|
|
156
|
+
fi
|
|
157
|
+
mv "$TARGET_ABS.new" "$TARGET_ABS"
|
|
158
|
+
|
|
159
|
+
echo "── run 2/2: 기준($BASELINE) 판 (파일만 되돌림, 인덱스는 안 건드림) ──"
|
|
160
|
+
_run_suite "$RUN_B"
|
|
161
|
+
echo " captured $(wc -l < "$RUN_B" | tr -d ' ') lines"
|
|
162
|
+
|
|
163
|
+
_restore
|
|
164
|
+
trap - EXIT # explicit restore already ran; the safety net has nothing left to do
|
|
165
|
+
if [ "$RESTORED" != 1 ]; then
|
|
166
|
+
echo "" >&2
|
|
167
|
+
echo "RESULT: RESTORE-FAILED — do not trust the file on disk, see backup path above" >&2
|
|
168
|
+
rm -rf "$WORKDIR"
|
|
169
|
+
exit 10
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
# ── ✅/❌ 라벨 추출 — 심볼\tSPACE-트림한 설명 ────────────────────────────────────────────
|
|
173
|
+
_extract_labels() { # $1=source-file → writes "P|F<TAB>desc"
|
|
174
|
+
grep -E '(✅|❌)' "$1" 2>/dev/null | while IFS= read -r line; do
|
|
175
|
+
case "$line" in
|
|
176
|
+
*✅*) sym=P; rest="${line#*✅}" ;;
|
|
177
|
+
*) sym=F; rest="${line#*❌}" ;;
|
|
178
|
+
esac
|
|
179
|
+
rest="$(printf '%s' "$rest" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
|
|
180
|
+
[ -n "$rest" ] && printf '%s\t%s\n' "$sym" "$rest"
|
|
181
|
+
done
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
LABELS_A="$WORKDIR/labels_a.tsv"; LABELS_B="$WORKDIR/labels_b.tsv"
|
|
185
|
+
_extract_labels "$RUN_A" > "$LABELS_A"
|
|
186
|
+
_extract_labels "$RUN_B" > "$LABELS_B"
|
|
187
|
+
NA=$(wc -l < "$LABELS_A" | tr -d ' ')
|
|
188
|
+
NB=$(wc -l < "$LABELS_B" | tr -d ' ')
|
|
189
|
+
|
|
190
|
+
if [ "$NA" -eq 0 ] || [ "$NB" -eq 0 ]; then
|
|
191
|
+
echo ""
|
|
192
|
+
echo "🟥 HARNESS ERROR — one of the two runs produced ZERO ✅/❌ lines (current=$NA, baseline=$NB)."
|
|
193
|
+
echo " That is not \"0 lanes exist\" — it means this probe cannot see the suite's verdicts at"
|
|
194
|
+
echo " all for that run (crash, missing labels, wrong suite path). Read the raw output:"
|
|
195
|
+
echo " current: $RUN_A"
|
|
196
|
+
echo " baseline: $RUN_B"
|
|
197
|
+
rm -rf "$WORKDIR"
|
|
198
|
+
exit 10
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
# DESC\tSYM, sorted by DESC — LC_ALL=C throughout so non-ASCII label text (this repo's labels are
|
|
202
|
+
# routinely Korean) never collapses under a locale-dependent string-equality/sort
|
|
203
|
+
# ([[feedback_locale_string_equality_breaks_nonascii]]).
|
|
204
|
+
DESC_A="$WORKDIR/desc_a.tsv"; DESC_B="$WORKDIR/desc_b.tsv"
|
|
205
|
+
awk -F'\t' '{print $2"\t"$1}' "$LABELS_A" | LC_ALL=C sort -t"$(printf '\t')" -k1,1 -k2,2 > "$DESC_A"
|
|
206
|
+
awk -F'\t' '{print $2"\t"$1}' "$LABELS_B" | LC_ALL=C sort -t"$(printf '\t')" -k1,1 -k2,2 > "$DESC_B"
|
|
207
|
+
|
|
208
|
+
JOINED="$WORKDIR/joined.tsv"
|
|
209
|
+
LC_ALL=C join -t "$(printf '\t')" -j 1 -o 1.1,1.2,2.2 "$DESC_A" "$DESC_B" > "$JOINED" 2>/dev/null || : > "$JOINED"
|
|
210
|
+
|
|
211
|
+
FLIPPED_RED="$WORKDIR/flipped_red.txt" # ✅(현재) → ❌(기준) — 앵커가 실제로 잡은 것
|
|
212
|
+
FLIPPED_GREEN="$WORKDIR/flipped_green.txt" # ❌(현재) → ✅(기준) — 이상 신호, 참고용
|
|
213
|
+
awk -F'\t' '$2=="P" && $3=="F" {print $1}' "$JOINED" > "$FLIPPED_RED"
|
|
214
|
+
awk -F'\t' '$2=="F" && $3=="P" {print $1}' "$JOINED" > "$FLIPPED_GREEN"
|
|
215
|
+
K=$(wc -l < "$FLIPPED_RED" | tr -d ' ')
|
|
216
|
+
J=$(wc -l < "$FLIPPED_GREEN" | tr -d ' ')
|
|
217
|
+
|
|
218
|
+
# 라벨 텍스트가 두 실행에서 안 겹치는 경우 — 조용히 버리지 않고 이름으로 남긴다(가정 위반 알림).
|
|
219
|
+
ONLY_A="$WORKDIR/only_a.txt"; ONLY_B="$WORKDIR/only_b.txt"
|
|
220
|
+
LC_ALL=C comm -23 <(cut -f1 "$DESC_A" | LC_ALL=C sort -u) <(cut -f1 "$DESC_B" | LC_ALL=C sort -u) > "$ONLY_A"
|
|
221
|
+
LC_ALL=C comm -13 <(cut -f1 "$DESC_A" | LC_ALL=C sort -u) <(cut -f1 "$DESC_B" | LC_ALL=C sort -u) > "$ONLY_B"
|
|
222
|
+
NUM_ONLY_A=$(wc -l < "$ONLY_A" | tr -d ' '); NUM_ONLY_B=$(wc -l < "$ONLY_B" | tr -d ' ')
|
|
223
|
+
|
|
224
|
+
echo ""
|
|
225
|
+
echo "── 결과 ──────────────────────────────────────────────────────────"
|
|
226
|
+
echo "현재(수리 후) 라벨: ${NA}줄 · 기준($BASELINE) 라벨: ${NB}줄"
|
|
227
|
+
echo ""
|
|
228
|
+
echo "되돌린 레인 (✅→❌, 앵커가 실제로 잡은 것): ${K}개"
|
|
229
|
+
if [ "$K" -gt 0 ]; then sed 's/^/ ❌ /' "$FLIPPED_RED"; fi
|
|
230
|
+
echo ""
|
|
231
|
+
echo "(참고) 반대방향 (❌→✅, 이상 신호): ${J}개"
|
|
232
|
+
if [ "$J" -gt 0 ]; then sed 's/^/ ⚠️ /' "$FLIPPED_GREEN"; fi
|
|
233
|
+
if [ "$NUM_ONLY_A" -gt 0 ] || [ "$NUM_ONLY_B" -gt 0 ]; then
|
|
234
|
+
echo ""
|
|
235
|
+
echo "🟥 라벨 텍스트가 두 실행에서 완전히 겹치지 않는다 — 이 도구의 가정(라벨 텍스트가 안정적)"
|
|
236
|
+
echo " 이 이 스위트에서는 안 맞을 수 있다. 아래는 매칭 대상에서 빠진 라벨(플립 집계에 미포함):"
|
|
237
|
+
[ "$NUM_ONLY_A" -gt 0 ] && sed 's/^/ 현재에만: /' "$ONLY_A"
|
|
238
|
+
[ "$NUM_ONLY_B" -gt 0 ] && sed 's/^/ 기준에만: /' "$ONLY_B"
|
|
239
|
+
fi
|
|
240
|
+
|
|
241
|
+
echo ""
|
|
242
|
+
if [ "$K" -ge 1 ]; then
|
|
243
|
+
echo "판정: 앵커 살아있음 — 되돌리면 정확히 ${K}개 레인이 빨개진다"
|
|
244
|
+
rm -rf "$WORKDIR"
|
|
245
|
+
exit 0
|
|
246
|
+
else
|
|
247
|
+
echo "판정: 앵커 장식 — 되돌려도 빨개지는 레인이 0개다"
|
|
248
|
+
rm -rf "$WORKDIR"
|
|
249
|
+
exit 1
|
|
250
|
+
fi
|
package/scripts/selfcheck.sh
CHANGED
|
@@ -187,10 +187,22 @@ else
|
|
|
187
187
|
fail=1
|
|
188
188
|
fi
|
|
189
189
|
|
|
190
|
-
# Bash surface: npm-shipped scripts + local bin wrappers + gate-chain infra
|
|
190
|
+
# Bash surface: npm-shipped scripts + local bin wrappers + gate-chain infra.
|
|
191
|
+
# `bin/fh-gate` · `bin/fh-run` · `bin/fh-goal` are named EXPLICITLY here (not a glob) and, unlike
|
|
192
|
+
# most of this list, are not covered by files_manifest_shipping_check.sh either (only their
|
|
193
|
+
# `.js` counterparts are declared in package.json files[]) — so a plain `[ -f "$f" ] || continue`
|
|
194
|
+
# made their disappearance invisible to BOTH checks at once. Reproduced 2026-09-03: deleting
|
|
195
|
+
# bin/fh-gate from a fixture tree left fail=0, no FAIL line, nothing. `scripts/*.sh` staying a
|
|
196
|
+
# silent skip on a genuinely-empty glob is correct (that arm still has no non-glob name); the
|
|
197
|
+
# named gate-chain-infra paths must not degrade the same way.
|
|
191
198
|
for f in scripts/*.sh bin/fh-gate bin/fh-run bin/fh-goal \
|
|
192
199
|
templates/regression_guard.sh templates/temper_check.sh templates/predelete_check.sh templates/.git-hooks/pre-commit; do
|
|
193
|
-
[ -f "$f" ]
|
|
200
|
+
if [ ! -f "$f" ]; then
|
|
201
|
+
case "$f" in
|
|
202
|
+
*'*'*) continue ;; # unmatched glob (nullglob off) — not a real path, legitimate skip
|
|
203
|
+
*) echo "FAIL bash -n coverage: gate-chain infra file missing: $f"; fail=1; continue ;;
|
|
204
|
+
esac
|
|
205
|
+
fi
|
|
194
206
|
check "bash -n $f" bash -n "$f"
|
|
195
207
|
done
|
|
196
208
|
|
|
@@ -594,6 +606,9 @@ for _pair in \
|
|
|
594
606
|
"scripts/destructive_pre_gate.sh|scripts/test_destructive_pre_gate_lanes.sh" \
|
|
595
607
|
"templates/.git-hooks/pre-push|scripts/test_prepush_destructive_lanes.sh" \
|
|
596
608
|
"templates/.git-hooks/pre-push|scripts/test_prepush_destructive_liveness.sh" \
|
|
609
|
+
"templates/.git-hooks/pre-push|scripts/test_push_zone_lanes.sh" \
|
|
610
|
+
"scripts/push_zone_check.sh|scripts/test_push_zone_lanes.sh" \
|
|
611
|
+
"scripts/session_close_check.sh|scripts/test_push_zone_lanes.sh" \
|
|
597
612
|
"scripts/session_close_check.sh|scripts/test_peer_worktree_detect_lanes.sh" \
|
|
598
613
|
"scripts/env_purity_scan.sh|scripts/test_env_purity_lanes.sh" \
|
|
599
614
|
"scripts/frontier_digest_daily.sh|scripts/test_frontier_digest_retry.sh" \
|
|
@@ -606,19 +621,36 @@ for _pair in \
|
|
|
606
621
|
"templates/.git-hooks/pre-commit|scripts/test_marker_thirdparty_lanes.sh" \
|
|
607
622
|
"templates/.git-hooks/pre-commit|scripts/test_marker_soul_check_lanes.sh" \
|
|
608
623
|
"templates/.git-hooks/pre-commit|scripts/test_marker_soul_tenet_lanes.sh" \
|
|
624
|
+
"templates/.git-hooks/pre-commit|scripts/test_marker_affected_lanes.sh" \
|
|
625
|
+
"templates/.git-hooks/pre-commit|scripts/test_marker_oracle_lanes.sh" \
|
|
626
|
+
`# ── 판정을 둘로 (2026-09-06, SWE-Gate 답습): 합성 «PASS» 가 acceptance-evidence 를 가리지 않는가 ──` \
|
|
627
|
+
"templates/.git-hooks/pre-commit|scripts/test_gate_two_verdicts_lanes.sh" \
|
|
628
|
+
`# ── fh-qp (QP) — chamber run #18 EMIT 2026-09-05: qp_tools.sh known-pair + residency lanes ──` \
|
|
629
|
+
"plugins/fh-qp/scripts/qp_tools.sh|scripts/test_fh_qp_lanes.sh" \
|
|
630
|
+
"plugins/fh-commons/skills/preprep/diagram_from_json.py|scripts/test_preprep_diagram_lanes.sh" \
|
|
631
|
+
`# ── action.yml — the GitHub Action wrapper: its exit-code mapping is where a typed verdict could become a boolean ──` \
|
|
632
|
+
"action.yml|scripts/test_action_yml_lanes.sh" \
|
|
609
633
|
"scripts/sim_isolated_run.sh|scripts/test_sim_path_isolation_lanes.sh" \
|
|
634
|
+
`# ── live-eval — the live twin of the static /prompt-regression probe check (2026-09-04) ──` \
|
|
635
|
+
"scripts/probe_live_eval.sh|scripts/test_probe_live_eval_lanes.sh" \
|
|
636
|
+
"scripts/probe_live_eval_lib.py|scripts/test_probe_live_eval_lanes.sh" \
|
|
610
637
|
"templates/.git-hooks/pre-commit|scripts/test_marker_first_use_lanes.sh" \
|
|
611
638
|
"scripts/fixture_guard_lib.sh|scripts/test_fixture_guard_lanes.sh" \
|
|
612
639
|
"scripts/stray_path_scan.sh|scripts/test_stray_path_lanes.sh" \
|
|
613
640
|
"scripts/session_close_check.sh|scripts/test_stray_path_lanes.sh" \
|
|
614
641
|
"templates/.git-hooks/pre-commit|scripts/test_hook_leg_wiring_lanes.sh" \
|
|
615
642
|
".claude/soul_tenets.txt|scripts/test_marker_soul_tenet_lanes.sh" \
|
|
643
|
+
"docs/map/fh_assets.architecture.json|scripts/test_fh_map_paths_lanes.sh" \
|
|
644
|
+
`# ── 지도 후처리(2026-09-06): 발행 폭 하한 + SVG 재생성. 리터럴 드리프트를 fail-closed 로 잡는다 ──` \
|
|
645
|
+
"scripts/map_postprocess.py|scripts/test_map_postprocess_lanes.sh" \
|
|
616
646
|
"templates/.git-hooks/pre-commit|scripts/test_precommit_staged_drift_lanes.sh" \
|
|
617
647
|
"templates/.git-hooks/pre-commit|scripts/test_marker_address_lanes.sh" \
|
|
648
|
+
"templates/.git-hooks/pre-commit|scripts/test_precommit_pointer_index_lanes.sh" \
|
|
618
649
|
"scripts/residency_closure_scan.py|scripts/test_residency_closure_lanes.sh" \
|
|
619
650
|
"scripts/reviewer_capability_corpus.tsv|scripts/test_reviewer_capability_conformance.sh" \
|
|
620
651
|
"scripts/field_canon_preload.sh|scripts/test_field_canon_lanes.sh" \
|
|
621
652
|
"scripts/stale_clone_guard.sh|scripts/test_stale_clone_guard_lanes.sh" \
|
|
653
|
+
"scripts/proposal_hook.sh|scripts/test_proposal_hook_lanes.sh" \
|
|
622
654
|
"plugins/fh-commons/skills/ko-tech-writer/SKILL.md|scripts/test_ko_tech_writer_lanes.sh" \
|
|
623
655
|
"scripts/script_caller_ratchet.sh|scripts/test_script_caller_ratchet_lanes.sh" \
|
|
624
656
|
"scripts/script_caller_ratchet.sh|scripts/test_runner_surface_index_lanes.sh" \
|
|
@@ -630,14 +662,23 @@ for _pair in \
|
|
|
630
662
|
"plugins/fh-commons/skills/preprep/lane_progression.py|scripts/test_preprep_progression_lanes.sh" \
|
|
631
663
|
"plugins/fh-commons/skills/preprep/lane_adjacent_dup.py|scripts/test_preprep_adjacent_dup_lanes.sh" \
|
|
632
664
|
"plugins/fh-commons/skills/preprep/lane_promise.py|scripts/test_preprep_promise_lanes.sh" \
|
|
665
|
+
"plugins/fh-commons/skills/preprep/lane_slide_refs.py|scripts/test_preprep_slide_refs_lanes.sh" \
|
|
633
666
|
"plugins/fh-commons/skills/preprep/SKILL.md|scripts/test_preprep_drift_anchor.sh" \
|
|
667
|
+
"scripts/test_preprep_drift_anchor.sh|scripts/test_preprep_drift_anchor_lanes.sh" \
|
|
634
668
|
"scripts/field_canon_preload.sh|scripts/test_skill_canon_preload_lanes.sh" \
|
|
635
669
|
`# ── round/ 회차 계기 4종(2026-09-01). 넷 다 한 스위트가 잡는다 — 주체별로 행을 둔다 ──` \
|
|
636
670
|
"scripts/round/delta_guard.sh|scripts/test_round_instruments_lanes.sh" \
|
|
637
671
|
"scripts/round/target_pin.sh|scripts/test_round_instruments_lanes.sh" \
|
|
638
672
|
"scripts/round/instrument_manifest.sh|scripts/test_round_instruments_lanes.sh" \
|
|
639
673
|
"scripts/round/eligcheck_qset.sh|scripts/test_round_instruments_lanes.sh" \
|
|
640
|
-
"scripts/round/gatecheck_qset.sh|scripts/test_round_instruments_lanes.sh"
|
|
674
|
+
"scripts/round/gatecheck_qset.sh|scripts/test_round_instruments_lanes.sh" \
|
|
675
|
+
`# ── 발화 착지(2026-09-05). 네 주체가 한 스위트로 묶인다 — 주체별로 행을 둔다 ──` \
|
|
676
|
+
"scripts/utterance_intake.sh|scripts/test_utterance_intake_lanes.sh" \
|
|
677
|
+
"scripts/transcript_utterances.py|scripts/test_utterance_intake_lanes.sh" \
|
|
678
|
+
"scripts/compaction_probe.sh|scripts/test_utterance_intake_lanes.sh" \
|
|
679
|
+
"scripts/session_close_check.sh|scripts/test_utterance_intake_lanes.sh" \
|
|
680
|
+
`# ── 워크트리 회수(2026-09-05, N=2 재발 → 스크립트). 사람이 부르는 도구지만 레인은 자동 ──` \
|
|
681
|
+
"scripts/worktree_reclaim.sh|scripts/test_worktree_reclaim_lanes.sh"
|
|
641
682
|
do
|
|
642
683
|
_subj="${_pair%%|*}"; _anc="${_pair##*|}"; _lbl="${_anc##*/}"
|
|
643
684
|
if [ ! -f "$_subj" ]; then
|
|
@@ -1056,6 +1097,29 @@ else
|
|
|
1056
1097
|
esac
|
|
1057
1098
|
fi
|
|
1058
1099
|
|
|
1100
|
+
# prepublish_scope_note — an embedded --self-test subject lane_runner_check.sh flagged as having
|
|
1101
|
+
# no dispatcher anywhere (2026-09-03): its own 7-lane known-pair (does validate.yml still call
|
|
1102
|
+
# selfcheck.sh — known-positive/negative, missing-workflow, commented-out call, real call beside a
|
|
1103
|
+
# stale commented one, the real `run: |` block-scalar shape, and echo-mention-is-not-a-call) lives
|
|
1104
|
+
# behind `--self-test`, and nothing runs it. It IS invoked at publish time (package.json
|
|
1105
|
+
# `prepublishOnly`) — but that is `check()`, the gate's default argument-less mode, running for
|
|
1106
|
+
# real; it never exercises the gate's OWN calibration. Not in the `for _subj in ...` loop above:
|
|
1107
|
+
# its terminal line is `── N pass / M fail`, never 캘리브레이션, same reason capability_registry_check
|
|
1108
|
+
# and capability_effect_probe were pulled out of that loop. Direct dispatch instead, same shape as
|
|
1109
|
+
# capability_effect_probe.sh above — whole-line terminal verdict with a non-zero PASS count, so an
|
|
1110
|
+
# emptied suite cannot certify itself. Ships via package.json files[], so absence is FAIL, not SKIP.
|
|
1111
|
+
if [ ! -f scripts/prepublish_scope_note.sh ]; then
|
|
1112
|
+
echo "FAIL prepublish_scope_note.sh: missing — it ships via package.json files[], so absence is deletion, not package mode"
|
|
1113
|
+
fail=1
|
|
1114
|
+
elif _out=$(bash scripts/prepublish_scope_note.sh --self-test < /dev/null 2>&1) \
|
|
1115
|
+
&& printf '%s\n' "$_out" | grep -qE '^ ── [1-9][0-9]* pass / 0 fail$'; then
|
|
1116
|
+
echo "PASS prepublish_scope_note.sh --self-test ($(printf '%s\n' "$_out" | grep -oE '[0-9]+ pass / [0-9]+ fail' | tail -1))"
|
|
1117
|
+
else
|
|
1118
|
+
echo "FAIL prepublish_scope_note.sh: --self-test failed or produced no terminal verdict line"
|
|
1119
|
+
_show_failure "$_out"
|
|
1120
|
+
fail=1
|
|
1121
|
+
fi
|
|
1122
|
+
|
|
1059
1123
|
# memory-link-check — the memory store is a GRAPH (memory_intent_recall.md: nodes=files,
|
|
1060
1124
|
# edges=[[links]], recall walks one hop). Measured 2026-07-28: 50 of 872 edges pointed at a note
|
|
1061
1125
|
# that existed under a different separator and 22 at nothing — a dead edge returns nothing and is
|
|
@@ -1143,6 +1207,20 @@ else
|
|
|
1143
1207
|
fail=1
|
|
1144
1208
|
fi
|
|
1145
1209
|
|
|
1210
|
+
# ⓕ 되돌림 범용 프로브 (six_axis_review_2026-09-04 강화 #2) — 15+ 손짜기 되돌림 스크립트를
|
|
1211
|
+
# 대체하는 계기다. 자기 자신을 known-pair 로 검증한다(장식 앵커→1, 실물 앵커→0, 복원 보장) —
|
|
1212
|
+
# 앵커가 아니라 그 앵커를 검증하는 계기이므로 반드시 실행돼야 한다.
|
|
1213
|
+
if [ ! -f scripts/revert_probe.sh ]; then
|
|
1214
|
+
_absent_subject_verdict "test_revert_probe_lanes.sh" "scripts/revert_probe.sh" || fail=1
|
|
1215
|
+
elif [ -f scripts/test_revert_probe_lanes.sh ]; then
|
|
1216
|
+
if ! bash scripts/test_revert_probe_lanes.sh; then
|
|
1217
|
+
fail=1
|
|
1218
|
+
fi
|
|
1219
|
+
else
|
|
1220
|
+
echo "FAIL test_revert_probe_lanes.sh: revert_probe.sh present but its anchor is missing"
|
|
1221
|
+
fail=1
|
|
1222
|
+
fi
|
|
1223
|
+
|
|
1146
1224
|
# 무효 워터마크 — 무효 회차의 «숫자 줄»이 자기 무효를 나르는가.
|
|
1147
1225
|
# 🟥 회차 3 은 자기 게이트가 VOID 를 찍고도 그 숫자만 기록으로 넘어갔다(VOID 낱말은 0회).
|
|
1148
1226
|
# 판정이 표 «밖»에 있었고 사람은 표를 복사하기 때문이다. 그 채널을 닫은 배선의 앵커다.
|
|
@@ -1533,6 +1611,20 @@ else
|
|
|
1533
1611
|
fail=1
|
|
1534
1612
|
fi
|
|
1535
1613
|
|
|
1614
|
+
# outbound-query PreToolUse 훅 — 위 CLI 가드의 **배선**(WebSearch|WebFetch). 레인이 두 층 판정
|
|
1615
|
+
# (override→deny · defaults→advisory), 미측정 4가지→deny, N/A 무음, 그리고 🟥 «출력에 토큰 값이
|
|
1616
|
+
# 없다»(H10)를 각각 짝으로 잡는다. 마지막 것이 이 훅의 존재 이유를 지키는 레인이다.
|
|
1617
|
+
if [ ! -f scripts/outbound_query_hook.sh ]; then
|
|
1618
|
+
_absent_subject_verdict "test_outbound_query_hook_lanes.sh" "scripts/outbound_query_hook.sh" || fail=1
|
|
1619
|
+
elif [ -f scripts/test_outbound_query_hook_lanes.sh ]; then
|
|
1620
|
+
if ! bash scripts/test_outbound_query_hook_lanes.sh; then
|
|
1621
|
+
fail=1
|
|
1622
|
+
fi
|
|
1623
|
+
else
|
|
1624
|
+
echo "FAIL test_outbound_query_hook_lanes.sh: outbound_query_hook.sh present but its anchor is missing"
|
|
1625
|
+
fail=1
|
|
1626
|
+
fi
|
|
1627
|
+
|
|
1536
1628
|
if [ ! -f scripts/pipe_verdict_guard.sh ]; then
|
|
1537
1629
|
_absent_subject_verdict "test_pipe_verdict_guard_lanes.sh" "scripts/pipe_verdict_guard.sh" || fail=1
|
|
1538
1630
|
elif [ -f scripts/test_pipe_verdict_guard_lanes.sh ]; then
|
|
@@ -1544,6 +1636,17 @@ else
|
|
|
1544
1636
|
fail=1
|
|
1545
1637
|
fi
|
|
1546
1638
|
|
|
1639
|
+
if [ ! -f scripts/backtick_guard.sh ]; then
|
|
1640
|
+
_absent_subject_verdict "test_backtick_guard_lanes.sh" "scripts/backtick_guard.sh" || fail=1
|
|
1641
|
+
elif [ -f scripts/test_backtick_guard_lanes.sh ]; then
|
|
1642
|
+
if ! bash scripts/test_backtick_guard_lanes.sh; then
|
|
1643
|
+
fail=1
|
|
1644
|
+
fi
|
|
1645
|
+
else
|
|
1646
|
+
echo "FAIL test_backtick_guard_lanes.sh: backtick_guard.sh present but its anchor is missing"
|
|
1647
|
+
fail=1
|
|
1648
|
+
fi
|
|
1649
|
+
|
|
1547
1650
|
if [ ! -f scripts/halffix_propagation_scan.sh ]; then
|
|
1548
1651
|
_absent_subject_verdict "test_halffix_lanes.sh" "scripts/halffix_propagation_scan.sh" || fail=1
|
|
1549
1652
|
elif [ -f scripts/test_halffix_lanes.sh ]; then
|
|
@@ -269,6 +269,73 @@ fi
|
|
|
269
269
|
|
|
270
270
|
fi
|
|
271
271
|
|
|
272
|
+
# ── ①-f UTTERANCE LANDING (advisory) ─────────────────────────────────────────
|
|
273
|
+
# WHY. 마감 체크는 **형식**만 본다 — 카드가 로그보다 새로운가, 필수 아티팩트가 있는가.
|
|
274
|
+
# **운영자 발화가 기록에 착지했는지는 아무도 안 봤다.** `utterance_landing_check.sh` 가 그걸
|
|
275
|
+
# 재려고 2026-08-08 에 지어졌는데 probes.tsv 를 **손으로** 짜야 해서 호출부가 0 개였다
|
|
276
|
+
# (2026-09-05 실측: 이 파일에서 그 스크립트를 부르는 줄이 하나도 없었다). 그 사이의 빠진 칸이
|
|
277
|
+
# `utterance_intake.sh` 다 — 전사본에서 발화를 뽑아 프로브를 자동 생성한다.
|
|
278
|
+
# 근거: 같은 날 운영자의 교리·설계급 발화 6 건이 착지한 경로가 전부 **거버너의 손**이었다.
|
|
279
|
+
#
|
|
280
|
+
# 🟥 라벨이 `①-e` 가 아닌 이유: 그건 아래 stray-path 블록이 **이미 쓰고 있다.** 한 라벨을 두
|
|
281
|
+
# 검사가 나눠 쓰면 「어느 ①-e 가 울렸나」를 아무도 못 가른다 — 이 배선을 준비한 진단은
|
|
282
|
+
# «①-e 는 비어 있다» 고 적었고, 그건 거짓이었다(레인 L13 이 «배선 전인데 초록» 으로 잡았다).
|
|
283
|
+
#
|
|
284
|
+
# 🟥 **advisory 다 — 막지 않는다.** 프로브는 키 낱말이라 기록이 같은 뜻을 다른 낱말로 적으면
|
|
285
|
+
# 과차단이 난다(안전한 방향이지만 소음이다). 노출 사다리(shadow) 로 몇 세션 관찰한 뒤
|
|
286
|
+
# close push 차단으로 승격할지는 **운영자 결정**이고, 그때까지 FAIL 을 세우지 않는다.
|
|
287
|
+
_UI_SUT="$FH/scripts/utterance_intake.sh"
|
|
288
|
+
if [ -f "$_UI_SUT" ]; then
|
|
289
|
+
# 세션 id 의 정본은 `branch_claim.sh` 다 — **새 판정기를 짓지 않는다**(재발명 금지).
|
|
290
|
+
# `show` 의 `세션ID` 줄이 그 값이고, 값이 `pid-` 로 시작하면 런타임이 id 를 안 준 것이라
|
|
291
|
+
# 전사본을 특정할 수 없다. 그때는 «다른 세션 것을 대신 읽는» 대신 UNMEASURED 로 남긴다 —
|
|
292
|
+
# 남의 전사본으로 낸 착지 판정은 틀린 답이지 근사값이 아니다.
|
|
293
|
+
_UI_SID=""
|
|
294
|
+
if [ -f "$FH/scripts/branch_claim.sh" ]; then
|
|
295
|
+
_UI_SID=$(LC_ALL=C bash "$FH/scripts/branch_claim.sh" show 2>/dev/null \
|
|
296
|
+
| LC_ALL=C sed -n 's/^세션ID[[:space:]][[:space:]]*//p' | head -1)
|
|
297
|
+
fi
|
|
298
|
+
case "$_UI_SID" in pid-*|'') _UI_SID="" ;; esac
|
|
299
|
+
# 전사본 slug = 프로젝트 절대경로에서 `/` `.` `_` 를 `-` 로 (실측 2026-09-05, ~/.claude/projects 대조)
|
|
300
|
+
_UI_SLUG=$(printf '%s' "$FH" | tr '/._' '---')
|
|
301
|
+
_UI_TRDIR="${FH_TRANSCRIPT_DIR:-$HOME/.claude/projects/$_UI_SLUG}"
|
|
302
|
+
_UI_TR="$_UI_TRDIR/$_UI_SID.jsonl"
|
|
303
|
+
if [ -z "$_UI_SID" ]; then
|
|
304
|
+
echo "ℹ️ ①-f UNMEASURED — 세션 id 미상(런타임이 CLAUDE_CODE_SESSION_ID 를 안 줬다)"
|
|
305
|
+
echo " 전사본을 특정할 수 없다. «발화가 다 적혔다» 가 아니라 «못 쟀다» 다."
|
|
306
|
+
elif [ ! -f "$_UI_TR" ]; then
|
|
307
|
+
echo "ℹ️ ①-f UNMEASURED — 전사본 부재: $_UI_TR"
|
|
308
|
+
echo " 부재를 0 으로 접지 마라 — 발화가 없었다는 뜻이 아니다."
|
|
309
|
+
else
|
|
310
|
+
# 기록 대상: 오늘자 산출 + 상시 카드/UAP. 배열로 모은다(zsh word-split 함정 회피).
|
|
311
|
+
_UI_RECS=()
|
|
312
|
+
for _p in "$FH/tracks/_meta/fh_completed_$TODAY.md" \
|
|
313
|
+
"$FH/tracks/_meta/user_adaptation_profile.md" \
|
|
314
|
+
"$FH/tracks/_meta/reference_next_session_starter.md"; do
|
|
315
|
+
[ -f "$_p" ] && _UI_RECS+=("$_p")
|
|
316
|
+
done
|
|
317
|
+
for _p in "$FH"/tracks/_meta/fh_signal_"$TODAY"_*.md "$FH"/tracks/*/*_"$TODAY".md; do
|
|
318
|
+
[ -f "$_p" ] && _UI_RECS+=("$_p")
|
|
319
|
+
done
|
|
320
|
+
if [ "${#_UI_RECS[@]}" -eq 0 ]; then
|
|
321
|
+
echo "⚠️ ①-f 오늘자 기록 파일이 하나도 없다 — 발화 착지를 잴 대상이 없다(UNMEASURED)"
|
|
322
|
+
echo " ④/⑤ 가 아직 안 돌았다는 뜻일 수 있다. 마감 순서를 확인해라."
|
|
323
|
+
else
|
|
324
|
+
_UI_OUT=$(bash "$_UI_SUT" "$_UI_TR" "${_UI_RECS[@]}" 2>&1); _UI_RC=$?
|
|
325
|
+
case "$_UI_RC" in
|
|
326
|
+
0) echo "✅ ①-f 발화 착지 — 프로브 전건 착지 (${#_UI_RECS[@]} 파일 대조)"
|
|
327
|
+
echo " ⚠️ 발화별 최장 토큰 ⌈n/2⌉ 일치 기준(키 2 개 이하는 OR) — «제대로 적혔다» 의 증명이 아니다." ;;
|
|
328
|
+
1) echo "⚠️ ①-f 미착지 발화가 있다 — advisory, 막지 않는다. 아래를 **확인**해라(기록 강제 아님):"
|
|
329
|
+
printf '%s\n' "$_UI_OUT" | sed 's/^/ /' ;;
|
|
330
|
+
*) echo "⚠️ ①-f 발화 착지 검사 UNMEASURED/계기이상 (rc=$_UI_RC) — 0 으로 읽지 마라"
|
|
331
|
+
printf '%s\n' "$_UI_OUT" | sed 's/^/ /' ;;
|
|
332
|
+
esac
|
|
333
|
+
fi
|
|
334
|
+
fi
|
|
335
|
+
else
|
|
336
|
+
echo "⚠️ ①-f utterance_intake.sh 없음 — skipped, not passed (UNMEASURED)"
|
|
337
|
+
fi
|
|
338
|
+
|
|
272
339
|
# ② FH assets changed today → harvest-loop owed
|
|
273
340
|
# NOTE: no `grep -q` here — under `set -o pipefail`, -q's early exit SIGPIPEs git log (141),
|
|
274
341
|
# masking a real match as pipeline failure so the warning NEVER fired on true positives
|
|
@@ -704,5 +771,38 @@ else
|
|
|
704
771
|
echo "⚠️ ①-e stray_path_scan.sh 없음 — skipped, not passed"
|
|
705
772
|
fi
|
|
706
773
|
|
|
774
|
+
# ── ①-f PUSH-ZONE advisory (surface-not-block; mirrors the pre-push hook's own zone check) ────
|
|
775
|
+
# WHY: templates/.git-hooks/pre-push blocks a single push whose remote sits outside the owner-
|
|
776
|
+
# account list, but it only ever sees the ONE push in front of it. The 2026-07-26 miss this whole
|
|
777
|
+
# axis exists for was a multi-repo ROUND — CLAUDE.local.md §REST API push 계정규칙 was applied
|
|
778
|
+
# correctly to one org repo and forgotten on another in the SAME round. `scripts/push_zone_check.sh`
|
|
779
|
+
# is the hand-run ENUMERATE tool for that round; wiring it here means it runs at least once per
|
|
780
|
+
# close without the operator separately remembering to ask for it — same shape as the ①-b open-PR
|
|
781
|
+
# sweep above. Advisory only: it lists, it never blocks (CLAUDE.md §Surface-Class Degrade Invariant,
|
|
782
|
+
# reversible half — a session-close check is not the irreversible boundary; that is the pre-push
|
|
783
|
+
# hook's job). Applicability is mechanical: both the script and an owner-account list must exist,
|
|
784
|
+
# or this reports UNCALIBRATED rather than guessing.
|
|
785
|
+
_PZ_SCRIPT="$FH/scripts/push_zone_check.sh"
|
|
786
|
+
_PZ_OWNERS="${PUSH_ZONE_OWNERS:-$FH/.claude/rules/.push-zone-owners}"
|
|
787
|
+
if [ -x "$_PZ_SCRIPT" ] && [ -f "$_PZ_OWNERS" ]; then
|
|
788
|
+
if _pz_out=$(bash "$_PZ_SCRIPT" 2>&1); then
|
|
789
|
+
# Counts only — the enumerator prints owner-account names and per-repo owners, and this close
|
|
790
|
+
# output lands in transcripts and session cards. The pre-push block never prints the list; this
|
|
791
|
+
# advisory must not either (codex #9, 2026-09-05). The names are one command away for the operator.
|
|
792
|
+
_pz_n=$(printf '%s\n' "$_pz_out" | grep -cE '^(✅|🔶) ' 2>/dev/null || true)
|
|
793
|
+
_pz_rest=$(printf '%s\n' "$_pz_out" | grep -cE '^🔶 ' 2>/dev/null || true)
|
|
794
|
+
echo "⚠️ ①-f push-zone: ${_pz_n:-0} repo(s) enumerated · ${_pz_rest:-0} outside the owner accounts (REST channel) — names withheld here; run: bash scripts/push_zone_check.sh"
|
|
795
|
+
else
|
|
796
|
+
# Verified 2026-09-05: push_zone_check.sh does not exit non-zero merely because `gh` is
|
|
797
|
+
# missing (its own `active="(unknown)"` fallback absorbs that) — the only documented non-zero
|
|
798
|
+
# exit is UNCALIBRATED for a missing owner list, already excluded by the `-f "$_PZ_OWNERS"`
|
|
799
|
+
# guard above. A non-zero exit reaching here is therefore an unanticipated failure; report it
|
|
800
|
+
# as UNMEASURED rather than silently treating empty/partial output as "nothing to enumerate".
|
|
801
|
+
echo "⚠️ ①-f push-zone: UNMEASURED — push_zone_check.sh exited non-zero, could not enumerate"
|
|
802
|
+
fi
|
|
803
|
+
else
|
|
804
|
+
echo "⏭️ ①-f push-zone UNCALIBRATED — script or owner-account list absent"
|
|
805
|
+
fi
|
|
806
|
+
|
|
707
807
|
echo "── close check: $([ "$FAIL" -eq 0 ] && echo CONSISTENT || echo VIOLATIONS) ──"
|
|
708
808
|
exit "$FAIL"
|