@chrono-meta/fh-gate 3.1.4 → 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.
- package/.claude/registry/agent_cards.json +1 -1
- package/.claude/rules/fh_4axis_gate.md +26 -1
- package/.claude-plugin/marketplace.json +3 -3
- package/AGENTS.md +8 -2
- package/CATALOG.md +4 -4
- package/CHEATSHEET.md +1 -1
- package/CLAUDE.md +54 -10
- package/README.ja.md +3 -3
- package/README.ko.md +3 -3
- package/README.md +3 -3
- package/README.zh.md +3 -3
- package/docs/OUTPUT_EVIDENCE.md +2 -2
- package/docs/STANDARDS_ALIGNMENT.md +1 -1
- package/docs/codex-compat.md +1 -1
- package/knowledge/shared/harness-core/agents_md_runtime_details.md +2 -2
- package/knowledge/shared/harness-core/claude_md_gate_details.md +26 -0
- package/knowledge/shared/harness-core/fh_three_layer_canon.md +1 -1
- package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +76 -3
- package/knowledge/shared/harness-core/governance_engineering_definition.md +89 -0
- package/knowledge/shared/harness-core/iso_ai_standards_crosswalk.md +1 -1
- package/knowledge/shared/harness-core/skill_quality_rubric.md +1 -1
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +99 -0
- package/knowledge/shared/rules/auto_project_mapping.md +1 -1
- package/knowledge/shared/rules/modes_and_value.md +2 -2
- package/package.json +12 -1
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-commons/README.md +38 -0
- package/plugins/fh-commons/skills/preprep/SKILL.md +23 -0
- package/plugins/fh-commons/skills/preprep/fixtures/font_revert_probe.py +92 -0
- package/plugins/fh-commons/skills/preprep/lane_font.py +462 -0
- package/plugins/fh-commons/skills/preprep/preprep.py +16 -1
- package/plugins/fh-commons/skills/preprep/surfaces.example.yaml +17 -0
- package/plugins/fh-commons/skills/preprep/test_lane_font.py +452 -0
- package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/CHANGELOG.md +164 -0
- package/plugins/fh-meta/skills/agent-composer/SKILL.md +2 -2
- package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +40 -0
- package/plugins/fh-meta/skills/frontier-digest/SKILL.md +1 -1
- package/plugins/fh-meta/skills/frontier-digest/SKILL_detail.md +43 -5
- package/plugins/fh-meta/skills/{hub-cc-pr-reviewer → harness-pr-reviewer}/SKILL.md +75 -3
- package/plugins/fh-meta/skills/{hub-cc-pr-reviewer → harness-pr-reviewer}/SKILL_detail.md +2 -2
- package/plugins/fh-meta/skills/harvest-loop/SKILL_detail.md +2 -2
- package/plugins/fh-meta/skills/install-doctor/SKILL.md +1 -1
- package/plugins/fh-meta/skills/install-wizard/SKILL.md +1 -1
- package/plugins/fh-meta/skills/meta-prompt-builder/SKILL.md +1 -1
- package/plugins/fh-meta/skills/pipeline-conductor/SKILL.md +1 -1
- package/plugins/fh-meta/skills/plugin-recommender/SKILL.md +1 -1
- package/plugins/fh-meta/skills/sim-conductor/SKILL.md +1 -1
- package/plugins/fh-qp/.claude-plugin/plugin.json +1 -1
- package/scripts/doc_claim_triad_scan.py +303 -0
- package/scripts/finding_fleet.sh +558 -0
- package/scripts/finding_pipeline.sh +572 -0
- package/scripts/finding_verifier.sh +173 -0
- package/scripts/finding_verify.py +468 -0
- package/scripts/frontier_digest_autopilot.sh +3 -3
- package/scripts/gate_pathspec_check.sh +1 -0
- package/scripts/gate_shape_scan.sh +120 -0
- package/scripts/selfcheck.sh +50 -0
- package/scripts/test_doc_claim_triad_lanes.sh +124 -0
- package/scripts/test_finding_pipeline_lanes.sh +2011 -0
- package/scripts/test_gate_shape_scan_lanes.sh +47 -0
- package/scripts/test_heavy_classifier_lanes.sh +13 -3
- package/scripts/test_marker_crossfamily_lanes.sh +75 -3
- package/scripts/test_marker_standpoint_lanes.sh +31 -6
- package/scripts/test_preprep_font_lanes.sh +87 -0
- package/templates/.git-hooks/pre-commit +158 -11
- package/templates/PRE-PUBLISH-CHECKLIST.md +29 -0
- package/templates/local_fh_context.md +1 -1
- package/templates/regression_guard.sh +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_gate_shape_scan_lanes.sh — scripts/gate_shape_scan.sh 회귀 앵커.
|
|
3
|
+
# 지키는 것은 «판별력»: known-pair 가 갈리는가, 주석만 있는 파일이 안 걸리는가, 바이너리가
|
|
4
|
+
# «아님» 으로 접히지 않는가, Promise `reject(` 가 게이트로 읽히지 않는가(실측 오탐).
|
|
5
|
+
set -uo pipefail
|
|
6
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"; SCAN="$HERE/gate_shape_scan.sh"
|
|
7
|
+
PASS=0; FAIL=0
|
|
8
|
+
ok(){ PASS=$((PASS+1)); printf ' ✅ %s\n' "$1"; }
|
|
9
|
+
no(){ FAIL=$((FAIL+1)); printf ' ❌ %s — %s\n' "$1" "${2:-}"; }
|
|
10
|
+
echo "── test_gate_shape_scan_lanes ──"
|
|
11
|
+
[ -x "$SCAN" ] || { no "L1 실행비트" "$SCAN"; echo "FAILED=1"; exit 1; }
|
|
12
|
+
bash -n "$SCAN" && ok "L1 구문" || no "L1 구문"
|
|
13
|
+
OUT=$(bash "$HERE/gate_shape_scan.sh" --selftest 2>&1); RC=$? # 직접 호출(변수 경유 아님) — new-code-anchor 가 실행으로 센다
|
|
14
|
+
[ "$RC" -eq 0 ] && ok "L2 selftest rc=0 (known-pair 5)" || no "L2 selftest" "rc=$RC · $OUT"
|
|
15
|
+
D="$(mktemp -d 2>/dev/null)" || D=""; [ -n "$D" ] && [ -w "$D" ] || { no "L3 환경" "mktemp -d 불가 — 레인 미측정(통과 아님)"; echo "PASS=$PASS FAIL=$FAIL"; echo "FAILED=1"; exit 1; }
|
|
16
|
+
printf 'export function up(p) {\n return new Promise((resolve, reject) => {\n if (!p) reject(new Error("x"));\n resolve(p);\n });\n}\n' > "$D/promise.ts"
|
|
17
|
+
bash "$SCAN" "$D/promise.ts" >/dev/null 2>&1; RC=$?
|
|
18
|
+
[ "$RC" -eq 1 ] && ok "L3 Promise reject( 는 게이트가 아니다 (실측 오탐 앵커)" || no "L3 Promise reject" "rc=$RC"
|
|
19
|
+
printf 'func main() {\n http.ListenAndServe("0.0.0.0:8080", nil)\n}\n' > "$D/serve.go"
|
|
20
|
+
bash "$SCAN" "$D/serve.go" >/dev/null 2>&1; RC=$?
|
|
21
|
+
[ "$RC" -eq 0 ] && ok "L4 Go ListenAndServe → GATE-SHAPED" || no "L4 Go exposure" "rc=$RC"
|
|
22
|
+
printf 'def f():\n return "PASS"\n' > "$D/enum.py"; printf 'def g():\n pass\n' > "$D/lower.py"
|
|
23
|
+
bash "$SCAN" "$D/enum.py" >/dev/null 2>&1; R1=$?; bash "$SCAN" "$D/lower.py" >/dev/null 2>&1; R2=$?
|
|
24
|
+
[ "$R1" -eq 0 ] && [ "$R2" -eq 1 ] && ok "L5 대문자 PASS 만 enum (소문자 pass 는 키워드)" || no "L5 enum case" "PASS rc=$R1 pass rc=$R2"
|
|
25
|
+
bash "$SCAN" >/dev/null 2>&1; RC=$?; [ "$RC" -eq 3 ] && ok "L6 인자 없음 → usage rc=3" || no "L6 usage" "rc=$RC"
|
|
26
|
+
printf '\x00\x01bin' > "$D/b.dat"; bash "$SCAN" "$D/serve.go" "$D/b.dat" >/dev/null 2>&1; RC=$?
|
|
27
|
+
[ "$RC" -eq 3 ] && ok "L6b [gate-shaped, binary] → 3 이 0 을 이긴다 (무음 미스 금지)" || no "L6b 혼합 exit" "rc=$RC"
|
|
28
|
+
printf 'x = author\n' > "$D/au.py"; printf 'ok = authorize(u)\n' > "$D/az.py"
|
|
29
|
+
bash "$SCAN" "$D/au.py" >/dev/null 2>&1; R1=$?; bash "$SCAN" "$D/az.py" >/dev/null 2>&1; R2=$?
|
|
30
|
+
[ "$R1" -eq 1 ] && [ "$R2" -eq 0 ] && ok "L6c 경계: author 는 아니고 authorize 는 맞다" || no "L6c auth 경계" "author rc=$R1 authorize rc=$R2"
|
|
31
|
+
# L7 — 되돌림: 분류기의 EXPOSURE 클래스를 죽이면 L4 가 빨개지는가 (앵커가 장식이 아님을 실행으로)
|
|
32
|
+
MUT="$D/scan_mut.sh"; /usr/bin/sed 's/^EXPOSURE_RE=.*/EXPOSURE_RE='"'"'(NEVER_MATCHES_XYZ)'"'"'/' "$SCAN" > "$MUT"
|
|
33
|
+
bash "$MUT" "$D/serve.go" >/dev/null 2>&1; RC=$?
|
|
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
|
+
|
|
46
|
+
/bin/rm -rf "$D"
|
|
47
|
+
echo "PASS=$PASS FAIL=$FAIL"; [ "$FAIL" -eq 0 ] && { echo "FAILED=0"; exit 0; } || { echo "FAILED=1"; exit 1; }
|
|
@@ -7,8 +7,14 @@
|
|
|
7
7
|
# louder symptom. Nothing was watching the watcher: measured 2026-08-20, `scripts/test_*.sh` files
|
|
8
8
|
# testing this classifier = **0**, while 12 test files touch the hook for other reasons.
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
10
|
+
# Provenance arc (sibling harness pmh-dev → FH, recorded both ways per the shared-layer canon rule):
|
|
11
|
+
# this lane started TWO-WAY in pmh-dev (2026-08-08), was absorbed into FH, and FH extended the
|
|
12
|
+
# classifier to FOUR-WAY (HEAVY · CARVEOUT · LIGHT · uncovered). The load-bearing idea is the
|
|
13
|
+
# extraction below, not the fixture list. pmh-dev's own «#32 seam» rows were NOT re-imported
|
|
14
|
+
# (2026-09-08, sibling-harvest review): each maps onto a class already pinned here — seam 1 (scripts/*.sh)
|
|
15
|
+
# → `scripts/selfcheck.sh` · seam 2 (SKILL_detail.md) → goal-quench row · seam 3 (agent .md) →
|
|
16
|
+
# challenger/local rows · seam 4 (sibling md in a skill dir) → `assets/note.md` row. Re-pinning the
|
|
17
|
+
# same class under a sibling's issue number would be a decorative row, not a new anchor.
|
|
12
18
|
#
|
|
13
19
|
# 🟥 FH's classifier is FOUR-WAY, not two-way like the sibling's. The port is not a copy:
|
|
14
20
|
# HEAVY → full gate
|
|
@@ -105,13 +111,17 @@ check carveout "README.zh-CN.md" "🟥 5-char locale forms too — c
|
|
|
105
111
|
check uncovered ".claude/registry/README.md" "🟥 STILL UNCOVERED — in files[] but not root; named, not closed"
|
|
106
112
|
check uncovered "package.json" "files[] / version changes are unclassified" # portability-noqa: "package.json" is a string literal fed to classify()'s regex test, never opened from disk — a ported repo without this exact file still exercises the same classifier logic
|
|
107
113
|
check uncovered ".github/workflows/validate.yml" "🟥 CI definition — changing what CI runs is unclassified"
|
|
108
|
-
check
|
|
114
|
+
check heavy "scripts/memory_link_check.py" "🟢 .py joined .sh on 2026-09-07 — was pinned «uncovered» here (7 shipped python files outside the gate)"
|
|
115
|
+
check heavy "scripts/doc_claim_triad_scan.py" ".py — the file whose gate-less commit exposed the blind spot"
|
|
116
|
+
check heavy "scripts/adapters/peer_resolve.py" ".py in a subdirectory — the regex is ^scripts/.*, not ^scripts/[^/]*"
|
|
109
117
|
|
|
110
118
|
echo " ── controls (the classifier discriminates, it does not just fire) ──"
|
|
111
119
|
# known-negative: a path that resembles a HEAVY one but must not match. This is the arm that
|
|
112
120
|
# separates "the classifier works" from "the classifier fires on everything" — without it, a
|
|
113
121
|
# regex of `.` would pass every HEAVY row above.
|
|
114
122
|
check uncovered "notes/scripts/foo.sh.txt" "control — .sh.txt is not a shell script (\$-anchored)"
|
|
123
|
+
check uncovered "scripts/notes.txt" "control — a non-executable under scripts/ must NOT become heavy with the .py widening"
|
|
124
|
+
check uncovered "scripts/data.pyc" "control — .pyc is not .py (\$-anchored, alternation closed)"
|
|
115
125
|
check uncovered "notes/skills-overview.md" "control — 'skills' in a path is not a skill spec"
|
|
116
126
|
# ⚠️ this control was first written as `docs/design/skills-overview.md`, which the lane
|
|
117
127
|
# correctly classified `carveout` (`^docs/.*\.md$`). The FIXTURE was wrong, not the
|
|
@@ -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"
|
|
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/
|
|
49
|
-
out=$( bash -c
|
|
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 "==
|
|
80
|
-
|
|
87
|
+
echo "== tier2+ grounds — BLOCKING 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
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_preprep_font_lanes.sh — preprep L15(서체 일관성)의 계약을 고정한다.
|
|
3
|
+
#
|
|
4
|
+
# WHY: 발표 템플릿을 주는 조직은 거의 항상 서체를 규정하는데, 서체 이탈은 **렌더링에서만**
|
|
5
|
+
# 드러나 사람이 눈으로 훑기 전에는 안 보인다. preprep 의 이웃 레인들은 배치(P1/P3)와 말의
|
|
6
|
+
# 정합(L1·L5·L8·R1~R5)을 보지, 「무슨 서체로 찍혔나」는 어느 레인도 안 봤다.
|
|
7
|
+
#
|
|
8
|
+
# 🟥 이 레인이 증명해야 하는 것은 «이탈을 잡는다»가 아니라 **«상속분을 안 잡는다»** 다.
|
|
9
|
+
# 실측(2026-09-11, 조직 배포 템플릿 기반 발표 덱 120장): 순진하게 세면 `Helvetica Neue`
|
|
10
|
+
# **85건**이 «위반»으로 나오는데, 그 85건은 전부 `slideMasters/` 안에 있고 **배포 템플릿의
|
|
11
|
+
# 마스터에도 정확히 같은 85건**이 있다(해시 동일). 저자는 그 글자를 찍은 적이 없다.
|
|
12
|
+
# 진짜 이탈은 하나뿐이었고 그건 의도였다(코드 데모의 고정폭 서체).
|
|
13
|
+
# ⇒ known-negative 를 «서체 하나뿐인 덱»으로 두면 그 오탐을 **구조적으로 못 잡는다.**
|
|
14
|
+
# 그래서 K1 은 «마스터에 이질 서체가 있지만 템플릿과 동일한 덱» 이다.
|
|
15
|
+
#
|
|
16
|
+
# 종료코드: 0 pass · 1 레인 실패 · 2 대상 부재 · 10 setup 실패
|
|
17
|
+
set -uo pipefail
|
|
18
|
+
cd "$(dirname "$0")/.." || exit 10
|
|
19
|
+
LANE=plugins/fh-commons/skills/preprep/lane_font.py
|
|
20
|
+
SELFTEST=plugins/fh-commons/skills/preprep/test_lane_font.py
|
|
21
|
+
PROBE=plugins/fh-commons/skills/preprep/fixtures/font_revert_probe.py
|
|
22
|
+
[ -f "$LANE" ] || { echo "ⓘ $LANE absent — subject missing (NOT a pass)"; exit 2; }
|
|
23
|
+
[ -f "$SELFTEST" ] || { echo "ⓘ $SELFTEST absent — subject missing (NOT a pass)"; exit 2; }
|
|
24
|
+
[ -f "$PROBE" ] || { echo "ⓘ $PROBE absent — subject missing (NOT a pass)"; exit 2; }
|
|
25
|
+
command -v python3 >/dev/null 2>&1 || { echo "ⓘ python3 absent — setup broke"; exit 10; }
|
|
26
|
+
# 🟥 스테일 바이트코드가 뮤턴트/복원을 가린다 — 이웃 레인이 실측한 사고다(2026-09-06).
|
|
27
|
+
rm -rf plugins/fh-commons/skills/preprep/__pycache__
|
|
28
|
+
PASS=0; FAIL=0
|
|
29
|
+
ok(){ echo " ✅ $1"; PASS=$((PASS+1)); }
|
|
30
|
+
ng(){ echo " ❌ $1"; FAIL=$((FAIL+1)); }
|
|
31
|
+
|
|
32
|
+
echo "== K1~K13 known-pair (레인 자체 self-test) =="
|
|
33
|
+
OUT=$(python3 "$SELFTEST" 2>&1); RC=$?
|
|
34
|
+
echo "$OUT" | sed 's/^/ /'
|
|
35
|
+
if [ "$RC" -eq 0 ] && echo "$OUT" | grep -q 'FAIL 0'; then
|
|
36
|
+
ok "self-test 전항 통과 (rc=$RC)"
|
|
37
|
+
elif [ "$RC" -eq 2 ]; then
|
|
38
|
+
echo " ⏭ 픽스처 생성 불가 — UNMEASURED (통과 아님)"; FAIL=$((FAIL+1))
|
|
39
|
+
else
|
|
40
|
+
ng "self-test 실패 (rc=$RC)"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
echo "== 배선·퇴화 — self-test 밖에서 한 번 더 =="
|
|
44
|
+
OUT=$(python3 -c "
|
|
45
|
+
import sys; sys.path.insert(0,'plugins/fh-commons/skills/preprep')
|
|
46
|
+
import lane_font as L
|
|
47
|
+
print(L.scan({}, '.')[1][0])
|
|
48
|
+
print(L.scan({'surfaces_by_id':{'built_deck':{'path':'/nope/none.pptx'}}}, '.')[1][0])
|
|
49
|
+
" 2>&1)
|
|
50
|
+
echo "$OUT" | grep -q 'NOT_CONFIGURED' && echo "$OUT" | grep -q 'UNMEASURED' \
|
|
51
|
+
&& ok "미선언 → NOT_CONFIGURED · 실물 없음 → UNMEASURED (둘 다 0 아님)" \
|
|
52
|
+
|| ng "퇴화 표기가 0 으로 접힌다: $OUT"
|
|
53
|
+
|
|
54
|
+
grep -q "import lane_font" plugins/fh-commons/skills/preprep/preprep.py \
|
|
55
|
+
&& ok "배선: preprep.py 가 이 레인을 부른다" \
|
|
56
|
+
|| ng "배선 없음 — 정의만 있고 아무도 안 부른다(built-but-not-wired)"
|
|
57
|
+
|
|
58
|
+
grep -q "lane_font" plugins/fh-commons/skills/preprep/SKILL.md \
|
|
59
|
+
&& ok "명세: SKILL.md 가 이 레인을 적는다(고아 구현 아님)" \
|
|
60
|
+
|| ng "SKILL.md 에 없다 — 명세 없는 구현"
|
|
61
|
+
|
|
62
|
+
# 🟥 이 프로브가 «template 미선언» 조건에서 도는 이유는 프로브 파일 머리에 적혀 있다. 요약:
|
|
63
|
+
# 한때 방어가 둘이었고(영역 분리 · 템플릿-자체-서체 강등) 해시 동일 부품에서는 ②가 ①을
|
|
64
|
+
# 완전히 덮어, 둘 다 켠 채 ①만 죽이면 «분리가 장식»이라는 **거짓 판정**이 났다.
|
|
65
|
+
# 그 뒤 ②는 cross-family 2라운드에서 **fail-open 으로 판정돼 걷혔다**(억제 → 맥락 주석).
|
|
66
|
+
# 지금 방어는 ① 하나뿐이고, 이 조건이 그 하나를 정확히 겨눈다.
|
|
67
|
+
echo "== 되돌림 — 영역 분리를 죽이면 오탐이 돌아오는가 (template 미선언 = 방어 하나) =="
|
|
68
|
+
OUT=$(python3 "$PROBE" separation 2>&1)
|
|
69
|
+
if echo "$OUT" | grep -q 'BASE 0' && echo "$OUT" | grep -qE 'MUT [1-9]'; then
|
|
70
|
+
ok "되돌림: 분리를 죽이면 마스터 상속 서체가 오탐으로 돌아온다 ($OUT) — 분리가 하중을 진다"
|
|
71
|
+
else
|
|
72
|
+
ng "되돌림 실패 — 분리를 죽여도 결과가 같다(그 분기가 장식이다): $OUT"
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# 🟥 «템플릿도 쓰는 이름» 을 노트로 **억제**하면 fail-open 이다 — 2라운드가 그렇게 지목해서
|
|
76
|
+
# 억제를 걷어내고 맥락 주석만 남겼다. 이 레인이 그 되돌림을 고정한다.
|
|
77
|
+
echo "== 템플릿도 쓰는 이름이 억제되지 않고 finding 으로 남는가 =="
|
|
78
|
+
OUT=$(python3 "$PROBE" annotation 2>&1)
|
|
79
|
+
if echo "$OUT" | grep -qE 'FINDING [1-9]' && echo "$OUT" | grep -qE 'ANNOTATED [1-9]'; then
|
|
80
|
+
ok "억제 아님: finding 은 남고 맥락 주석만 붙는다 ($OUT)"
|
|
81
|
+
else
|
|
82
|
+
ng "억제로 되돌아갔다 — 저자가 그 이름으로 본문을 찍어도 종료코드가 안 움직인다: $OUT"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
echo "── preprep font lanes: PASS=$PASS FAIL=$FAIL"
|
|
86
|
+
[ "$FAIL" -eq 0 ] || exit 1
|
|
87
|
+
exit 0
|
|
@@ -213,10 +213,11 @@ fi
|
|
|
213
213
|
# dlp-filter.sh, …) — the gate's OWN tooling. Leaving it ungated was a gate-locality
|
|
214
214
|
# seam: the engine that enforces the 4-axis could itself ship unverified. Gated broadly,
|
|
215
215
|
# not by an allowlist, so a NEW gate script cannot silently slip in ungated (the drift
|
|
216
|
-
# that would re-open the seam). Scope is `scripts
|
|
217
|
-
#
|
|
218
|
-
#
|
|
219
|
-
#
|
|
216
|
+
# that would re-open the seam). Scope is `scripts/**/*.{sh,py}` (2026-09-07: `.py` joined
|
|
217
|
+
# — a 252-line python instrument landed with no marker while 7 shipped python files sat
|
|
218
|
+
# outside this term the whole time; the heavy-classifier lane had it PINNED as `uncovered`).
|
|
219
|
+
# A governance script with yet another extension or placed elsewhere (bin/) is still NOT
|
|
220
|
+
# auto-caught and must be hand-gated — broaden this term if that changes. Axis 1 (regression_guard) skips non-markdown, so the live effect on a
|
|
220
221
|
# script is Axes 2-3 (adversarial design + phantom), NOT behavioral regression — that is
|
|
221
222
|
# covered downstream (selfcheck.sh / count_check.sh at publish), not by this commit gate.
|
|
222
223
|
# seam #3 (2026-06-27): agent definitions (plugins/*/agents/*.md, .claude/agents/*.md) are
|
|
@@ -242,7 +243,7 @@ fi
|
|
|
242
243
|
# «no FH asset staged» 로 빠져 4축 게이트를 통째로 건너뛰었다. tenet 을 바꾸는 것은
|
|
243
244
|
# 이 레포에서 가장 하중 큰 편집 중 하나다 — LIGHT 는커녕 CARVEOUT 도 아니다.
|
|
244
245
|
HEAVY=$(echo "$STAGED" \
|
|
245
|
-
| grep -E "(SKILL(_detail)?\.md|^plugins/[^/]+/skills/[^/]+/.*\.md$|\.claude/rules/|^knowledge/shared/rules/|templates/|CLAUDE\.md|^scripts/.*\.sh$|^plugins/[^/]+/agents/.*\.md$|^\.claude/agents/.*\.md$|^\.claude/soul_tenets\.txt$)" || true)
|
|
246
|
+
| grep -E "(SKILL(_detail)?\.md|^plugins/[^/]+/skills/[^/]+/.*\.md$|\.claude/rules/|^knowledge/shared/rules/|templates/|CLAUDE\.md|^scripts/.*\.(sh|py)$|^plugins/[^/]+/agents/.*\.md$|^\.claude/agents/.*\.md$|^\.claude/soul_tenets\.txt$)" || true)
|
|
246
247
|
|
|
247
248
|
# Carve-out candidates: knowledge/ docs, docs/*.md, AGENTS.md. Heavy only if substantive.
|
|
248
249
|
CARVEOUT=$(echo "$STAGED" \
|
|
@@ -804,8 +805,25 @@ validate_defense_leg() { # $1 = marker path — fires ONLY at the floor tiers (s
|
|
|
804
805
|
# would block every in-flight branch and teach `--no-verify`, which disarms the Destructive-Op
|
|
805
806
|
# gate sharing this hook.
|
|
806
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"
|
|
807
824
|
validate_crossfamily_leg() { # $1 = marker path
|
|
808
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
|
|
809
827
|
if [ ! -f "$m" ] || ! grep -qE '^[[:space:]]*crossfamily:[[:space:]]*[^[:space:]]' "$m"; then
|
|
810
828
|
echo " ❌ FAIL — load-bearing file staged with no 'crossfamily:' line in the Axes 2-3 marker."
|
|
811
829
|
echo " Verdict/gate/irreversible-surface code shares the author's blind spot with a"
|
|
@@ -868,6 +886,31 @@ validate_crossfamily_leg() { # $1 = marker path
|
|
|
868
886
|
_res_kind=""
|
|
869
887
|
[ "$_res_wellformed" -eq 1 ] && _res_kind=$(printf '%s' "$_res_full" | sed -E 's/^residency=([A-Za-z_]*)\(.*$/\1/')
|
|
870
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
|
+
|
|
871
914
|
case "$val" in
|
|
872
915
|
declined)
|
|
873
916
|
# `declined` means ONE thing: the OPERATOR declined sidecars (UAP), a chosen floor. It was
|
|
@@ -1021,6 +1064,81 @@ validate_crossfamily_leg() { # $1 = marker path
|
|
|
1021
1064
|
return 1
|
|
1022
1065
|
fi
|
|
1023
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
|
|
1024
1142
|
echo " ✅ cross-family leg: $val${reason:+ — $(printf '%s' "$reason" | cut -c1-60)}" ;;
|
|
1025
1143
|
DEGRADED_SINGLE_FAMILY|DEGRADED_PANEL_UNUSED|UNKNOWN)
|
|
1026
1144
|
# A degrade is allowed; being silent about WHICH degrade, or about its grounds, is not.
|
|
@@ -1128,6 +1246,7 @@ validate_crossfamily_leg() { # $1 = marker path
|
|
|
1128
1246
|
# and 0 over-blocks. Anchor: scripts/test_marker_standpoint_lanes.sh
|
|
1129
1247
|
validate_standpoint_leg() { # $1 = marker path
|
|
1130
1248
|
local m="$1" line val reason n quoted=""
|
|
1249
|
+
local _sp_mdate _sp_block _sp_grace
|
|
1131
1250
|
n=$(grep -cE '^[[:space:]]*standpoint:' "$m" 2>/dev/null); n=${n:-0}
|
|
1132
1251
|
if [ "$n" -eq 0 ]; then
|
|
1133
1252
|
echo " ❌ FAIL — load-bearing file staged with no 'standpoint:' line in the Axes 2-3 marker."
|
|
@@ -1173,13 +1292,41 @@ validate_standpoint_leg() { # $1 = marker path
|
|
|
1173
1292
|
tier1b\(*\)|tier2\(*\)|tier2b\(*\)|tier3\(*\))
|
|
1174
1293
|
case "$val" in
|
|
1175
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 자 하한은 그대로 남는다.
|
|
1176
1302
|
if [ "${#reason}" -lt 20 ] || ! printf '%s' "$reason" \
|
|
1177
|
-
| grep -qiE 'ran
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
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)."
|
|
1183
1330
|
else
|
|
1184
1331
|
echo " ✅ standpoint leg: $val$quoted"
|
|
1185
1332
|
fi ;;
|