@chrono-meta/fh-gate 2.15.0 → 3.0.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/rules/fh_4axis_gate.md +38 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/CLAUDE.md +1 -1
- package/README.md +6 -1
- package/knowledge/shared/harness-core/fh_three_layer_canon.md +47 -0
- package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +10 -0
- package/knowledge/shared/harness-core/measurement-integrity-checklist.md +15 -1
- package/knowledge/shared/harness-core/ship_readiness_gate.md +20 -3
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +111 -0
- package/package.json +9 -1
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/CHANGELOG.md +49 -0
- package/plugins/fh-meta/skills/salience-splitter/SKILL.md +1 -1
- package/scripts/backtick_guard.sh +194 -0
- package/scripts/capability_effect_probe.sh +14 -1
- package/scripts/context_continuity_score.sh +106 -12
- package/scripts/fh-gate.sh +3 -3
- package/scripts/files_manifest_shipping_check.sh +19 -0
- package/scripts/gate_pathspec_check.sh +1 -1
- package/scripts/package_coverage_check.sh +25 -2
- package/scripts/proposal_hook.sh +89 -0
- package/scripts/public_surface_scan_files.sh +11 -2
- package/scripts/revert_probe.sh +250 -0
- package/scripts/selfcheck.sh +67 -3
- package/scripts/sim_isolated_run.sh +97 -7
- package/scripts/test_backtick_guard_lanes.sh +115 -0
- package/scripts/test_degrade_scan_shell_probes.sh +7 -7
- package/scripts/test_files_manifest_shipping_lanes.sh +5 -5
- package/scripts/test_heavy_classifier_lanes.sh +1 -1
- package/scripts/test_lane_runner_lanes.sh +59 -33
- package/scripts/test_mapped_tracks_lanes.sh +1 -1
- package/scripts/test_marker_soul_check_lanes.sh +24 -0
- package/scripts/test_node_check_lanes.sh +34 -34
- package/scripts/test_package_coverage_lanes.sh +53 -27
- package/scripts/test_pipe_verdict_guard_lanes.sh +5 -5
- package/scripts/test_precommit_pointer_index_lanes.sh +33 -0
- package/scripts/test_preprep_drift_anchor.sh +13 -4
- package/scripts/test_preprep_drift_anchor_lanes.sh +23 -0
- package/scripts/test_proposal_hook_lanes.sh +36 -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 +17 -0
- package/scripts/test_verdict_watermark_lanes.sh +27 -1
- package/scripts/utterance_landing_check.sh +2 -2
- package/templates/.git-hooks/pre-commit +27 -4
- package/templates/settings.PreToolUse.snippet.json +37 -1
- package/plugins/fh-commons/README.md +0 -38
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_revert_probe_lanes.sh — known-pair calibration for scripts/revert_probe.sh
|
|
3
|
+
#
|
|
4
|
+
# WHY THESE LANES. revert_probe.sh exists to replace 15+ hand-written revert-and-observe scripts
|
|
5
|
+
# with one generic tool. The two failure modes that would make it worse than the hand-rolled
|
|
6
|
+
# scripts it replaces are exactly the two known-pair lanes below:
|
|
7
|
+
# L1 DECORATIVE anchor — a suite whose lanes never actually depend on the target file's content
|
|
8
|
+
# must be reported as decorative (exit 1), not silently scored as "passed".
|
|
9
|
+
# L2 REAL anchor — the mirror-image known-positive: a suite that DOES catch the revert
|
|
10
|
+
# must report the exact flipped lane and exit 0. Without this half, L1 proves nothing
|
|
11
|
+
# ([[feedback_control_presence_is_not_discrimination]]).
|
|
12
|
+
# L3 RESTORE GUARANTEED — the target file must come back byte-identical after the probe, in
|
|
13
|
+
# BOTH the normal-completion path and the harness-error path (suite crashes / produces no
|
|
14
|
+
# parseable output). A revert tool that leaves the baseline content sitting in the working
|
|
15
|
+
# tree on the failure path is strictly worse than not having the tool.
|
|
16
|
+
# L4+ usage guards, harness-error detection, and the git-show (not git-checkout) restore path.
|
|
17
|
+
#
|
|
18
|
+
# Fixtures are throwaway git repos + throwaway suite scripts, never this repo's own assets — a
|
|
19
|
+
# lane suite that references a repo-specific path fails on a fresh clone (portability_lint P9).
|
|
20
|
+
#
|
|
21
|
+
# Usage: bash scripts/test_revert_probe_lanes.sh
|
|
22
|
+
|
|
23
|
+
set -uo pipefail
|
|
24
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
25
|
+
SUT="${FH_REVERT_PROBE_BIN:-$ROOT/scripts/revert_probe.sh}"
|
|
26
|
+
pass=0; fail=0
|
|
27
|
+
ok() { printf ' ✅ %s\n' "$1"; pass=$((pass+1)); }
|
|
28
|
+
no() { printf ' ❌ %s\n' "$1"; fail=$((fail+1)); }
|
|
29
|
+
|
|
30
|
+
if [ ! -f "$SUT" ]; then
|
|
31
|
+
echo "FAIL test_revert_probe_lanes.sh: subject absent ($SUT) — skipped is not passed"
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
WORKROOT="$(mktemp -d "${TMPDIR:-/tmp}/revertlane-XXXXXX")"
|
|
36
|
+
trap 'rm -rf "$WORKROOT"' EXIT
|
|
37
|
+
|
|
38
|
+
# ── throwaway repo: commit 1 = baseline (missing the fix) · commit 2/HEAD = current (has the fix)
|
|
39
|
+
SRC="$WORKROOT/src"; mkdir -p "$SRC"
|
|
40
|
+
(
|
|
41
|
+
cd "$SRC" && git init -q . && git config user.email l@l && git config user.name l
|
|
42
|
+
printf 'value = old_and_broken\n' > guard.txt
|
|
43
|
+
git add guard.txt && git commit -qm "baseline (no fix)"
|
|
44
|
+
printf 'value = MARKER_OK\n' > guard.txt
|
|
45
|
+
git add guard.txt && git commit -qm "current (has the fix)"
|
|
46
|
+
) >/dev/null 2>&1
|
|
47
|
+
TARGET="$SRC/guard.txt"
|
|
48
|
+
TARGET_SHA_BEFORE() { shasum -a 256 "$TARGET" 2>/dev/null | awk '{print $1}'; }
|
|
49
|
+
|
|
50
|
+
# ── fixture suites ──────────────────────────────────────────────────────────────────────────
|
|
51
|
+
# REAL anchor: the lane's pass/fail genuinely depends on the target file's content.
|
|
52
|
+
REAL_SUITE="$WORKROOT/real_suite.sh"
|
|
53
|
+
cat > "$REAL_SUITE" <<'EOF'
|
|
54
|
+
#!/usr/bin/env bash
|
|
55
|
+
if grep -q MARKER_OK "$FIXTURE_TARGET" 2>/dev/null; then
|
|
56
|
+
printf ' %s %s\n' "✅" "marker present in guard.txt"
|
|
57
|
+
else
|
|
58
|
+
printf ' %s %s\n' "❌" "marker present in guard.txt"
|
|
59
|
+
fi
|
|
60
|
+
exit 0
|
|
61
|
+
EOF
|
|
62
|
+
chmod +x "$REAL_SUITE"
|
|
63
|
+
|
|
64
|
+
# DECORATIVE anchor: always ✅, never actually reads the target file.
|
|
65
|
+
DECO_SUITE="$WORKROOT/deco_suite.sh"
|
|
66
|
+
cat > "$DECO_SUITE" <<'EOF'
|
|
67
|
+
#!/usr/bin/env bash
|
|
68
|
+
printf ' %s %s\n' "✅" "unrelated check that never reads guard.txt"
|
|
69
|
+
exit 0
|
|
70
|
+
EOF
|
|
71
|
+
chmod +x "$DECO_SUITE"
|
|
72
|
+
|
|
73
|
+
# HARNESS-ERROR suite: crashes before printing any ✅/❌ line at all.
|
|
74
|
+
CRASH_SUITE="$WORKROOT/crash_suite.sh"
|
|
75
|
+
cat > "$CRASH_SUITE" <<'EOF'
|
|
76
|
+
#!/usr/bin/env bash
|
|
77
|
+
echo "no verdict lines here, just noise" >&2
|
|
78
|
+
exit 1
|
|
79
|
+
EOF
|
|
80
|
+
chmod +x "$CRASH_SUITE"
|
|
81
|
+
|
|
82
|
+
echo "── revert_probe known-pair lanes ──────────────────────────────────"
|
|
83
|
+
|
|
84
|
+
# L1 — DECORATIVE anchor → exit 1, 0 flipped lanes
|
|
85
|
+
BEFORE1="$(TARGET_SHA_BEFORE)"
|
|
86
|
+
OUT1=$(FIXTURE_TARGET="$TARGET" bash "$SUT" "$TARGET" "$DECO_SUITE" --baseline HEAD~1 2>&1); RC1=$?
|
|
87
|
+
[ "$RC1" -eq 1 ] && ok "L1 decorative anchor → exit 1" || no "L1 decorative anchor: rc=$RC1 (want 1)"
|
|
88
|
+
printf '%s' "$OUT1" | grep -q "되돌린 레인 (✅→❌, 앵커가 실제로 잡은 것): 0개" \
|
|
89
|
+
&& ok "L1b report states 0 flipped lanes" || no "L1b report did not state 0 flipped lanes"
|
|
90
|
+
AFTER1="$(TARGET_SHA_BEFORE)"
|
|
91
|
+
[ "$BEFORE1" = "$AFTER1" ] && ok "L1c target file restored after decorative run" \
|
|
92
|
+
|| no "L1c target file NOT restored (before=$BEFORE1 after=$AFTER1)"
|
|
93
|
+
|
|
94
|
+
# L2 — REAL anchor → exit 0, exactly 1 flipped lane, names it
|
|
95
|
+
BEFORE2="$(TARGET_SHA_BEFORE)"
|
|
96
|
+
OUT2=$(FIXTURE_TARGET="$TARGET" bash "$SUT" "$TARGET" "$REAL_SUITE" --baseline HEAD~1 2>&1); RC2=$?
|
|
97
|
+
[ "$RC2" -eq 0 ] && ok "L2 real anchor → exit 0" || no "L2 real anchor: rc=$RC2 (want 0)"
|
|
98
|
+
printf '%s' "$OUT2" | grep -q "되돌린 레인 (✅→❌, 앵커가 실제로 잡은 것): 1개" \
|
|
99
|
+
&& ok "L2b report states exactly 1 flipped lane" || no "L2b report did not state 1 flipped lane"
|
|
100
|
+
printf '%s' "$OUT2" | grep -q "marker present in guard.txt" \
|
|
101
|
+
&& ok "L2c report names the exact flipped label" || no "L2c flipped label not named in report"
|
|
102
|
+
AFTER2="$(TARGET_SHA_BEFORE)"
|
|
103
|
+
[ "$BEFORE2" = "$AFTER2" ] && ok "L2d target file restored after real-anchor run" \
|
|
104
|
+
|| no "L2d target file NOT restored (before=$BEFORE2 after=$AFTER2)"
|
|
105
|
+
|
|
106
|
+
# L3 — frontier warning header always present (both suites above already exercised it; assert once)
|
|
107
|
+
printf '%s' "$OUT2" | grep -q "2607.22880" && ok "L3 frontier-warning citation present in output" \
|
|
108
|
+
|| no "L3 frontier-warning citation missing"
|
|
109
|
+
printf '%s' "$OUT2" | grep -q "되돌린 파일(뮤턴트) 수: 1" \
|
|
110
|
+
&& ok "L3b mutant-count line present (n=1 caveat)" || no "L3b mutant-count line missing"
|
|
111
|
+
|
|
112
|
+
# L4 — HARNESS ERROR: suite produces zero ✅/❌ lines → exit 10, and restore STILL happens.
|
|
113
|
+
# This is the lane that matters most: a crash on the baseline run is exactly the moment a naive
|
|
114
|
+
# implementation would leave the baseline content sitting in the working tree.
|
|
115
|
+
BEFORE4="$(TARGET_SHA_BEFORE)"
|
|
116
|
+
OUT4=$(FIXTURE_TARGET="$TARGET" bash "$SUT" "$TARGET" "$CRASH_SUITE" --baseline HEAD~1 2>&1); RC4=$?
|
|
117
|
+
[ "$RC4" -eq 10 ] && ok "L4 zero-label suite → exit 10 (harness error, not a silent pass)" \
|
|
118
|
+
|| no "L4 zero-label suite: rc=$RC4 (want 10)"
|
|
119
|
+
AFTER4="$(TARGET_SHA_BEFORE)"
|
|
120
|
+
[ "$BEFORE4" = "$AFTER4" ] && ok "L4b target file restored even on the harness-error path" \
|
|
121
|
+
|| no "L4b RESTORE FAILED ON HARNESS-ERROR PATH (before=$BEFORE4 after=$AFTER4) — this is the defect class this suite exists to catch"
|
|
122
|
+
|
|
123
|
+
# L5 — usage guards
|
|
124
|
+
OUT5=$(bash "$SUT" 2>&1); RC5=$?
|
|
125
|
+
[ "$RC5" -eq 2 ] && ok "L5 no args → exit 2" || no "L5 no args: rc=$RC5"
|
|
126
|
+
OUT5b=$(bash "$SUT" "$TARGET" 2>&1); RC5b=$?
|
|
127
|
+
[ "$RC5b" -eq 2 ] && ok "L5b missing suite arg → exit 2" || no "L5b missing suite arg: rc=$RC5b"
|
|
128
|
+
OUT5c=$(bash "$SUT" "$WORKROOT/does_not_exist.txt" "$REAL_SUITE" 2>&1); RC5c=$?
|
|
129
|
+
[ "$RC5c" -eq 2 ] && ok "L5c missing target file → exit 2" || no "L5c missing target file: rc=$RC5c"
|
|
130
|
+
OUT5d=$(bash "$SUT" "$TARGET" "$WORKROOT/does_not_exist.sh" 2>&1); RC5d=$?
|
|
131
|
+
[ "$RC5d" -eq 2 ] && ok "L5d missing suite file → exit 2" || no "L5d missing suite file: rc=$RC5d"
|
|
132
|
+
OUT5e=$(bash "$SUT" "$TARGET" "$REAL_SUITE" --baseline totally-bogus-ref 2>&1); RC5e=$?
|
|
133
|
+
[ "$RC5e" -eq 2 ] && ok "L5e bad --baseline ref → exit 2" || no "L5e bad baseline ref: rc=$RC5e"
|
|
134
|
+
|
|
135
|
+
# L6 — the restore path uses `git show`, never `git checkout <ref> -- <path>` (the latter STAGES
|
|
136
|
+
# the revert into the index — [[feedback_git_checkout_path_stages_the_revert]]). Assert the repo's
|
|
137
|
+
# index carries no staged change after a probe run, both directions.
|
|
138
|
+
( cd "$SRC" && git diff --cached --quiet ) \
|
|
139
|
+
&& ok "L6 no staged changes left in the index after any probe run" \
|
|
140
|
+
|| no "L6 the index has staged changes — a checkout-based restore would do this"
|
|
141
|
+
( cd "$SRC" && git status --porcelain ) | grep -q . \
|
|
142
|
+
&& no "L6b working tree not clean after probes (git status is non-empty)" \
|
|
143
|
+
|| ok "L6b working tree clean after all probes (file content == committed HEAD)"
|
|
144
|
+
|
|
145
|
+
echo "revert_probe lanes: $pass passed, $fail failed"
|
|
146
|
+
[ "$fail" -eq 0 ] || exit 1
|
|
@@ -138,9 +138,7 @@ PREMISE_FAILED=0
|
|
|
138
138
|
# Human-readable on both platforms: GNU prints a date for %y, BSD's %Fm prints a raw epoch, so the
|
|
139
139
|
# BSD branch asks for a formatted date instead — the whole point of this line is eyeballing.
|
|
140
140
|
_mtime_h() {
|
|
141
|
-
stat -c %y "$1" 2>/dev/null
|
|
142
|
-
|| stat -f "%Sm" -t "%Y-%m-%d %H:%M:%S" "$1" 2>/dev/null \
|
|
143
|
-
|| echo "?"
|
|
141
|
+
stat -c %y "$1" 2>/dev/null || stat -f "%Sm" -t "%Y-%m-%d %H:%M:%S" "$1" 2>/dev/null || echo "?"
|
|
144
142
|
}
|
|
145
143
|
|
|
146
144
|
_assert_newer() { # $1=expected-newer $2=reference $3=lane label — returns 1 if premise fails
|
|
@@ -298,7 +296,7 @@ _carry_fixture() { # $1=오늘카드 본문 $2=fh_completed 본문(빈 문자
|
|
|
298
296
|
printf '%s\n' "$card_body" > "$T/tracks/_meta/reference_next_session_starter.md"
|
|
299
297
|
[ -n "$done_body" ] && printf '%s\n' "$done_body" > "$T/tracks/_meta/fh_completed_${TODAY}.md"
|
|
300
298
|
# companion store: 어제 날짜로 커밋된 «이전 카드» 미러 하나
|
|
301
|
-
FUT=$(date -
|
|
299
|
+
FUT=$(date -d '+2 days' '+%m-%d' 2>/dev/null || date -v+2d '+%m-%d')
|
|
302
300
|
( cd "$S" && git init -q . && git config user.email a@l && git config user.name a \
|
|
303
301
|
&& mkdir -p tracks-meta \
|
|
304
302
|
&& printf '기한 %s 미팅 안건 [B]\n' "$FUT" > tracks-meta/reference_next_session_starter.md \
|
|
@@ -307,7 +305,7 @@ _carry_fixture() { # $1=오늘카드 본문 $2=fh_completed 본문(빈 문자
|
|
|
307
305
|
git commit -qm prior ) >/dev/null 2>&1
|
|
308
306
|
printf '%s|%s' "$T" "$S"
|
|
309
307
|
}
|
|
310
|
-
_carry_future() { date -
|
|
308
|
+
_carry_future() { date -d '+2 days' '+%m-%d' 2>/dev/null || date -v+2d '+%m-%d'; }
|
|
311
309
|
|
|
312
310
|
_carry_lane() { # $1=name $2=pattern $3=expect $4="REPO|STORE" $5=extra-env(옵션)
|
|
313
311
|
local name="$1" pat="$2" expect="$3" pair="$4" env5="${5:-}" T S out hit
|
|
@@ -261,5 +261,22 @@ if [ -x "$ROOT/scripts/round/arm_blind_probe.sh" ]; then
|
|
|
261
261
|
|| no "L24 눈가림 실패 — 팔이 정답지를 읽을 수 있다"
|
|
262
262
|
else no "L24 프로브 없음 — 검사 못 함(스킵 아님)"; fi
|
|
263
263
|
|
|
264
|
+
# ── L25 ⓒ 날짜 오염 통제 필드 (six_axis_review_2026-09-04 강화 #3) — 존재만 본다, 값은 안 본다.
|
|
265
|
+
# 이 러너가 판정을 안 낸다는 것이 헤더의 약속이라, 레인도 «필드가 있나»만 잰다.
|
|
266
|
+
OUTDIR="$WORKROOT/o25"
|
|
267
|
+
( cd "$SRC" && PATH="$STUBBIN:$PATH" HOME="$FAKEHOME" FH_STUB_MODE=say \
|
|
268
|
+
bash "$SUT" --arm a --reps 1 --prompt p --out "$OUTDIR" ) >/dev/null 2>&1
|
|
269
|
+
META="$OUTDIR/a_r1.meta.tsv"
|
|
270
|
+
if [ -f "$META" ]; then
|
|
271
|
+
grep -q '^corpus_head_date ' "$META" && ok "L25a corpus_head_date field recorded" \
|
|
272
|
+
|| no "L25a corpus_head_date field missing"
|
|
273
|
+
grep -q '^sim_model ' "$META" && ok "L25b sim_model field recorded" \
|
|
274
|
+
|| no "L25b sim_model field missing"
|
|
275
|
+
grep -q '^sim_model_cutoff ' "$META" && ok "L25c sim_model_cutoff field recorded" \
|
|
276
|
+
|| no "L25c sim_model_cutoff field missing"
|
|
277
|
+
else
|
|
278
|
+
no "L25 meta.tsv not written at all ($META)"
|
|
279
|
+
fi
|
|
280
|
+
|
|
264
281
|
echo "sim_isolated_run lanes: $pass passed, $fail failed"
|
|
265
282
|
[ "$fail" -eq 0 ] || exit 1
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
# ⇒ 쓰는 쪽에서 나르게 한다. 이 레인은 «한 줄만 복사해도 딸려오나»를 축자로 검정한다.
|
|
12
12
|
set -uo pipefail
|
|
13
13
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
14
|
-
S="$ROOT/scripts/context_continuity_score.sh"
|
|
14
|
+
S="${CCS_UNDER_TEST:-$ROOT/scripts/context_continuity_score.sh}" # 되돌림 프로브용 오버라이드(원본 사본을 가리켜 L29 가 빨개지는지 본다)
|
|
15
15
|
T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
|
|
16
16
|
pass=0; fail=0
|
|
17
17
|
ok(){ printf ' ✅ %s\n' "$1"; pass=$((pass+1)); }
|
|
@@ -423,5 +423,31 @@ if grep -q 'DELIVER" = 1 \] && \[ "$arm" = ARM' "$S"; then
|
|
|
423
423
|
else no "L28b 사본을 못 만들었다 — 되돌림 미검증(스킵 아님)"; fi
|
|
424
424
|
else no "L28 deliver 분기를 못 찾았다 — 검사 못 함(스킵 아님)"; fi
|
|
425
425
|
|
|
426
|
+
|
|
427
|
+
# ── L29 (2026-09-02, 전파 — eligcheck 와 같은 `_tsv_pipe` 프로세스 치환 구멍) ──
|
|
428
|
+
SEAL29="$T/seal_deadbeef-abc_20260901-101010.md"; : > "$SEAL29"
|
|
429
|
+
run29(){ ( cd "$ROOT" && bash "$S" --seal "$SEAL29" --qset "$1" --reps 1 --out "$2" --rescore 2>&1 </dev/null ); }
|
|
430
|
+
# ── L29 🟥 파서 실패/0 행이 «🟢 계기 생존»으로 접히지 않는다 ─────────────────────────
|
|
431
|
+
# 실측(2026-09-02, 원본, bash 5.3): 값에 `|` 하나 · 빈 qset → 두 루프가 0 행 → ROWS 비어 →
|
|
432
|
+
# 판정선 분모 전부 0 → 「🟢 계기 생존」 rc 0. bash 3.2 는 unbound 로 rc 1 — 우연이지 게이트가 아니다.
|
|
433
|
+
PT="ZZL29POS$(printf '%s' 4419)"
|
|
434
|
+
printf 'q1\tpositive\t질문P\t%s\t\t\n' "$PT" > "$T/q6.tsv" # 6 열 컨트롤 (round2 실물 폭)
|
|
435
|
+
printf 'q1\tpositive\t질문|P\t%s\t\t\n' "$PT" > "$T/q_pipe.tsv"
|
|
436
|
+
: > "$T/q_empty.tsv"
|
|
437
|
+
mkdir -p "$T/o"; printf '답은 %s 입니다.\n' "$PT" > "$T/o/q1_ARM_r1.txt"; printf '모르겠습니다. 기록이 없습니다.\n' > "$T/o/q1_CTRL_r1.txt"
|
|
438
|
+
O=$(run29 "$T/q6.tsv" "$T/o"); rc=$?
|
|
439
|
+
[ "$rc" = 0 ] && printf '%s' "$O" | grep -q '🟢' && ok "L29a CONTROL 6열 정상 qset → 🟢 rc 0" || no "L29a 컨트롤이 죽었다 (rc=$rc) — 아래는 무의미"
|
|
440
|
+
O=$(run29 "$T/q_pipe.tsv" "$T/o"); rc=$?
|
|
441
|
+
# 🟥 «크래시»는 게이트가 아니다 — bash 3.2 는 set -u 의 `"${ROWS[@]}"` unbound 로 rc 1 에 죽어 rc≠0 이
|
|
442
|
+
# «우연히» 나온다(bash 5.3 = CI 에선 🟢 rc 0). 그래서 unbound 를 명시적으로 배제한다.
|
|
443
|
+
if [ "$rc" -ne 0 ] && ! printf '%s' "$O" | grep -q '🟢 계기 생존' && ! printf '%s' "$O" | grep -q 'unbound variable'; then ok "L29b 값에 '|' → 타입된 차단(rc≠0 ∧ 🟢 없음 ∧ 크래시 아님) [rc=$rc]"; else no "L29b 파서 실패가 «🟢 계기 생존»으로 접히거나 크래시로만 막힌다 [rc=$rc]"; fi
|
|
444
|
+
O=$(run29 "$T/q_empty.tsv" "$T/o"); rc=$?
|
|
445
|
+
if [ "$rc" -ne 0 ] && ! printf '%s' "$O" | grep -q '🟢 계기 생존' && ! printf '%s' "$O" | grep -q 'unbound variable'; then ok "L29c 빈 qset → 타입된 차단(rc≠0 ∧ 🟢 없음 ∧ 크래시 아님) [rc=$rc]"; else no "L29c 빈 qset 이 «🟢 계기 생존»으로 접히거나 크래시로만 막힌다 [rc=$rc]"; fi
|
|
446
|
+
# ── L29d 🟥 오염 게이트가 6 열 qset 에서 눈을 감지 않는다 (read 가 뒤 열을 tok 에 삼키는 결함) ──
|
|
447
|
+
# 실측(원본): 4 열 + tracked 토큰 → exit 5 · 6 열 같은 토큰 → 통과. 토큰은 «이 파일 자신»에 확실히 있는 낱말.
|
|
448
|
+
printf 'q1\tpositive\t질문P\tcontext_continuity\t\t\n' > "$T/q6c.tsv"
|
|
449
|
+
O=$(run29 "$T/q6c.tsv" "$T/o"); rc=$?
|
|
450
|
+
[ "$rc" = 5 ] && ok "L29d 6열 qset 의 tracked 토큰 → 오염 차단(5)" || no "L29d 6열 qset 에서 오염 게이트가 눈을 감는다 [rc=$rc]"
|
|
451
|
+
|
|
426
452
|
echo "verdict watermark lanes: $pass passed, $fail failed"
|
|
427
453
|
[ "$fail" -eq 0 ] || exit 1
|
|
@@ -132,7 +132,7 @@ done
|
|
|
132
132
|
_probe_hits() {
|
|
133
133
|
local pat="$1"; shift
|
|
134
134
|
grep -lE -- "$pat" "$@" 2>/dev/null
|
|
135
|
-
return "${PIPESTATUS[0]:-$?}"
|
|
135
|
+
return "${PIPESTATUS[0]:-$?}" # portability-noqa: shebang (line 1) pins bash — PIPESTATUS is a real array there, unlike zsh
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
# ── 1단계: 컨트롤. 살아있음을 증명하기 전에는 타깃을 인쇄하지 않는다 ──
|
|
@@ -179,7 +179,7 @@ MISS=0; N=0
|
|
|
179
179
|
while IFS=$'\t' read -r kind pat label; do
|
|
180
180
|
[ "${kind:-}" = "TARGET" ] || continue
|
|
181
181
|
N=$((N+1))
|
|
182
|
-
hits=$(_probe_hits "$pat" "${FILES[@]}" | wc -l | tr -d ' '); _rc=${PIPESTATUS[0]}
|
|
182
|
+
hits=$(_probe_hits "$pat" "${FILES[@]}" | wc -l | tr -d ' '); _rc=${PIPESTATUS[0]} # portability-noqa: shebang (line 1) pins bash — PIPESTATUS is a real array there, unlike zsh
|
|
183
183
|
if [ "$_rc" -ge 2 ]; then
|
|
184
184
|
printf " 🟥 %-46s 프로브 오류\n" "${label:-$pat}"
|
|
185
185
|
BAD_PAT="${BAD_PAT:+$BAD_PAT, }TARGET/${label:-$pat}"
|
|
@@ -1470,6 +1470,18 @@ validate_soul_present_leg() { # $1 = marker path
|
|
|
1470
1470
|
echo " «이 기록이 템플릿 자신인가»만 본다 — 채널 검사다."
|
|
1471
1471
|
return 1
|
|
1472
1472
|
fi
|
|
1473
|
+
# ── ①영혼 2칸 존재 검사 (advisory only, 2026-09-04 — six_axis_review 판정안 4) ─────
|
|
1474
|
+
# «성공 정의»와 «절대 안 함» 두 낱말(변형 포함)이 둘 다 있는지만 본다. Mechanization
|
|
1475
|
+
# Boundary: 존재만 — 두 칸이 다 있어도 내용이 옳다는 보장은 없고, 하나가 없어도 커밋을
|
|
1476
|
+
# 막지 않는다(위 non-vacuity 검사가 이미 최소 6낱말을 요구·차단한다 — 이건 그 위에 얹는
|
|
1477
|
+
# 별도 관측이지 새 차단이 아니다). 「없음」 선언 분기는 위에서 이미 return 했으므로 여기
|
|
1478
|
+
# 도달하지 않는다.
|
|
1479
|
+
_soul_missing=""
|
|
1480
|
+
printf '%s' "$body" | grep -qE '성공 ?정의' || _soul_missing="성공 정의"
|
|
1481
|
+
printf '%s' "$body" | grep -qE '절대 ?안 ?함' || _soul_missing="${_soul_missing:+$_soul_missing · }절대 안 함"
|
|
1482
|
+
if [ -n "$_soul_missing" ]; then
|
|
1483
|
+
echo " ⚠️ ①영혼 2칸 미달 — 빠진 낱말: $_soul_missing (advisory, 커밋은 안 막는다)"
|
|
1484
|
+
fi
|
|
1473
1485
|
echo " ✅ ①영혼 present: $words word(s)"
|
|
1474
1486
|
return 0
|
|
1475
1487
|
}
|
|
@@ -2658,14 +2670,25 @@ if [ -n "$MD_STAGED" ]; then
|
|
|
2658
2670
|
echo "[Pointers] Detail-pointer resolution (staged markdown)..."
|
|
2659
2671
|
PTR_FAIL=0; PTR_CHECKED=0
|
|
2660
2672
|
for f in $MD_STAGED; do
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2673
|
+
# Read the STAGED blob, not the working-tree file. diff-filter=ACMR already guarantees $f
|
|
2674
|
+
# exists in the index, but a file can be staged and then deleted or further-edited on disk
|
|
2675
|
+
# before commit — `git commit` commits the INDEX, so `[ -f "$REPO_ROOT/$f" ] || continue`
|
|
2676
|
+
# here silently dropped the check on a committed broken pointer (deleted-on-disk case) and,
|
|
2677
|
+
# for a further-edited case, would have checked the wrong bytes entirely. Reproduced 2026-09-03:
|
|
2678
|
+
# stage a .md with a broken `**Detail**: See §X` pointer, `rm` it from disk (index untouched),
|
|
2679
|
+
# commit — PTR_CHECKED stayed 0 and the broken pointer landed silently.
|
|
2680
|
+
_md_blob="$(git -C "$REPO_ROOT" show ":$f" 2>/dev/null)" || {
|
|
2681
|
+
echo " ❌ $f: staged (diff-filter=ACMR) but unreadable from the index — cannot verify its Detail pointers"
|
|
2682
|
+
PTR_FAIL=1; continue
|
|
2683
|
+
}
|
|
2684
|
+
fdir="$(cd "$REPO_ROOT/$(dirname "$f")" 2>/dev/null && pwd)"
|
|
2685
|
+
[ -n "$fdir" ] || fdir="$REPO_ROOT/$(dirname "$f")"
|
|
2686
|
+
blocks="$(printf '%s\n' "$_md_blob" | awk '
|
|
2664
2687
|
inblk && /^>/ { blk=blk" "$0; next }
|
|
2665
2688
|
inblk { print blk; inblk=0; blk="" }
|
|
2666
2689
|
/\*\*Detail\*\*: See/ { blk=$0; inblk=1; next }
|
|
2667
2690
|
END { if(inblk) print blk }
|
|
2668
|
-
'
|
|
2691
|
+
')"
|
|
2669
2692
|
[ -z "$blocks" ] && continue
|
|
2670
2693
|
while IFS= read -r block; do
|
|
2671
2694
|
[ -z "$block" ] && continue
|
|
@@ -50,7 +50,28 @@
|
|
|
50
50
|
"built on a 46-PR-behind clone and manufactured a duplicate of an upstream lane. found→extend",
|
|
51
51
|
"applies at repo level; this is its mechanical layer. Same advisory contract; fail-open on",
|
|
52
52
|
"every uncertainty (non-repo, no upstream, fetch failure).",
|
|
53
|
-
" bash scripts/test_stale_clone_guard_lanes.sh # expect: all known pairs hold (count self-reported)"
|
|
53
|
+
" bash scripts/test_stale_clone_guard_lanes.sh # expect: all known pairs hold (count self-reported)",
|
|
54
|
+
"",
|
|
55
|
+
"FOURTH GUARD, Bash matcher — backtick_guard.sh (added 2026-09-03, weekly_audit_2026-09-02 HIGH #1):",
|
|
56
|
+
"advisory when a Bash call carries a backtick inside a shell DOUBLE-QUOTING context — an unquoted",
|
|
57
|
+
"heredoc body (<<EOF) or a \"double-quoted string\". There a backtick is command substitution: the text",
|
|
58
|
+
"between the backticks is REPLACED by the command's output — empty when it names no command, so the",
|
|
59
|
+
"text is silently DELETED and the record commits with a hole (every record hook checks presence, not",
|
|
60
|
+
"completeness). Measured 7x 2026-08-10..09-02 with a resident memory rule that failed each time because",
|
|
61
|
+
"the actor's task had a different name than the rule's title. Quote-aware state machine, not a regex:",
|
|
62
|
+
"single-quoted text, quoted heredocs (<<'EOF'), and escaped backticks are CLEAN. Same advisory contract;",
|
|
63
|
+
"escalate with FH_BACKTICK_BLOCK=1; opt out with `# noqa: backtick` (whole payload).",
|
|
64
|
+
" bash scripts/test_backtick_guard_lanes.sh # expect: all known pairs hold (count self-reported)",
|
|
65
|
+
"",
|
|
66
|
+
"FIFTH GUARD, Edit|Write|Bash matcher — proposal_hook.sh (added 2026-09-03, identity ⑤ r4):",
|
|
67
|
+
"advisory when a verdict/guard line in scripts/**/*.sh or templates/*.sh is about to change: puts ONE",
|
|
68
|
+
"instruction into the model context — offer the user a known-pair control + degrade_direction_scan.sh in",
|
|
69
|
+
"one line. Measured: the same rule as a CLAUDE.md table row fired 1/15 at floor tier (r3); as this hook",
|
|
70
|
+
"9/9 relay on editing reps and 0/5 on a usage-string edit (r4). Quote-aware: an edit confined to quoted",
|
|
71
|
+
"strings does not fire. Bash path (sed -i / > / tee into a .sh) covered with a weaker rule (named",
|
|
72
|
+
"residual in the script header). Evidence row in .claude/.proposal_hook_events.tsv. Relaying an",
|
|
73
|
+
"injected instruction is not initiative — see the r4 RESULT for what this does and does not claim.",
|
|
74
|
+
" bash scripts/test_proposal_hook_lanes.sh # expect: all known pairs hold (count self-reported)"
|
|
54
75
|
],
|
|
55
76
|
"project_settings_json": {
|
|
56
77
|
"hooks": {
|
|
@@ -67,6 +88,11 @@
|
|
|
67
88
|
"type": "command",
|
|
68
89
|
"command": "bash \"$CLAUDE_PROJECT_DIR/scripts/destructive_pre_gate.sh\"",
|
|
69
90
|
"timeout": 5
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"type": "command",
|
|
94
|
+
"command": "bash \"$CLAUDE_PROJECT_DIR/scripts/backtick_guard.sh\"",
|
|
95
|
+
"timeout": 5
|
|
70
96
|
}
|
|
71
97
|
]
|
|
72
98
|
},
|
|
@@ -79,6 +105,16 @@
|
|
|
79
105
|
"timeout": 20
|
|
80
106
|
}
|
|
81
107
|
]
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"matcher": "Edit|Write|Bash",
|
|
111
|
+
"hooks": [
|
|
112
|
+
{
|
|
113
|
+
"type": "command",
|
|
114
|
+
"command": "bash \"$CLAUDE_PROJECT_DIR/scripts/proposal_hook.sh\"",
|
|
115
|
+
"timeout": 5
|
|
116
|
+
}
|
|
117
|
+
]
|
|
82
118
|
}
|
|
83
119
|
]
|
|
84
120
|
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# fh-commons — Contention-Born Skill Habitat
|
|
2
|
-
|
|
3
|
-
**A different layer of plugin from fh-meta.**
|
|
4
|
-
|
|
5
|
-
fh-meta = meta-engineering skills for operating, diagnosing, and improving the harness itself
|
|
6
|
-
fh-commons = general-purpose utility skills born from the contention layer, transplantable to any project
|
|
7
|
-
|
|
8
|
-
## Placement Criteria
|
|
9
|
-
|
|
10
|
-
When the `contention-layer` skill harvests contentions and generates new skill candidates:
|
|
11
|
-
|
|
12
|
-
| Determination | Destination |
|
|
13
|
-
|---|---|
|
|
14
|
-
| Harness engineering in nature | `fh-meta` |
|
|
15
|
-
| Project-general · domain-agnostic | **`fh-commons`** (this plugin) |
|
|
16
|
-
| Domain/team specific | field harvest (decided by the field team) |
|
|
17
|
-
|
|
18
|
-
## Skill List
|
|
19
|
-
|
|
20
|
-
| Skill | Description | Contention Parent |
|
|
21
|
-
|---|---|---|
|
|
22
|
-
| `convergence-loop` | General-purpose gate reinforcement that replaces single-pass structures with a convergence loop of up to N rounds | harvest-loop (recurring single-pass-distrust pattern across hub gates) |
|
|
23
|
-
| `deliberation` | Innovator → Devil-Advocate → Mediator 3-layer multi-perspective synthesis. Generates conditional verdicts without binary win/loss | Migrated from fh-meta (2026-05-23 — domain-agnostic general decision structure) |
|
|
24
|
-
| `ko-tech-writer` | Korean technical-writing pipeline — register detection, venue calibration, translationese scan, honesty layering, number/claim gate, perceptual QA | Field-session harvest (2026-08-10 — conference tech-doc revision session, operator-driven shipping) |
|
|
25
|
-
|
|
26
|
-
## The `origin` Field
|
|
27
|
-
|
|
28
|
-
Commons skills include the following fields in their SKILL.md frontmatter:
|
|
29
|
-
|
|
30
|
-
```yaml
|
|
31
|
-
# Contention-born (extracted by contention-layer)
|
|
32
|
-
origin: contention-layer
|
|
33
|
-
contention-parents: [skill-A, skill-B]
|
|
34
|
-
|
|
35
|
-
# Migrated from fh-meta (reclassified as general-purpose)
|
|
36
|
-
origin: fh-meta
|
|
37
|
-
migration: "YYYY-MM-DD — reason for migration"
|
|
38
|
-
```
|