@chrono-meta/fh-gate 3.2.0 → 3.5.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 +25 -0
- package/.claude-plugin/marketplace.json +9 -3
- package/AGENTS.md +2 -2
- package/CATALOG.md +4 -4
- package/CHEATSHEET.md +1 -1
- package/CLAUDE.md +4 -4
- package/README.md +2 -2
- package/docs/OUTPUT_EVIDENCE.md +1 -1
- package/docs/STANDARDS_ALIGNMENT.md +1 -1
- package/docs/codex-compat.md +2 -2
- package/knowledge/shared/harness-core/agents_md_runtime_details.md +2 -2
- package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +30 -3
- 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 +120 -4
- package/knowledge/shared/rules/modes_and_value.md +2 -2
- package/package.json +9 -1
- package/plugins/fh-commons/.claude-plugin/plugin.json +2 -2
- package/plugins/fh-commons/README.md +38 -0
- package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/CHANGELOG.md +117 -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-preprep/.claude-plugin/plugin.json +22 -0
- package/plugins/fh-preprep/README.md +24 -0
- package/plugins/{fh-commons → fh-preprep}/skills/preprep/SKILL.md +75 -3
- package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/mk_slide_fixtures.py +152 -1
- package/plugins/fh-preprep/skills/preprep/lane_attr_consistency.py +370 -0
- package/plugins/fh-preprep/skills/preprep/lane_geometry.py +339 -0
- package/plugins/fh-preprep/skills/preprep/lane_screen_parity.py +232 -0
- package/plugins/fh-preprep/skills/preprep/oox.py +305 -0
- package/plugins/{fh-commons → fh-preprep}/skills/preprep/preprep.py +52 -4
- package/plugins/fh-preprep/skills/preprep/safe_install.py +369 -0
- package/plugins/{fh-commons → fh-preprep}/skills/preprep/surfaces.example.yaml +29 -0
- package/plugins/fh-preprep/skills/preprep/test_preprep_lanes_rp.py +1417 -0
- package/plugins/fh-preprep/skills/preprep/usage_ledger.py +191 -0
- package/plugins/fh-qp/.claude-plugin/plugin.json +1 -1
- package/scripts/fh-run.sh +13 -0
- package/scripts/field_canon_preload.sh +1 -1
- package/scripts/finding_fleet.sh +391 -13
- package/scripts/finding_pipeline.sh +370 -11
- package/scripts/finding_verifier.sh +31 -2
- package/scripts/finding_verify.py +198 -13
- package/scripts/frontier_digest_autopilot.sh +3 -3
- package/scripts/gate_shape_scan.sh +16 -2
- package/scripts/selfcheck.sh +12 -8
- package/scripts/test_fh_run_resolver_lanes.sh +116 -0
- package/scripts/test_files_manifest_shipping_lanes.sh +31 -0
- package/scripts/test_finding_pipeline_lanes.sh +1556 -4
- package/scripts/test_gate_shape_scan_lanes.sh +11 -0
- package/scripts/test_marker_crossfamily_lanes.sh +75 -3
- package/scripts/test_marker_standpoint_lanes.sh +31 -6
- package/scripts/test_preprep_adjacent_dup_lanes.sh +1 -1
- package/scripts/test_preprep_diagram_lanes.sh +1 -1
- package/scripts/test_preprep_drift_anchor.sh +27 -12
- package/scripts/test_preprep_drift_anchor_lanes.sh +4 -2
- package/scripts/test_preprep_font_lanes.sh +7 -7
- package/scripts/test_preprep_progression_lanes.sh +1 -1
- package/scripts/test_preprep_promise_lanes.sh +1 -1
- package/scripts/test_preprep_retired_lanes.sh +45 -2
- package/scripts/test_preprep_slide_refs_lanes.sh +5 -5
- package/scripts/test_usage_ledger_lanes.sh +243 -0
- package/scripts/usage_report.py +211 -0
- package/templates/.git-hooks/pre-commit +152 -6
- package/templates/local_fh_context.md +1 -1
- package/templates/regression_guard.sh +1 -1
- package/plugins/fh-commons/skills/preprep/lane_geometry.py +0 -181
- package/plugins/fh-commons/skills/preprep/test_preprep_lanes_rp.py +0 -184
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/README.md +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/canon_terms.example.yaml +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/diagram_from_json.py +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/adjdup_known_negative.md +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/adjdup_known_positive.md +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/fixture_R3_negative.pptx +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/fixture_R3_positive.pptx +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/font_revert_probe.py +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/progression_known_negative.md +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/progression_known_positive.md +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/promise_known_negative.md +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/promise_known_positive.md +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/interslide_deps.py +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/jargon_terms.example.yaml +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/lane_adjacent_dup.py +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/lane_diagram.py +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/lane_font.py +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/lane_progression.py +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/lane_promise.py +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/lane_slide_refs.py +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/lane_slide_relations.py +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/ooxml/c1_baseline.txt +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/ooxml/gate.py +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/preprep_wire.py +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/presentation_checklist.md +0 -0
- /package/plugins/{fh-commons → fh-preprep}/skills/preprep/test_lane_font.py +0 -0
|
@@ -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"
|
|
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
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
# J4 처방 몇 자 / 몇 % / 어느 문장인지를 내나 — 숫자 없는 지적은 처방이 아니다
|
|
12
12
|
set -uo pipefail
|
|
13
13
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
14
|
-
SKILL="$HERE/plugins/fh-
|
|
14
|
+
SKILL="$HERE/plugins/fh-preprep/skills/preprep"
|
|
15
15
|
PASS=0; FAIL=0
|
|
16
16
|
ok(){ echo " ✅ $1"; PASS=$((PASS+1)); }
|
|
17
17
|
ng(){ echo " ❌ $1"; FAIL=$((FAIL+1)); }
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# «굽힘» 쪽 실사용은 tracks-meta/dispatch/2026-09-05_archify-preprep/REPORT.md 가 기록한다.
|
|
15
15
|
set -uo pipefail
|
|
16
16
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
17
|
-
SKILL="$HERE/plugins/fh-
|
|
17
|
+
SKILL="$HERE/plugins/fh-preprep/skills/preprep"
|
|
18
18
|
PASS=0; FAIL=0
|
|
19
19
|
ok(){ echo " ✅ $1"; PASS=$((PASS+1)); }
|
|
20
20
|
ng(){ echo " ❌ $1"; FAIL=$((FAIL+1)); }
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# test_preprep_drift_anchor.sh — 이원화의 단일-소스 앵커.
|
|
3
3
|
#
|
|
4
|
-
# preprep 은 두 진입점을 갖는다: FH 안의 스킬(plugins/fh-
|
|
4
|
+
# preprep 은 두 진입점을 갖는다: FH 안의 스킬(plugins/fh-preprep/skills/preprep/)과,
|
|
5
5
|
# 거기서 뽑아 세우는 standalone 현장 하네스. 🟥 **코드 사본이 둘이면 갈린다** —
|
|
6
6
|
# FH 자신의 규칙이 그것을 «single source of truth collapse · double maintenance burden»
|
|
7
7
|
# 이라 부른다. 그래서 이원화는 «복사본 둘»이 아니라 «단일 소스 + 얇은 두 진입점»이어야 하고,
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# D3 진입점 문서가 단일 소스를 가리키나 (죽은 포인터 금지)
|
|
15
15
|
set -uo pipefail
|
|
16
16
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
17
|
-
SRC="$HERE/plugins/fh-
|
|
17
|
+
SRC="$HERE/plugins/fh-preprep/skills/preprep"
|
|
18
18
|
# standalone 배포 위치는 환경변수로 받는다. 기본값을 박으면 다른 머신에서 거짓 SKIP 이 된다.
|
|
19
19
|
# 🟥 2026-09-03 — 그런데 «아무도 그 변수를 안 걸어서» D2 가 여태 SKIP 이었고, 그 사이 컴패니언
|
|
20
20
|
# 저장소의 fork(724줄)가 정본(789줄)과 갈라져 L9~L11 을 안 부르고 있었다 — 정본 주석이 이미
|
|
@@ -32,19 +32,31 @@ ng(){ echo " ❌ $1"; FAIL=$((FAIL+1)); }
|
|
|
32
32
|
sk(){ echo " ⏭ $1 — SKIPPED (**통과 아님**)"; SKIP=$((SKIP+1)); }
|
|
33
33
|
|
|
34
34
|
# D1 — 단일 소스
|
|
35
|
+
# 🟥 2026-09-10 정정: 파일 목록을 **박아 두지 않는다.** 박아 둔 목록은 조용히 낡는다 —
|
|
36
|
+
# 이 검사는 「10파일 · python 7파일」이라고 출력하면서 그 사이 늘어난 레인 셋
|
|
37
|
+
# (lane_slide_relations · lane_geometry · lane_slide_refs)을 **한 번도 안 봤다.**
|
|
38
|
+
# 개수는 참이었고 «무엇에 대한 개수인가»가 어긋난 형태다
|
|
39
|
+
# ([[feedback_count_is_true_but_referent_drifted]]).
|
|
40
|
+
# ⇒ 필수 문서만 이름으로 걸고, **python 은 디렉터리에서 뽑는다** — 레인을 새로 지으면
|
|
41
|
+
# 자동으로 이 앵커 안에 들어온다. 배선을 잊는 것으로 검사를 벗어날 수 없다.
|
|
35
42
|
missing=""
|
|
36
|
-
for f in
|
|
43
|
+
for f in SKILL.md README.md surfaces.example.yaml preprep.py; do
|
|
37
44
|
[ -f "$SRC/$f" ] || missing="$missing $f"
|
|
38
45
|
done
|
|
46
|
+
PYN=0
|
|
47
|
+
for f in "$SRC"/*.py; do [ -f "$f" ] && PYN=$((PYN+1)); done
|
|
39
48
|
if [ -n "$missing" ]; then ng "D1 단일 소스 결손:$missing"
|
|
49
|
+
elif [ "$PYN" -lt 5 ]; then
|
|
50
|
+
ng "D1 python 파일이 $PYN 개뿐 — 스킬이 헐었거나 SRC 가 틀린 곳을 가리킨다"
|
|
40
51
|
elif ! command -v python3 >/dev/null 2>&1; then
|
|
41
52
|
sk "D1 구문 검사 — python3 부재라 «돌 수 있나»를 못 쟀다(UNMEASURED)"
|
|
42
53
|
else
|
|
43
54
|
synerr=""
|
|
44
|
-
for f in
|
|
45
|
-
python3 -c "import ast,sys;ast.parse(open(sys.argv[1],encoding='utf-8').read())" "$
|
|
55
|
+
for f in "$SRC"/*.py; do
|
|
56
|
+
python3 -c "import ast,sys;ast.parse(open(sys.argv[1],encoding='utf-8').read())" "$f" 2>/dev/null \
|
|
57
|
+
|| synerr="$synerr $(basename "$f")"
|
|
46
58
|
done
|
|
47
|
-
[ -z "$synerr" ] && ok "D1
|
|
59
|
+
[ -z "$synerr" ] && ok "D1 필수 문서 3 + preprep.py 실재 · python ${PYN}파일 전부 구문 통과(디렉터리에서 뽑음)" \
|
|
48
60
|
|| ng "D1 구문 실패:$synerr"
|
|
49
61
|
fi
|
|
50
62
|
|
|
@@ -54,13 +66,16 @@ if [ -z "$DIST" ]; then
|
|
|
54
66
|
elif [ ! -d "$DIST" ]; then
|
|
55
67
|
ng "D2 $DIST_SRC 이 가리키는 곳이 없다: $DIST (설정됐는데 부재 = 드리프트 아니라 배선 결함)"
|
|
56
68
|
else
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
69
|
+
# 🟥 같은 정정 — 여기도 목록이 박혀 있었고, 게다가 **네 개를 돌면서 「5파일」이라고 출력**했다.
|
|
70
|
+
# 라벨이 자기 루프에 대해서도 거짓말한 셈이라, 세는 것과 말하는 것을 한 변수로 묶는다.
|
|
71
|
+
drift=""; n=0
|
|
72
|
+
for f in "$SRC"/*.py; do
|
|
73
|
+
b=$(basename "$f"); n=$((n+1))
|
|
74
|
+
if [ ! -f "$DIST/$b" ]; then drift="$drift $b(부재)"
|
|
75
|
+
elif ! cmp -s "$f" "$DIST/$b"; then drift="$drift $b(갈림)"; fi
|
|
61
76
|
done
|
|
62
|
-
[ -z "$drift" ] && ok "D2 standalone
|
|
63
|
-
|| ng "D2
|
|
77
|
+
[ -z "$drift" ] && ok "D2 standalone python ${n}파일이 단일 소스와 바이트 동일 ($DIST_SRC)" \
|
|
78
|
+
|| ng "D2 드리프트($n 중):$drift ⇒ 사본이 둘이 됐다. 단일 소스에서 다시 뽑아라"
|
|
64
79
|
fi
|
|
65
80
|
|
|
66
81
|
# D3 — 진입점 포인터가 죽었나
|
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
# explicit var still wins · nothing set → SKIP (never a silent PASS).
|
|
9
9
|
set -u
|
|
10
10
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"; A="$HERE/scripts/test_preprep_drift_anchor.sh"
|
|
11
|
-
SRC="$HERE/plugins/fh-
|
|
11
|
+
SRC="$HERE/plugins/fh-preprep/skills/preprep"; T=$(mktemp -d); pass=0; fail=0
|
|
12
12
|
chk(){ if [ "$1" = 0 ]; then echo " ✅ $2"; pass=$((pass+1)); else echo " ❌ $2"; fail=$((fail+1)); fi; }
|
|
13
|
-
|
|
13
|
+
# 🟥 2026-09-11: 앵커(D2)는 «디렉터리의 *.py 전부» 를 돈다(2026-09-10 덱 세션 정정). 여기 목록이 5개로 박혀 있어
|
|
14
|
+
# 새 레인 파일(lane_attr_consistency 등)이 «부재 드리프트» 로 읽혀 L2/L4 가 CI 에서 빨개졌다 — 앵커와 같은 규칙으로.
|
|
15
|
+
mk(){ mkdir -p "$1"; for f in "$SRC"/*.py; do cp "$f" "$1/$(basename "$f")"; done; }
|
|
14
16
|
echo "[preprep-drift-anchor] D2 known pairs"
|
|
15
17
|
# L1 nothing set → D2 SKIP (skip != pass), rc 0 (D2 is not a FAIL)
|
|
16
18
|
out=$(env -u PREPREP_STANDALONE_DIR FH_COMPANION_STORE="$T/nostore" bash "$A" 2>&1); printf '%s' "$out" | grep -q "D2 .*SKIPPED"; chk $? "L1 var unset + no companion preprep → D2 SKIPPED (not PASS)"
|
|
@@ -16,15 +16,15 @@
|
|
|
16
16
|
# 종료코드: 0 pass · 1 레인 실패 · 2 대상 부재 · 10 setup 실패
|
|
17
17
|
set -uo pipefail
|
|
18
18
|
cd "$(dirname "$0")/.." || exit 10
|
|
19
|
-
LANE=plugins/fh-
|
|
20
|
-
SELFTEST=plugins/fh-
|
|
21
|
-
PROBE=plugins/fh-
|
|
19
|
+
LANE=plugins/fh-preprep/skills/preprep/lane_font.py
|
|
20
|
+
SELFTEST=plugins/fh-preprep/skills/preprep/test_lane_font.py
|
|
21
|
+
PROBE=plugins/fh-preprep/skills/preprep/fixtures/font_revert_probe.py
|
|
22
22
|
[ -f "$LANE" ] || { echo "ⓘ $LANE absent — subject missing (NOT a pass)"; exit 2; }
|
|
23
23
|
[ -f "$SELFTEST" ] || { echo "ⓘ $SELFTEST absent — subject missing (NOT a pass)"; exit 2; }
|
|
24
24
|
[ -f "$PROBE" ] || { echo "ⓘ $PROBE absent — subject missing (NOT a pass)"; exit 2; }
|
|
25
25
|
command -v python3 >/dev/null 2>&1 || { echo "ⓘ python3 absent — setup broke"; exit 10; }
|
|
26
26
|
# 🟥 스테일 바이트코드가 뮤턴트/복원을 가린다 — 이웃 레인이 실측한 사고다(2026-09-06).
|
|
27
|
-
rm -rf plugins/fh-
|
|
27
|
+
rm -rf plugins/fh-preprep/skills/preprep/__pycache__
|
|
28
28
|
PASS=0; FAIL=0
|
|
29
29
|
ok(){ echo " ✅ $1"; PASS=$((PASS+1)); }
|
|
30
30
|
ng(){ echo " ❌ $1"; FAIL=$((FAIL+1)); }
|
|
@@ -42,7 +42,7 @@ fi
|
|
|
42
42
|
|
|
43
43
|
echo "== 배선·퇴화 — self-test 밖에서 한 번 더 =="
|
|
44
44
|
OUT=$(python3 -c "
|
|
45
|
-
import sys; sys.path.insert(0,'plugins/fh-
|
|
45
|
+
import sys; sys.path.insert(0,'plugins/fh-preprep/skills/preprep')
|
|
46
46
|
import lane_font as L
|
|
47
47
|
print(L.scan({}, '.')[1][0])
|
|
48
48
|
print(L.scan({'surfaces_by_id':{'built_deck':{'path':'/nope/none.pptx'}}}, '.')[1][0])
|
|
@@ -51,11 +51,11 @@ echo "$OUT" | grep -q 'NOT_CONFIGURED' && echo "$OUT" | grep -q 'UNMEASURED' \
|
|
|
51
51
|
&& ok "미선언 → NOT_CONFIGURED · 실물 없음 → UNMEASURED (둘 다 0 아님)" \
|
|
52
52
|
|| ng "퇴화 표기가 0 으로 접힌다: $OUT"
|
|
53
53
|
|
|
54
|
-
grep -q "import lane_font" plugins/fh-
|
|
54
|
+
grep -q "import lane_font" plugins/fh-preprep/skills/preprep/preprep.py \
|
|
55
55
|
&& ok "배선: preprep.py 가 이 레인을 부른다" \
|
|
56
56
|
|| ng "배선 없음 — 정의만 있고 아무도 안 부른다(built-but-not-wired)"
|
|
57
57
|
|
|
58
|
-
grep -q "lane_font" plugins/fh-
|
|
58
|
+
grep -q "lane_font" plugins/fh-preprep/skills/preprep/SKILL.md \
|
|
59
59
|
&& ok "명세: SKILL.md 가 이 레인을 적는다(고아 구현 아님)" \
|
|
60
60
|
|| ng "SKILL.md 에 없다 — 명세 없는 구현"
|
|
61
61
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# 🟥 P3 이 없으면 선언을 지우는 것만으로 이 레인이 조용해진다(가장 싼 무마 경로).
|
|
9
9
|
set -uo pipefail
|
|
10
10
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
11
|
-
SKILL="$HERE/plugins/fh-
|
|
11
|
+
SKILL="$HERE/plugins/fh-preprep/skills/preprep"
|
|
12
12
|
PASS=0; FAIL=0
|
|
13
13
|
ok(){ echo " ✅ $1"; PASS=$((PASS+1)); }
|
|
14
14
|
ng(){ echo " ❌ $1"; FAIL=$((FAIL+1)); }
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# Q4 판정보류 «맞나» 로 사람에게 넘기지 «틀렸다» 고 단정하지 않는다
|
|
14
14
|
set -uo pipefail
|
|
15
15
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
16
|
-
SKILL="$HERE/plugins/fh-
|
|
16
|
+
SKILL="$HERE/plugins/fh-preprep/skills/preprep"
|
|
17
17
|
PASS=0; FAIL=0
|
|
18
18
|
ok(){ echo " ✅ $1"; PASS=$((PASS+1)); }
|
|
19
19
|
ng(){ echo " ❌ $1"; FAIL=$((FAIL+1)); }
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# R4 처방 대체어를 finding 에 싣나 (처방 없는 판정은 절반이다)
|
|
13
13
|
set -uo pipefail
|
|
14
14
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
15
|
-
SKILL="$HERE/plugins/fh-
|
|
15
|
+
SKILL="$HERE/plugins/fh-preprep/skills/preprep"
|
|
16
16
|
PASS=0; FAIL=0
|
|
17
17
|
ok(){ echo " ✅ $1"; PASS=$((PASS+1)); }
|
|
18
18
|
ng(){ echo " ❌ $1"; FAIL=$((FAIL+1)); }
|
|
@@ -50,5 +50,48 @@ g(){ python3 -c "import json,sys;d=json.loads(sys.argv[1]);print(d[sys.argv[2]][
|
|
|
50
50
|
case "$(g use msg)" in *측정하다*) ok "R4 처방: 대체어를 finding 에 싣는다" ;;
|
|
51
51
|
*) ng "R4 실패 — 처방 없는 판정이다: $(g use msg)" ;; esac
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
|
|
54
|
+
# ── C 갈래 (2026-09-13 신설) — 설정을 못 읽은 것이 «발견» 으로 나가지 않나 ─────────────
|
|
55
|
+
#
|
|
56
|
+
# 실사고: `python3 preprep.py` 를 인자 없이 돌리면 **트레이스백으로 죽고 rc=1** 이었다.
|
|
57
|
+
# 이 도구의 선언(SKILL.md:164)은 「0 통과 · 1 발견 · 2 판정불가(계기오류/미측정)」이므로
|
|
58
|
+
# 🟥 **1 은 「뭔가 찾았다」** 다 — 즉 「한 줄도 못 쟀다」가 「발견 있음」과 **같은 값**으로 나갔고
|
|
59
|
+
# 부르는 쪽이 둘을 못 갈랐다. Done-When #1(「종료코드가 0/1/2 로 확정」)이 잡으라는 바로 그것이다.
|
|
60
|
+
#
|
|
61
|
+
# 여기서 재는 것: 설정 부재·깨짐·계약 미달 셋이 전부 **2** 로 확정되나, 그리고 정상 설정은
|
|
62
|
+
# 여전히 2 가 «아닌» 값을 내나(컨트롤 — 항상 2 면 판별력이 0 이다).
|
|
63
|
+
PP="$HERE/plugins/fh-preprep/skills/preprep/preprep.py"
|
|
64
|
+
CT=$(mktemp -d); trap 'rm -rf "$CT"' EXIT
|
|
65
|
+
|
|
66
|
+
_rc(){ ( cd "$CT" && python3 "$PP" "$1" >/dev/null 2>&1 ); printf '%s' "$?"; }
|
|
67
|
+
|
|
68
|
+
C1=$(_rc "$CT/does_not_exist.yaml")
|
|
69
|
+
[ "$C1" = "2" ] && ok "C1 설정 부재 → rc=2 (판정불가 · 발견 아님)" || ng "C1 설정 부재가 rc=$C1 — «발견 있음»(1)이나 트레이스백과 안 갈린다"
|
|
70
|
+
|
|
71
|
+
printf 'root: [ unclosed
|
|
72
|
+
' > "$CT/broken.yaml"
|
|
73
|
+
C2=$(_rc "$CT/broken.yaml")
|
|
74
|
+
[ "$C2" = "2" ] && ok "C2 YAML 깨짐 → rc=2" || ng "C2 깨진 설정이 rc=$C2"
|
|
75
|
+
|
|
76
|
+
printf '' > "$CT/empty.yaml"
|
|
77
|
+
C3=$(_rc "$CT/empty.yaml")
|
|
78
|
+
[ "$C3" = "2" ] && ok "C3 빈 설정(파싱은 됨 · 계약 미달) → rc=2" || ng "C3 빈 설정이 rc=$C3 — 「돌았는데 발견 0」으로 읽힌다"
|
|
79
|
+
|
|
80
|
+
# 🟥 컨트롤 — 정상 설정에서는 2 가 «안» 나와야 한다. 안 그러면 위 셋은 아무것도 증명 못 한다.
|
|
81
|
+
# 🟥 `kind` 가 빠지면 도구가 **옳게** «기계 어댑터 없음 → UNMEASURED → rc=2» 를 낸다.
|
|
82
|
+
# 초판 픽스처가 그걸 빠뜨려 컨트롤이 죽었고(정상 설정도 2) C1~C3 이 아무것도 증명하지
|
|
83
|
+
# 못했다 — 컨트롤이 대상과 같이 죽으면 차이가 사라진다. 도구가 아니라 픽스처가 틀렸다.
|
|
84
|
+
printf 'root: %s
|
|
85
|
+
surfaces:
|
|
86
|
+
- id: s1
|
|
87
|
+
path: deck.md
|
|
88
|
+
kind: markdown
|
|
89
|
+
' "$CT" > "$CT/ok.yaml"
|
|
90
|
+
printf '# 제목
|
|
91
|
+
본문 한 줄.
|
|
92
|
+
' > "$CT/deck.md"
|
|
93
|
+
C4=$(_rc "$CT/ok.yaml")
|
|
94
|
+
[ "$C4" != "2" ] && ok "C4 컨트롤: 정상 설정은 2 가 아니다 (실측 rc=$C4) — 판별력 있음" || ng "C4 컨트롤 죽음: 정상 설정도 rc=2 — 위 셋은 아무것도 증명하지 않는다"
|
|
95
|
+
|
|
96
|
+
echo "retired + config-verdict lanes: $PASS passed, $FAIL failed"
|
|
54
97
|
[ "$FAIL" -eq 0 ] || exit 1
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# 종료코드: 0 pass · 1 레인 실패 · 2 대상 부재 · 10 setup 실패
|
|
13
13
|
set -uo pipefail
|
|
14
14
|
cd "$(dirname "$0")/.." || exit 10
|
|
15
|
-
LANE=plugins/fh-
|
|
15
|
+
LANE=plugins/fh-preprep/skills/preprep/lane_slide_refs.py
|
|
16
16
|
[ -f "$LANE" ] || { echo "ⓘ $LANE absent — subject missing (NOT a pass)"; exit 2; }
|
|
17
17
|
command -v python3 >/dev/null 2>&1 || { echo "ⓘ python3 absent — setup broke"; exit 10; }
|
|
18
18
|
T=$(mktemp -d) || exit 10; trap 'rm -rf "$T"' EXIT
|
|
@@ -20,7 +20,7 @@ T=$(mktemp -d) || exit 10; trap 'rm -rf "$T"' EXIT
|
|
|
20
20
|
# 판별자를 뮤턴트로 바꿨다가 원본을 복원했는데 **스테일 __pycache__ 가 뮤턴트를 계속
|
|
21
21
|
# 먹여** 「복원했는데도 적색」이 나왔다. 반대 방향이 더 위험하다 — 뮤턴트를 넣었는데
|
|
22
22
|
# 캐시가 원본을 먹이면 «되돌려도 초록» 이 되어 **앵커가 죽은 것을 못 본다**.
|
|
23
|
-
rm -rf plugins/fh-
|
|
23
|
+
rm -rf plugins/fh-preprep/skills/preprep/__pycache__
|
|
24
24
|
PASS=0; FAIL=0
|
|
25
25
|
ok(){ echo " ✅ $1"; PASS=$((PASS+1)); }
|
|
26
26
|
ng(){ echo " ❌ $1"; FAIL=$((FAIL+1)); }
|
|
@@ -28,7 +28,7 @@ ng(){ echo " ❌ $1"; FAIL=$((FAIL+1)); }
|
|
|
28
28
|
RUN=$T/run.py
|
|
29
29
|
cat > "$RUN" <<'PY'
|
|
30
30
|
import sys, os, types
|
|
31
|
-
sys.path.insert(0, 'plugins/fh-
|
|
31
|
+
sys.path.insert(0, 'plugins/fh-preprep/skills/preprep')
|
|
32
32
|
|
|
33
33
|
# 🟥 python-pptx 를 **의존하지 않는다**. 이 스위트는 «분류» 를 재는 것이고, 그 입력은
|
|
34
34
|
# 「장 → 화면 텍스트 · 노트」라는 **작은 오리 타입**이 전부다(lane 의 _texts/
|
|
@@ -151,7 +151,7 @@ echo "$OUT" | grep -q 'SLIDE 1' \
|
|
|
151
151
|
|
|
152
152
|
echo "== 배선·퇴화 =="
|
|
153
153
|
OUT=$(python3 -c "
|
|
154
|
-
import sys; sys.path.insert(0,'plugins/fh-
|
|
154
|
+
import sys; sys.path.insert(0,'plugins/fh-preprep/skills/preprep')
|
|
155
155
|
import lane_slide_refs as L
|
|
156
156
|
print(L.scan({}, '.')[1][0])
|
|
157
157
|
print(L.scan({'surfaces_by_id':{'built_deck':{'path':'/nope/none.pptx'}}}, '.')[1][0])
|
|
@@ -160,7 +160,7 @@ echo "$OUT" | grep -q 'NOT_CONFIGURED' && echo "$OUT" | grep -q 'UNMEASURED' \
|
|
|
160
160
|
&& ok "L8 미선언 → NOT_CONFIGURED · 실물 없음 → UNMEASURED (둘 다 0 아님)" \
|
|
161
161
|
|| ng "L8 퇴화 표기가 0 으로 접힌다: $OUT"
|
|
162
162
|
|
|
163
|
-
grep -q "import lane_slide_refs" plugins/fh-
|
|
163
|
+
grep -q "import lane_slide_refs" plugins/fh-preprep/skills/preprep/preprep.py \
|
|
164
164
|
&& ok "L9 배선: preprep.py 가 이 레인을 부른다" \
|
|
165
165
|
|| ng "L9 배선 없음 — 정의만 있고 아무도 안 부른다(built-but-not-wired)"
|
|
166
166
|
|