@chrono-meta/fh-gate 3.2.0 → 3.4.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 (48) hide show
  1. package/.claude/registry/agent_cards.json +1 -1
  2. package/.claude/rules/fh_4axis_gate.md +25 -0
  3. package/.claude-plugin/marketplace.json +3 -3
  4. package/AGENTS.md +2 -2
  5. package/CATALOG.md +4 -4
  6. package/CHEATSHEET.md +1 -1
  7. package/CLAUDE.md +4 -4
  8. package/docs/OUTPUT_EVIDENCE.md +1 -1
  9. package/docs/STANDARDS_ALIGNMENT.md +1 -1
  10. package/docs/codex-compat.md +1 -1
  11. package/knowledge/shared/harness-core/agents_md_runtime_details.md +2 -2
  12. package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +30 -3
  13. package/knowledge/shared/harness-core/iso_ai_standards_crosswalk.md +1 -1
  14. package/knowledge/shared/harness-core/skill_quality_rubric.md +1 -1
  15. package/knowledge/shared/learnings/subagent_invocations_log.yaml +31 -0
  16. package/knowledge/shared/rules/modes_and_value.md +2 -2
  17. package/package.json +2 -1
  18. package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
  19. package/plugins/fh-commons/README.md +38 -0
  20. package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
  21. package/plugins/fh-meta/CHANGELOG.md +95 -0
  22. package/plugins/fh-meta/skills/agent-composer/SKILL.md +2 -2
  23. package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +40 -0
  24. package/plugins/fh-meta/skills/frontier-digest/SKILL.md +1 -1
  25. package/plugins/fh-meta/skills/frontier-digest/SKILL_detail.md +43 -5
  26. package/plugins/fh-meta/skills/{hub-cc-pr-reviewer → harness-pr-reviewer}/SKILL.md +75 -3
  27. package/plugins/fh-meta/skills/{hub-cc-pr-reviewer → harness-pr-reviewer}/SKILL_detail.md +2 -2
  28. package/plugins/fh-meta/skills/harvest-loop/SKILL_detail.md +2 -2
  29. package/plugins/fh-meta/skills/install-doctor/SKILL.md +1 -1
  30. package/plugins/fh-meta/skills/install-wizard/SKILL.md +1 -1
  31. package/plugins/fh-meta/skills/meta-prompt-builder/SKILL.md +1 -1
  32. package/plugins/fh-meta/skills/pipeline-conductor/SKILL.md +1 -1
  33. package/plugins/fh-meta/skills/plugin-recommender/SKILL.md +1 -1
  34. package/plugins/fh-meta/skills/sim-conductor/SKILL.md +1 -1
  35. package/plugins/fh-qp/.claude-plugin/plugin.json +1 -1
  36. package/scripts/finding_fleet.sh +391 -13
  37. package/scripts/finding_pipeline.sh +370 -11
  38. package/scripts/finding_verifier.sh +31 -2
  39. package/scripts/finding_verify.py +198 -13
  40. package/scripts/frontier_digest_autopilot.sh +3 -3
  41. package/scripts/gate_shape_scan.sh +16 -2
  42. package/scripts/test_finding_pipeline_lanes.sh +1556 -4
  43. package/scripts/test_gate_shape_scan_lanes.sh +11 -0
  44. package/scripts/test_marker_crossfamily_lanes.sh +75 -3
  45. package/scripts/test_marker_standpoint_lanes.sh +31 -6
  46. package/templates/.git-hooks/pre-commit +152 -6
  47. package/templates/local_fh_context.md +1 -1
  48. package/templates/regression_guard.sh +1 -1
@@ -32,5 +32,16 @@ bash "$SCAN" "$D/au.py" >/dev/null 2>&1; R1=$?; bash "$SCAN" "$D/az.py" >/dev/nu
32
32
  MUT="$D/scan_mut.sh"; /usr/bin/sed 's/^EXPOSURE_RE=.*/EXPOSURE_RE='"'"'(NEVER_MATCHES_XYZ)'"'"'/' "$SCAN" > "$MUT"
33
33
  bash "$MUT" "$D/serve.go" >/dev/null 2>&1; RC=$?
34
34
  [ "$RC" -eq 1 ] && ok "L7 되돌림: EXPOSURE 제거 → serve.go 가 NOT 로 떨어진다 (레인이 실물을 잰다)" || no "L7 되돌림" "rc=$RC (제거해도 초록이면 앵커가 장식)"
35
+ # L8 — pmh-dev #76: 여러 줄 docstring 안쪽 줄은 게이트가 아니다 (""" · ''' · 컨트롤 = docstring 뒤 실제 판정 줄)
36
+ printf 'def t():\n """docstring\n PASS allow true\n """\n return 1\n' > "$D/doc3.py"
37
+ printf "def t():\n '''docstring\n return Verdict.ALLOW in prose\n '''\n return 1\n" > "$D/doc1.py"
38
+ printf 'def t():\n """docstring\n PASS allow true\n """\n return Verdict.ALLOW\n' > "$D/doc_ctrl.py"
39
+ bash "$SCAN" "$D/doc3.py" >/dev/null 2>&1; R1=$?; bash "$SCAN" "$D/doc1.py" >/dev/null 2>&1; R2=$?; bash "$SCAN" "$D/doc_ctrl.py" >/dev/null 2>&1; R3=$?
40
+ [ "$R1" -eq 1 ] && [ "$R2" -eq 1 ] && [ "$R3" -eq 0 ] && ok "L8 docstring 안쪽(triple-double · triple-single)은 NOT · 그 뒤 실제 판정 줄은 GATE-SHAPED" || no "L8 docstring" "triple=$R1 single=$R2 ctrl=$R3"
41
+ # L8b — 되돌림: 짝 상태 추적을 죽이면 L8 이 빨개지는가
42
+ MUT2="$D/scan_mut2.sh"; /usr/bin/sed 's/if (n % 2 == 1) { indoc=1; next }/if (0) { indoc=1; next }/' "$SCAN" > "$MUT2"
43
+ bash "$MUT2" "$D/doc3.py" >/dev/null 2>&1; RC=$?
44
+ [ "$RC" -eq 0 ] && ok "L8b 되돌림: 짝 상태 제거 → doc3.py 가 GATE-SHAPED 로 돌아간다 (레인이 실물을 잰다)" || no "L8b 되돌림" "rc=$RC"
45
+
35
46
  /bin/rm -rf "$D"
36
47
  echo "PASS=$PASS FAIL=$FAIL"; [ "$FAIL" -eq 0 ] && { echo "FAILED=0"; exit 0; } || { echo "FAILED=1"; exit 1; }
@@ -38,7 +38,8 @@ sed -n '/^validate_crossfamily_leg()/,/^}/p' "$HOOK" > "$T/fn.sh"
38
38
  # itself lives OUTSIDE the function (same placement as DEFEATER_GRACE_DATE next to
39
39
  # validate_defeater_leg), so it is not part of this extraction and is supplied by the harness
40
40
  # below, exactly like GRACE_RES is.
41
- if ! grep -q 'DEGRADED_PANEL_UNUSED' "$T/fn.sh" || ! grep -q '_res_active' "$T/fn.sh"; then
41
+ if ! grep -q 'DEGRADED_PANEL_UNUSED' "$T/fn.sh" || ! grep -q '_res_active' "$T/fn.sh" \
42
+ || ! grep -q '_ev_active' "$T/fn.sh"; then
42
43
  echo "❌ HARNESS-ERROR — validate_crossfamily_leg did not extract from $HOOK (or the residency"
43
44
  echo " token block is missing from it). Fixtures below would measure an empty/stale function."
44
45
  exit 1
@@ -46,6 +47,11 @@ fi
46
47
 
47
48
  GRACE_RES=$(grep -m1 '^RESIDENCY_TOKEN_GRACE_DATE=' "$HOOK" | sed -E 's/.*"(.*)"/\1/')
48
49
  [ -n "$GRACE_RES" ] || { echo "❌ HARNESS-ERROR — RESIDENCY_TOKEN_GRACE_DATE 를 못 읽었다"; exit 1; }
50
+ # 🟥 evidence= (2026-09-12) 도 같은 자리에 산다. 이 값을 «안 주입하면» 함수 안에서 빈 문자열이 되고
51
+ # `[ "$mdate" \< "" ]` 은 언제나 거짓 → grace 가 통째로 무효 → panel 을 쓰는 **기존 픽스처 전부**가
52
+ # evidence 없다는 이유로 BLOCK 된다. 실행해 보지 않으면 안 보이는 자리라 가드를 둔다.
53
+ GRACE_EV=$(grep -m1 '^EVIDENCE_TOKEN_GRACE_DATE=' "$HOOK" | sed -E 's/.*"(.*)"/\1/')
54
+ [ -n "$GRACE_EV" ] || { echo "❌ HARNESS-ERROR — EVIDENCE_TOKEN_GRACE_DATE 를 못 읽었다"; exit 1; }
49
55
  # Every fixture below this point (k*/b*/x*/c*) tests crossfamily's PRE-EXISTING shape rules —
50
56
  # panel format, embedding/reranker filters, declined, degrade grounds — none of them are testing
51
57
  # the residency-token addition. So mk()/run() stamp a date BEFORE RESIDENCY_TOKEN_GRACE_DATE into
@@ -61,7 +67,7 @@ PRE_RES="2026-08-01" # < RESIDENCY_TOKEN_GRACE_DATE (2026-09-05) by constructi
61
67
  # every pre-existing fixture was silently treated as un-exempt).
62
68
  mk() { printf "$1" > "$T/.axes_23_passed_fix_x_$2_${PRE_RES}.marker"; } # $1=body $2=fixture name
63
69
  run() {
64
- bash -c "RESIDENCY_TOKEN_GRACE_DATE='$GRACE_RES'; source '$T/fn.sh'; validate_crossfamily_leg '$T/.axes_23_passed_fix_x_$1_${PRE_RES}.marker'" \
70
+ bash -c "RESIDENCY_TOKEN_GRACE_DATE='$GRACE_RES'; EVIDENCE_TOKEN_GRACE_DATE='$GRACE_EV'; source '$T/fn.sh'; validate_crossfamily_leg '$T/.axes_23_passed_fix_x_$1_${PRE_RES}.marker'" \
65
71
  >/dev/null 2>&1
66
72
  }
67
73
 
@@ -197,7 +203,7 @@ check c5 BLOCK "qwen-embedding — same overlap, other direction"
197
203
  # Same ordering requirement as mk()/run() above — date LAST, immediately before `.marker`.
198
204
  resfix() { printf "%s\n" "$1" > "$T/.axes_23_passed_fix_x_$3_$2.marker"; } # $1=body $2=date $3=fname
199
205
  resrun() { # $1=date $2=fname → rc
200
- bash -c "RESIDENCY_TOKEN_GRACE_DATE='$GRACE_RES'; source '$T/fn.sh'; validate_crossfamily_leg '$T/.axes_23_passed_fix_x_$2_$1.marker'" \
206
+ bash -c "RESIDENCY_TOKEN_GRACE_DATE='$GRACE_RES'; EVIDENCE_TOKEN_GRACE_DATE='$GRACE_EV'; source '$T/fn.sh'; validate_crossfamily_leg '$T/.axes_23_passed_fix_x_$2_$1.marker'" \
201
207
  >/dev/null 2>&1
202
208
  }
203
209
  rescheck() { # $1=fname $2=date $3=body $4=expect(PASS|BLOCK) $5=label
@@ -250,6 +256,72 @@ rescheck r16 "$GRACE_RES" 'crossfamily: panel(codex) — residency=CLEAN(files=3
250
256
  rescheck r17 "$GRACE_RES" 'crossfamily: panel(codex) — residency=CLEAN(files=3, stripped=2) — stripped files were named to the reviewer' \
251
257
  PASS "r17 control — a second em-dash in the grounds does not break the token parse"
252
258
 
259
+ echo
260
+ echo "── evidence= token in crossfamily grounds (grace = $GRACE_EV) ──"
261
+ # 🟥 왜 있나 — arXiv:2609.10969 이 고정예산 2×2(48 템플릿 · 2,880 시나리오)로 두 축을 분리해 재니
262
+ # «같은 증거를 읽는 교차-모델 투표» 가 위험 제안의 62.9 % 를 승인하고 «독립 출처» 는 22.9 % 였다
263
+ # (출처 40.9 %p vs 모델 다양성 11.3 %p = 3.6 배). 이 훅이 하드 차단하는 축이 약한 쪽이고,
264
+ # `panel(codex, gemini)` 가 «같은 diff» 를 읽은 패널이면 62.9 % 팔인데 기록은 강한 값으로 남는다.
265
+ # 이 토큰은 그 구분을 «말할 수 있게» 만든다 — SHARED 를 불법으로 만드는 것이 아니다.
266
+ EVOK='residency=CLEAN(files=3)'
267
+ evcheck() { rescheck "$1" "$GRACE_EV" "$3" "$4" "$2"; } # $1=fname $2=label $3=body $4=expect
268
+
269
+ evcheck e1 "e1 panel + residency 만 있고 evidence= 없음 → BLOCK (구분이 안 적히면 강한 값으로 오독된다)" \
270
+ "crossfamily: panel(codex) — $EVOK · R1..R2, 4 findings" BLOCK
271
+ evcheck e2 "e2 evidence=SHARED(...) — 정직한 약한 팔도 **통과한다**(불법이 아니다)" \
272
+ "crossfamily: panel(codex) — $EVOK · evidence=SHARED(same staged diff sent to both) · R1, 3 findings" PASS
273
+ evcheck e3 "e3 evidence=INDEPENDENT(...) → PASS" \
274
+ "crossfamily: panel(codex, gemini) — $EVOK · evidence=INDEPENDENT(each ran its own checkout) · R1..R2" PASS
275
+ evcheck e4 "e4 evidence=MIXED(...) → PASS" \
276
+ "crossfamily: panel(codex, gemini) — $EVOK · evidence=MIXED(codex got the diff, gemini ran the repo) · R1" PASS
277
+ evcheck e5 "e5 닫힌 집합 밖 evidence=PARTIAL(...) → BLOCK" \
278
+ "crossfamily: panel(codex) — $EVOK · evidence=PARTIAL(some) · R1" BLOCK
279
+ evcheck e6 "e6 닫는 괄호 없는 evidence=SHARED(x → 형식 위반(부재로 읽히면 fail-open)" \
280
+ "crossfamily: panel(codex) — $EVOK · evidence=SHARED(same diff · R1" BLOCK
281
+ evcheck e7 "e7 토큰 두 개 → BLOCK (읽는 쪽이 첫 것만 취한다)" \
282
+ "crossfamily: panel(codex) — $EVOK · evidence=SHARED(same diff to both) · evidence=INDEPENDENT(no) · R1" BLOCK
283
+ evcheck e8 "e8 SHARED 본문이 공허(한 낱말) → BLOCK — 약한 팔을 «기록된 것처럼» 남기지 못한다" \
284
+ "crossfamily: panel(codex) — $EVOK · evidence=SHARED(diff) · R1, 3 findings" BLOCK
285
+ evcheck e8b "e8b 컨트롤 — INDEPENDENT 는 자기서술적이라 한 낱말도 통과(과차단이 override 를 훈련시킨다)" \
286
+ "crossfamily: panel(codex) — $EVOK · evidence=INDEPENDENT(rerun) · R1, 3 findings" PASS
287
+ evcheck e8c "e8c SHARED 본문이 자리표시자 <...> → BLOCK" \
288
+ "crossfamily: panel(codex) — $EVOK · evidence=SHARED(<what>) · R1" BLOCK
289
+ evcheck e9 "e9 컨트롤 — 패널이 안 돌았으면(DEGRADED) evidence= 는 요구하지 않는다(전면 요구가 아니다)" \
290
+ 'crossfamily: DEGRADED_SINGLE_FAMILY — residency=CLEAN(files=0) probed codex/agy/gemini, 0 reachable' PASS
291
+ rescheck e10 "2026-09-11" "crossfamily: panel(codex) — $EVOK · R1..R2, 4 findings" PASS \
292
+ "e10 grace 경계 — grace 하루 전 날짜의 마커는 evidence= 없이도 통과(소급 없음)"
293
+
294
+ echo "· cross-family(agy) 가 초판 evidence= 에서 찾은 여섯 결함 — 전부 실행으로 재현 후 수리 ·"
295
+ evcheck e11 "e11 🟥 중첩 한 겹은 정당하다 — MIXED(codex(diff), gemini(repo)) (초판은 본문이 'codex(diff' 로 잘려 과차단)" \
296
+ "crossfamily: panel(codex) — $EVOK · evidence=MIXED(codex(diff), gemini(repo)) · R1" PASS
297
+ evcheck e11b "e11b 두 겹 중첩은 여전히 형식 위반(무한 중첩을 정규식으로 쫓지 않는다)" \
298
+ "crossfamily: panel(codex) — $EVOK · evidence=SHARED(a(b(c))) · R1" BLOCK
299
+ evcheck e12 "e12 🟥 INDEPENDENT() 빈 본문 → BLOCK (초판은 통과 — «길이 미검사» 를 «본문 없어도 됨» 으로 접었다)" \
300
+ "crossfamily: panel(codex) — $EVOK · evidence=INDEPENDENT() · R1" BLOCK
301
+ evcheck e12b "e12b 🟥 INDEPENDENT(<what>) 자리표시자 → BLOCK (초판 통과)" \
302
+ "crossfamily: panel(codex) — $EVOK · evidence=INDEPENDENT(<what>) · R1" BLOCK
303
+ evcheck e12c "e12c 컨트롤 — INDEPENDENT(rerun) 은 짧아도 통과(길이 바는 SHARED/MIXED 에만)" \
304
+ "crossfamily: panel(codex) — $EVOK · evidence=INDEPENDENT(rerun) · R1" PASS
305
+ evcheck e13 "e13 🟥 하이픈 복합어 SHARED(same-diff) → PASS (초판은 wc -w 로 1 낱말이라 과차단)" \
306
+ "crossfamily: panel(codex) — $EVOK · evidence=SHARED(same-diff) · R1" PASS
307
+ evcheck e13b "e13b 🟥 한국어 본문 SHARED(동일 프롬프트 파일) → PASS — 이 레포는 근거를 한국어로 쓴다" \
308
+ "crossfamily: panel(codex) — $EVOK · evidence=SHARED(동일 프롬프트 파일) · R1" PASS
309
+ evcheck e13c "e13c 컨트롤 — SHARED(diff) 는 여전히 너무 얇다(어느 산출물인지 안 말한다)" \
310
+ "crossfamily: panel(codex) — $EVOK · evidence=SHARED(diff) · R1" BLOCK
311
+ rescheck e14 "2026-09-11" "crossfamily: panel(codex) — $EVOK · evidence=PARTIAL(some) · R1" BLOCK \
312
+ "e14 🟥 pre-grace 라도 «쓴 토큰» 의 형식은 본다 — 부재는 면제, 오작성은 면제 아님(초판 통과)"
313
+ rescheck e14b "2026-09-11" "crossfamily: panel(codex) — $EVOK · R1..R2, 4 findings" PASS \
314
+ "e14b 컨트롤 — pre-grace + 토큰 부재는 그대로 통과(면제가 살아 있다)"
315
+ # e15 — 상수 미주입이 «조용히 grace 를 끄는» 것이 아니라 크게 죽는지. 다른 레인과 러너가 달라 직접 돈다.
316
+ N=$((N+1))
317
+ resfix "crossfamily: panel(codex) — $EVOK · R1..R2" "2026-09-11" e15
318
+ _E15=$(bash -c "RESIDENCY_TOKEN_GRACE_DATE='$GRACE_RES'; source '$T/fn.sh'; validate_crossfamily_leg '$T/.axes_23_passed_fix_x_e15_2026-09-11.marker'" 2>&1)
319
+ if printf '%s' "$_E15" | grep -q 'EVIDENCE_TOKEN_GRACE_DATE unset'; then
320
+ echo "✅ e15 🟥 상수 미주입 → 크게 죽는다(초판은 조용히 grace 를 꺼서 pre-grace 마커 전부를 막았다) → BLOCK"
321
+ else
322
+ echo "❌ e15 상수 미주입 — 가드가 안 울렸다: $(printf '%s' "$_E15" | head -2)"; FAIL=1
323
+ fi
324
+
253
325
  echo
254
326
  if [ "$FAIL" -eq 0 ]; then echo "✅ all $N fixtures behave"; else echo "❌ regression ($N fixtures run)"; fi
255
327
  exit "$FAIL"
@@ -42,11 +42,19 @@ if ! grep -q 'DEGRADED_NO_TARGET_ACCESS' "$T/fn.sh"; then
42
42
  exit 1
43
43
  fi
44
44
 
45
+ # 🟥 STANDPOINT_GROUNDS_GRACE_DATE 는 함수 «바깥» 에 살아서 이 추출에 안 들어온다. 주입하지 않으면
46
+ # `set -u` 아래에서 그 확장이 죽거나(날짜 있는 마커) 조용히 grace 를 무효화한다(날짜 없는 마커).
47
+ # 훅 쪽에도 `:?` 가드를 뒀으므로 미주입은 이제 **크게** 죽는다 — 그래서 여기서 반드시 읽어 온다.
48
+ GRACE_SP=$(grep -m1 '^STANDPOINT_GROUNDS_GRACE_DATE=' "$HOOK" | sed -E 's/.*"(.*)"/\1/')
49
+ [ -n "$GRACE_SP" ] || { echo "❌ HARNESS-ERROR — STANDPOINT_GROUNDS_GRACE_DATE 를 못 읽었다"; exit 1; }
50
+ # 마커명에 날짜를 박는다 — validate_standpoint_leg 의 mdate 추출은 `.*_(YYYY-MM-DD)\.marker$` 다.
51
+ # 기본은 grace «이후» 로 둔다: 새 계약이 기본값이어야 한다.
52
+ POST_SP="$GRACE_SP"
45
53
  FAIL=0
46
- lane() { # $1 = id $2 = expect(BLOCK|PASS) $3 = marker body
47
- local id="$1" expect="$2" body="$3" rc out
48
- printf '%s\n' "$body" > "$T/m.marker"
49
- out=$( bash -c 'set -uo pipefail; . "$1"; validate_standpoint_leg "$2"' _ "$T/fn.sh" "$T/m.marker" 2>&1 ); rc=$?
54
+ lane() { # $1 = id $2 = expect(BLOCK|PASS) $3 = marker body [$4 = marker date]
55
+ local id="$1" expect="$2" body="$3" mdate="${4:-$POST_SP}" rc out
56
+ printf '%s\n' "$body" > "$T/m_${mdate}.marker"
57
+ out=$( bash -c "set -uo pipefail; STANDPOINT_GROUNDS_GRACE_DATE='$GRACE_SP'; . \"\$1\"; validate_standpoint_leg \"\$2\"" _ "$T/fn.sh" "$T/m_${mdate}.marker" 2>&1 ); rc=$?
50
58
  local got=PASS; [ $rc -ne 0 ] && got=BLOCK
51
59
  if [ "$got" = "$expect" ]; then
52
60
  printf ' ✅ %-34s %s\n' "$id" "$got"
@@ -76,8 +84,25 @@ lane N5-deg-grounded PASS 'standpoint: DEGRADED_NOT_RUN — 표적 접근 가
76
84
  lane N6-deg-targets PASS 'standpoint: DEGRADED_NOT_RUN(qasp-dev · pmh-dev) — 전파 자산이라 해당되나 대상 레포에서 아무것도 실행 안 했다'
77
85
  lane N7-quoted-value PASS 'standpoint: "tier2(pmh-dev) — 그 레포 로컬 클론에서 실제로 실행, 양·음 arm 확인"'
78
86
 
79
- echo "== WARN siderecorded, not blocking (the named residual above) =="
80
- lane N8-tier2-vague PASS 'standpoint: tier2(pmh-dev) 그쪽 레포 기준으로 판단했다'
87
+ echo "== tier2+ groundsBLOCKING since 2026-09-12 (was advisory) =="
88
+ # ⚠️ 계약 변경 2026-09-12 — 이 픽스처는 PASS 단언하고 있었다(advisory 시절). 뒤집는 이유:
89
+ # arXiv:2609.10969 이 출처 축을 40.9 %p, 모델 축을 11.3 %p 로 재면서(n=2,880, 고정 호출예산)
90
+ # «강한 축의 grounds 를 advisory 로 두고 약한 축을 하드 차단» 이 균형이 아니게 됐다. 옛 기대값은
91
+ # 조용히 지우지 않고 여기서 이유와 함께 뒤집는다(crossfamily k3 의 선례와 같은 형태).
92
+ lane N8-tier2-vague BLOCK 'standpoint: tier2(pmh-dev) — 그쪽 레포 기준으로 판단했다'
93
+ lane N8b-tier2-named PASS 'standpoint: tier2(pmh-dev) — 그 클론에서 `bash scripts/x.sh` 를 돌렸고 출력은 30/30 PASS 였다'
94
+ lane N8c-tier1b-vague PASS 'standpoint: tier1b(pmh-dev) — 그쪽 파일만 읽었다, 실행은 안 했다'
95
+ # grace 경계 — 하루 전 날짜의 마커는 종전 advisory 대로 통과(소급 없음)
96
+ lane N8d-pre-grace PASS 'standpoint: tier2(pmh-dev) — 그쪽 레포 기준으로 판단했다' 2026-09-11
97
+
98
+ echo "== 과차단 수리 (cross-family agy 적발 — 차단이 된 순간 이건 override 를 훈련시키는 결함이다) =="
99
+ # 초판 키워드에 «뒤따르는 공백» 이 박혀 있어 콜론·다른 러너가 전부 막혔다. 실측으로 셋 다 BLOCK 이었다.
100
+ lane N8e-cargo PASS 'standpoint: tier2(pmh-dev) — 그쪽 클론에서 cargo check 로 확인했고 결과는 0 errors 였다'
101
+ lane N8f-python PASS 'standpoint: tier2(pmh-dev) — python scripts/eval.py 를 그 레포에서 수행, 12 passed 나왔다'
102
+ lane N8g-colon PASS 'standpoint: tier2(pmh-dev) — ran: ./ci.sh 결과 30/30 초록이었다'
103
+ lane N8h-output-only PASS 'standpoint: tier2(pmh-dev) — 그 레포에서 스위트를 돌렸고 53/53 으로 끝났다'
104
+ # 컨트롤 — 느슨하게 만든 뒤에도 «명령도 출력도 안 적은 줄» 은 여전히 막힌다(과교정 방지)
105
+ lane N8i-still-vague BLOCK 'standpoint: tier2(pmh-dev) — 그쪽 관점에서 충분히 검토했다고 판단한다'
81
106
 
82
107
  [ $FAIL -eq 0 ] && { echo "✅ all standpoint lanes behave"; exit 0; }
83
108
  echo "❌ standpoint lane regression"; exit 1
@@ -805,8 +805,25 @@ validate_defense_leg() { # $1 = marker path — fires ONLY at the floor tiers (s
805
805
  # would block every in-flight branch and teach `--no-verify`, which disarms the Destructive-Op
806
806
  # gate sharing this hook.
807
807
  RESIDENCY_TOKEN_GRACE_DATE="2026-09-05"
808
+ # 🟥 `evidence=` — panel(...) 이 «무엇을 받았는지» 를 적게 한다. 2026-09-12 신설, 근거는 외부 실측이다:
809
+ # arXiv:2609.10969 (48 템플릿 · 2,880 시나리오 · **고정예산 2×2**) 에서 «같은 증거를 읽는 교차-모델
810
+ # 투표» 는 위험 제안의 **62.9 %** 를 승인하고 «독립 출처» 는 **22.9 %** — 출처 효과 40.9 %p 대
811
+ # 모델 다양성 11.3 %p, **3.6 배**. 즉 이 훅이 이미 하드 차단하는 축(계열)이 약한 쪽이고,
812
+ # `panel(codex, gemini)` 가 **같은 diff 를 읽은 패널**이면 그것은 62.9 % 팔인데 기록은 강한 값으로
813
+ # 남는다. 그 구분을 닫는 토큰이다. 🟥 새 필드도 새 게이트도 만들지 않는다 — residency= 와 같은
814
+ # 자리(crossfamily 의 grounds) 에 같은 문법으로 얹는다.
815
+ EVIDENCE_TOKEN_GRACE_DATE="2026-09-12"
816
+ # 🟥 `standpoint:` tier2+ grounds — 2026-09-12 부로 **advisory 에서 차단으로**. 근거가 바뀌었다:
817
+ # 이 carve-out 은 증거가 FH 자기 관찰 n=4+8 뿐일 때 합리적이었다. arXiv:2609.10969 이 같은 비교를
818
+ # n=2,880 에서 **40.9 %p 대 11.3 %p** 로 재면서, 「강한 축의 grounds 를 advisory 로 두고 약한 축을
819
+ # 하드 차단한다」가 더는 균형이 아니게 됐다. §Mechanization Boundary 검사는 여전히 통과한다 —
820
+ # «저자가 실행한 명령과 본 출력을 명명했는가» 는 **기록의 속성**(존재·비공허·귀속가능)이지 결론이 아니다.
821
+ # 🟥 정직한 범위: 이 게이트는 **기록의 형태**만 막는다. 그 실행이 진짜였는지는 여전히 안 본다 —
822
+ # 게임 가능성은 §Mechanization Boundary 가 사람에게 남긴 의도된 잔여이고 이 변경이 닫는 구멍이 아니다.
823
+ STANDPOINT_GROUNDS_GRACE_DATE="2026-09-12"
808
824
  validate_crossfamily_leg() { # $1 = marker path
809
825
  local m="$1" line val reason fams mdate _res_active _res_tokens _res_ntok _res_wellformed _res_kind
826
+ local _ev_active _ev_tokens _ev_ntok _ev_full _ev_nfull _ev_wellformed _ev_kind _ev_body _ev_grace _ev_strip
810
827
  if [ ! -f "$m" ] || ! grep -qE '^[[:space:]]*crossfamily:[[:space:]]*[^[:space:]]' "$m"; then
811
828
  echo " ❌ FAIL — load-bearing file staged with no 'crossfamily:' line in the Axes 2-3 marker."
812
829
  echo " Verdict/gate/irreversible-surface code shares the author's blind spot with a"
@@ -869,6 +886,31 @@ validate_crossfamily_leg() { # $1 = marker path
869
886
  _res_kind=""
870
887
  [ "$_res_wellformed" -eq 1 ] && _res_kind=$(printf '%s' "$_res_full" | sed -E 's/^residency=([A-Za-z_]*)\(.*$/\1/')
871
888
 
889
+ # evidence= token — same grammar and same presence-first rule as residency= above. REQUIRED for
890
+ # panel(...) on markers dated >= EVIDENCE_TOKEN_GRACE_DATE; format-only-if-present elsewhere.
891
+ # 🟥 상수를 «먼저» 평가한다(cross-family agy 적발). 이걸 mdate 검사 뒤에 두면 두 결함이 같이 난다:
892
+ # ⓐ 미주입 시 `[ "$mdate" \< "" ]` 이 거짓이 되어 **grace 가 통째로 꺼지고** pre-grace 마커가
893
+ # 전부 막힌다(실측 확인) ⓑ mdate 가 비면 단축평가로 `:?` 가드가 아예 안 돈다.
894
+ _ev_grace="${EVIDENCE_TOKEN_GRACE_DATE:?EVIDENCE_TOKEN_GRACE_DATE unset — partial extraction of this hook? refusing to guess a grace date}"
895
+ _ev_active=1
896
+ if [ -n "$mdate" ] && [ "$mdate" \< "$_ev_grace" ]; then _ev_active=0; fi
897
+ _ev_tokens=$(printf '%s' "$reason" | grep -oE 'evidence=[^[:space:]]*' 2>/dev/null || true)
898
+ _ev_ntok=0
899
+ [ -n "$_ev_tokens" ] && _ev_ntok=$(printf '%s\n' "$_ev_tokens" | grep -c .)
900
+ # 🟥 `[^)]*` 는 중첩 괄호에서 안쪽 `)` 에 끊긴다 — `evidence=MIXED(codex(diff), gemini(repo))` 의
901
+ # 본문이 `codex(diff` 로 잘려 «공허» 판정을 받고 **정당한 기록이 하드 차단**됐다(실측).
902
+ # 한 겹 중첩을 허용한다(두 겹은 여전히 형식 위반 — 무한 중첩을 정규식으로 쫓지 않는다).
903
+ _ev_full=$(printf '%s' "$reason" | grep -oE 'evidence=(SHARED|INDEPENDENT|MIXED)\(([^()]|\([^()]*\))*\)' 2>/dev/null || true)
904
+ _ev_nfull=0
905
+ [ -n "$_ev_full" ] && _ev_nfull=$(printf '%s\n' "$_ev_full" | grep -c .)
906
+ _ev_wellformed=0
907
+ [ "$_ev_ntok" -eq 1 ] && [ "$_ev_nfull" -eq 1 ] && _ev_wellformed=1
908
+ _ev_kind=""; _ev_body=""
909
+ if [ "$_ev_wellformed" -eq 1 ]; then
910
+ _ev_kind=$(printf '%s' "$_ev_full" | sed -E 's/^evidence=([A-Z]*)\(.*$/\1/')
911
+ _ev_body=$(printf '%s' "$_ev_full" | sed -E 's/^evidence=[A-Z]*\(//; s/\)$//')
912
+ fi
913
+
872
914
  case "$val" in
873
915
  declined)
874
916
  # `declined` means ONE thing: the OPERATOR declined sidecars (UAP), a chosen floor. It was
@@ -1022,6 +1064,81 @@ validate_crossfamily_leg() { # $1 = marker path
1022
1064
  return 1
1023
1065
  fi
1024
1066
  fi
1067
+ # evidence= — WHAT the panel received. Same four checks in the same order as residency=
1068
+ # (duplicate → absent → shape → meaning). Why this exists at all: arXiv:2609.10969 measured
1069
+ # the two axes against each other at fixed call budget (48 templates, 2,880 scenarios) and a
1070
+ # cross-model vote over SHARED evidence approved 62.9% of unsafe proposals vs 22.9% with an
1071
+ # independent source — 40.9pp vs 11.3pp for model diversity. A `panel(codex, gemini)` that
1072
+ # read the same diff is that 62.9% arm while recording as this enum's STRONG value. The token
1073
+ # makes the distinction sayable; it does not make shared evidence illegal.
1074
+ # 🟥 grace 여부와 무관하게, 토큰이 «있으면» 형식은 본다 — residency 가 `DEGRADED_*` 에서 하는
1075
+ # 약속과 같은 모양이다. 이게 없으면 pre-grace 마커의 `evidence=PARTIAL(...)` 이 조용히
1076
+ # 통과한다(실측). 부재는 grace 가 면제하지만, **잘못 쓴 것은 면제하지 않는다.**
1077
+ if [ "$_ev_active" -eq 0 ] && [ "$_ev_ntok" -gt 0 ] && [ "$_ev_wellformed" -ne 1 ]; then
1078
+ echo " ❌ FAIL — 'evidence=' token present but malformed (pre-grace marker: the token is"
1079
+ echo " OPTIONAL here, but a token that IS written must be well-formed):"
1080
+ printf '%s\n' "$_ev_tokens" | sed 's/^/ /'
1081
+ echo " Closed set evidence=(SHARED|INDEPENDENT|MIXED)(...) with a closing paren."
1082
+ return 1
1083
+ fi
1084
+ if [ "$_ev_active" -eq 1 ]; then
1085
+ if [ "$_ev_ntok" -gt 1 ]; then
1086
+ echo " ❌ FAIL — grounds carries MORE THAN ONE 'evidence=' token."
1087
+ printf '%s\n' "$_ev_tokens" | sed 's/^/ /'
1088
+ echo " Same trap as two crossfamily: lines — the reader takes the first. Leave one."
1089
+ return 1
1090
+ fi
1091
+ if [ "$_ev_ntok" -eq 0 ]; then
1092
+ echo " ❌ FAIL — panel(...) with no 'evidence=' token in grounds."
1093
+ echo " State WHAT the panel received, because that axis carries ~3.6x the effect of"
1094
+ echo " family diversity (arXiv:2609.10969: shared evidence approves 62.9% of unsafe"
1095
+ echo " proposals vs 22.9% independent; 40.9pp vs 11.3pp). One of:"
1096
+ echo " evidence=SHARED(<what every family read — e.g. the same diff>)"
1097
+ echo " evidence=INDEPENDENT(<what each got separately — own checkout, own run>)"
1098
+ echo " evidence=MIXED(<which member got which>)"
1099
+ echo " e.g. crossfamily: panel($fams) — residency=CLEAN(files=3) · evidence=SHARED(same"
1100
+ echo " staged diff to both) · R1..R2, 4 findings"
1101
+ echo " 🟥 SHARED is a legal, common answer — it is not a failure. Recording it as if"
1102
+ echo " it were INDEPENDENT is the failure this token exists to stop."
1103
+ return 1
1104
+ fi
1105
+ if [ "$_ev_wellformed" -ne 1 ]; then
1106
+ echo " ❌ FAIL — 'evidence=' token present but not one well-formed"
1107
+ echo " evidence=(SHARED|INDEPENDENT|MIXED)(...) token:"
1108
+ printf '%s\n' "$_ev_tokens" | sed 's/^/ /'
1109
+ echo " Closed set with a closing paren — same shape as residency=, same reason: a"
1110
+ echo " malformed token must be malformed, never invisible."
1111
+ return 1
1112
+ fi
1113
+ # Meaning check, and it is deliberately only on SHARED/MIXED: those are the values whose
1114
+ # whole content is "which evidence did they actually share", so an empty body says nothing.
1115
+ # INDEPENDENT is self-describing; over-blocking it would train the override.
1116
+ # 🟥 본문 검사, 두 층 — cross-family agy 가 초판의 두 결함을 다 지목했다:
1117
+ # ⓐ **빈 본문과 자리표시자는 세 값 모두 차단**한다. 초판은 INDEPENDENT 를 통째로 면제해서
1118
+ # `evidence=INDEPENDENT()` 와 `evidence=INDEPENDENT(<what>)` 가 **통과했다**(실측 fail-open).
1119
+ # «자기서술적이라 본문 검사 면제» 는 «본문이 없어도 된다» 와 다른 말이었는데 초판이 접었다.
1120
+ # ⓑ 길이 바는 **문자 수**로 센다. `wc -w` 는 `same-diff` 도 `동일diff` 도 1 낱말로 세서
1121
+ # 정당한 한국어/하이픈 기록을 막았다(실측) — 한국어로 근거를 쓰는 이 레포에서 이건 상습 과차단이다.
1122
+ _ev_strip=$(printf '%s' "$_ev_body" | tr -d '[:space:]')
1123
+ if [ -z "$_ev_strip" ] \
1124
+ || printf '%s' "$_ev_body" | grep -qE '^[[:space:]]*(TBD|tbd|N/?A|n/?a|없음|미정|-+|\?+|<[^>]*>)[[:space:]]*$'; then
1125
+ echo " ❌ FAIL — evidence=$_ev_kind(...) has an EMPTY or placeholder body: '$_ev_body'"
1126
+ echo " All three values need a body — 'self-describing' meant it is not length-checked,"
1127
+ echo " never that it can be blank. Name what each member actually received."
1128
+ return 1
1129
+ fi
1130
+ case "$_ev_kind" in
1131
+ SHARED|MIXED)
1132
+ # 문자 수 기준(공백 제외). 4자 미만이면 «diff» 같은 한 토큰이고, 그건 어느 산출물인지
1133
+ # 말하지 않는다. 🟥 낱말 수로 세지 않는다 — CJK 와 하이픈 복합어가 전부 1 낱말이 된다.
1134
+ if [ "$(printf '%s' "$_ev_strip" | wc -m | tr -d ' ')" -lt 6 ]; then
1135
+ echo " ❌ FAIL — evidence=$_ev_kind(...) body too thin to identify the artifact: '$_ev_body'"
1136
+ echo " Name it (\"same staged diff\", \"one findings.jsonl\", \"동일 프롬프트 파일\")."
1137
+ echo " A bare $_ev_kind(diff) records the weak arm as if it were documented."
1138
+ return 1
1139
+ fi ;;
1140
+ esac
1141
+ fi
1025
1142
  echo " ✅ cross-family leg: $val${reason:+ — $(printf '%s' "$reason" | cut -c1-60)}" ;;
1026
1143
  DEGRADED_SINGLE_FAMILY|DEGRADED_PANEL_UNUSED|UNKNOWN)
1027
1144
  # A degrade is allowed; being silent about WHICH degrade, or about its grounds, is not.
@@ -1129,6 +1246,7 @@ validate_crossfamily_leg() { # $1 = marker path
1129
1246
  # and 0 over-blocks. Anchor: scripts/test_marker_standpoint_lanes.sh
1130
1247
  validate_standpoint_leg() { # $1 = marker path
1131
1248
  local m="$1" line val reason n quoted=""
1249
+ local _sp_mdate _sp_block _sp_grace
1132
1250
  n=$(grep -cE '^[[:space:]]*standpoint:' "$m" 2>/dev/null); n=${n:-0}
1133
1251
  if [ "$n" -eq 0 ]; then
1134
1252
  echo " ❌ FAIL — load-bearing file staged with no 'standpoint:' line in the Axes 2-3 marker."
@@ -1174,13 +1292,41 @@ validate_standpoint_leg() { # $1 = marker path
1174
1292
  tier1b\(*\)|tier2\(*\)|tier2b\(*\)|tier3\(*\))
1175
1293
  case "$val" in
1176
1294
  tier2\(*\)|tier2b\(*\)|tier3\(*\))
1295
+ # 🟥 키워드에 «뒤따르는 공백» 을 박아두면 정당한 기록이 막힌다 — cross-family agy 적발,
1296
+ # 실측 확인: `cargo check` · `python scripts/eval.py` · `ran: ./ci.sh` 가 전부 BLOCK 이었다.
1297
+ # advisory 일 때는 경고 한 줄이었지만 **차단이 된 순간 이건 override 를 훈련시키는 결함**이다.
1298
+ # ⇒ ⓐ 경계를 «비-알파벳» 으로 바꾸고(콜론·세미콜론·줄끝 허용) ⓑ 흔한 러너를 넣고
1299
+ # ⓒ «출력» 의 형태(`30/30`, `rc=`, `./path`)도 실행의 증거로 받는다.
1300
+ # ⚠️ 방향: 이건 **느슨하게** 만드는 변경이다. 막고 싶은 것은 «명령도 출력도 안 적은 줄» 이고,
1301
+ # 길이 20 자 하한은 그대로 남는다.
1177
1302
  if [ "${#reason}" -lt 20 ] || ! printf '%s' "$reason" \
1178
- | grep -qiE 'ran |run |실행|돌렸|호출|invok|call|rc=|exit |output|출력|suite|스위트|test|bash |grep |npm |pytest|command|커맨드|arm'; then
1179
- echo " ⚠️ standpoint leg: $val recorded, but the grounds do not NAME an execution."
1180
- echo " ${val%%(*} asserts code RAN in the target. The discriminator is mechanical:"
1181
- echo " name the command you ran and the output you saw. If you only read files,"
1182
- echo " the honest rung is tier1b — weaker on purpose, so the owed arm stays visible."
1183
- echo " Advisory by design (see the named residual above this function)."
1303
+ | grep -qiE '(^|[^[:alpha:]])(ran|run|runs|exec|invoke[ds]?|call|calls|called|exit|output|suite|test|tests|tested|bash|sh|zsh|grep|npm|pnpm|yarn|pytest|python|python3|cargo|make|go|gradle|mvn|dotnet|node|command)([^[:alpha:]]|$)|실행|돌렸|돌린|돌려|호출|출력|스위트|커맨드|rc=|[0-9]+/[0-9]+|\./'; then
1304
+ # Grace: markers dated before STANDPOINT_GROUNDS_GRACE_DATE keep the old advisory
1305
+ # behaviour (no retro-blocking), same pattern as the residency=/evidence= tokens.
1306
+ _sp_mdate=$(printf '%s' "$m" | sed -nE 's/.*_([0-9]{4}-[0-9]{2}-[0-9]{2})\.marker$/\1/p')
1307
+ _sp_block=1
1308
+ # Unreadable date NOT exempt. The hook stamps TODAY into every marker filename it
1309
+ # creates, so an unparseable date is an unexpected shape, not evidence of age.
1310
+ # 🟥 상수를 «먼저» 평가한다 — 초판은 `[ -n "$_sp_mdate" ] && [ ... ${VAR:?} ]` 였고,
1311
+ # mdate 가 비면 단축평가로 `:?` 가드가 **아예 안 돌았다**(agy 적발). 가드가 있다고
1312
+ # 적어놓고 안 도는 것은 장식이다([[feedback_anchor_can_be_decorative]]).
1313
+ _sp_grace="${STANDPOINT_GROUNDS_GRACE_DATE:?STANDPOINT_GROUNDS_GRACE_DATE unset — partial extraction of this hook? refusing to guess a grace date}"
1314
+ if [ -n "$_sp_mdate" ] && [ "$_sp_mdate" \< "$_sp_grace" ]; then _sp_block=0; fi
1315
+ if [ "$_sp_block" -eq 1 ]; then
1316
+ echo " ❌ FAIL — standpoint leg: $val — the grounds do not NAME an execution."
1317
+ echo " ${val%%(*} asserts code RAN in the target. The discriminator is mechanical:"
1318
+ echo " name the command you ran and the output you saw. If you only read files,"
1319
+ echo " the honest rung is tier1b — weaker on purpose, so the owed arm stays visible."
1320
+ echo " standpoint: tier2(<harness>) — ran \`bash scripts/x.sh\` there, output: 30/30 PASS"
1321
+ echo " 🟥 BLOCKING since 2026-09-12 (was advisory). Why it changed:"
1322
+ echo " arXiv:2609.10969 measured evidence-source diversity at 40.9pp against 11.3pp"
1323
+ echo " for model diversity (n=2,880, fixed call budget) — this is the STRONGER axis,"
1324
+ echo " and leaving its grounds advisory while hard-blocking the weaker one was not a"
1325
+ echo " balance. Honest scope: this blocks on the RECORD's shape, never on whether the"
1326
+ echo " run was real."
1327
+ return 1
1328
+ fi
1329
+ echo " ⚠️ standpoint leg: $val — grounds name no execution (pre-$STANDPOINT_GROUNDS_GRACE_DATE marker, advisory)."
1184
1330
  else
1185
1331
  echo " ✅ standpoint leg: $val$quoted"
1186
1332
  fi ;;
@@ -11,7 +11,7 @@ description: forge-harness path and skill list pointer — local only, do not co
11
11
  **forge-harness path**: `~/path/to/forge-harness` (replace with your actual install path)
12
12
  **Session records**: `{FH_ROOT}/tracks/_meta/`
13
13
 
14
- **Available skills (fh-meta, 35)**: agent-composer · dialogue-harvest · fh · apex-review · asset-placement-gate · auto-decorrelation · context-doctor · contention-layer · corpus-grounding-expander · cross-ecosystem-synergy-detection · deep-clarify · edit-manifest · field-harvest · frontier-digest · goal-quench · harness-doctor · harvest-loop · hub-cc-pr-reviewer · install-doctor · install-wizard · marketplace-gate · memory-hygiene · meta-prompt-builder · persona-roster-expander · phantom-quench · pipeline-conductor · plugin-recommender · prompt-regression · public-surface-audit · return-path-gate · sim-conductor · salience-splitter · steel-quench · verify-bidirectional · video-ingest
14
+ **Available skills (fh-meta, 35)**: agent-composer · dialogue-harvest · fh · apex-review · asset-placement-gate · auto-decorrelation · context-doctor · contention-layer · corpus-grounding-expander · cross-ecosystem-synergy-detection · deep-clarify · edit-manifest · field-harvest · frontier-digest · goal-quench · harness-doctor · harvest-loop · harness-pr-reviewer · install-doctor · install-wizard · marketplace-gate · memory-hygiene · meta-prompt-builder · persona-roster-expander · phantom-quench · pipeline-conductor · plugin-recommender · prompt-regression · public-surface-audit · return-path-gate · sim-conductor · salience-splitter · steel-quench · verify-bidirectional · video-ingest
15
15
 
16
16
  **Available skills (fh-commons)**: convergence-loop · deliberation · mcp-circuit-breaker · token-budget-gate
17
17
 
@@ -15,7 +15,7 @@
15
15
  # 구분하려면 stdout 의 `REGRESSION_GUARD_RESULT=skip` 을 보라. 종료코드만 보는 호출자는
16
16
  # 미검사를 통과로 읽는다(2026-07-22: pre-commit 이 정확히 그랬고, AGENTS.md 변경이
17
17
  # 그 경로로 '✅ PASS' 를 받고 지나갔다).
18
- # 배선 현황: pre-commit ✅ / harness-doctor · harvest-loop · hub-cc-pr-reviewer ·
18
+ # 배선 현황: pre-commit ✅ / harness-doctor · harvest-loop · harness-pr-reviewer ·
19
19
  # self_evolution_routine = **미배선(종료코드만 판정)** — 알려진 잔여.
20
20
  #
21
21
  # PR mode rationale: using 'main' as BASE_REF for a PR branch includes changes from OTHER