@chrono-meta/fh-gate 3.0.0 โ 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/regression/probes_live.yaml +137 -0
- package/.claude/rules/.residency-patterns.defaults +7 -0
- package/.claude/rules/fh_4axis_gate.md +50 -1
- package/.claude-plugin/marketplace.json +8 -2
- package/AGENTS.md +27 -0
- package/CATALOG.md +17 -0
- package/CLAUDE.md +12 -2
- package/README.ja.md +51 -7
- package/README.ko.md +48 -7
- package/README.md +37 -5
- package/README.zh.md +45 -8
- package/docs/STANDARDS_ALIGNMENT.md +61 -0
- package/docs/USER_GUIDE.md +3 -0
- package/docs/USE_CASES.md +50 -0
- package/docs/model_tier_expectations.md +60 -0
- package/knowledge/shared/harness-core/fh_three_layer_canon.md +30 -10
- package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +8 -0
- package/knowledge/shared/harness-core/harness_incubator_doctrine.md +9 -0
- package/knowledge/shared/harness-core/iso_ai_standards_crosswalk.md +139 -0
- package/knowledge/shared/harness-core/measurement-integrity-checklist.md +20 -0
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +441 -4
- package/package.json +40 -2
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-commons/skills/preprep/README.md +4 -1
- package/plugins/fh-commons/skills/preprep/SKILL.md +99 -3
- package/plugins/fh-commons/skills/preprep/diagram_from_json.py +154 -0
- package/plugins/fh-commons/skills/preprep/fixtures/fixture_R3_negative.pptx +0 -0
- package/plugins/fh-commons/skills/preprep/fixtures/fixture_R3_positive.pptx +0 -0
- package/plugins/fh-commons/skills/preprep/fixtures/mk_slide_fixtures.py +179 -0
- package/plugins/fh-commons/skills/preprep/interslide_deps.py +98 -9
- package/plugins/fh-commons/skills/preprep/lane_adjacent_dup.py +4 -1
- package/plugins/fh-commons/skills/preprep/lane_diagram.py +95 -0
- package/plugins/fh-commons/skills/preprep/lane_geometry.py +181 -0
- package/plugins/fh-commons/skills/preprep/lane_promise.py +4 -1
- package/plugins/fh-commons/skills/preprep/lane_slide_refs.py +134 -0
- package/plugins/fh-commons/skills/preprep/lane_slide_relations.py +300 -0
- package/plugins/fh-commons/skills/preprep/preprep.py +129 -8
- package/plugins/fh-commons/skills/preprep/preprep_wire.py +210 -0
- package/plugins/fh-commons/skills/preprep/presentation_checklist.md +4 -0
- package/plugins/fh-commons/skills/preprep/surfaces.example.yaml +18 -0
- package/plugins/fh-commons/skills/preprep/test_preprep_lanes_rp.py +184 -0
- package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/CHANGELOG.md +43 -1
- package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +87 -2
- package/plugins/fh-meta/skills/frontier-digest/SKILL.md +2 -2
- package/plugins/fh-meta/skills/frontier-digest/SKILL_detail.md +25 -5
- package/plugins/fh-meta/skills/hub-cc-pr-reviewer/SKILL.md +11 -0
- package/plugins/fh-meta/skills/hub-cc-pr-reviewer/SKILL_detail.md +3 -0
- package/plugins/fh-qp/.claude-plugin/plugin.json +22 -0
- package/plugins/fh-qp/README.md +71 -0
- package/plugins/fh-qp/fixtures/evidence_known_clean.txt +3 -0
- package/plugins/fh-qp/fixtures/evidence_known_dirty.txt +6 -0
- package/plugins/fh-qp/fixtures/reach_known_partial.tsv +4 -0
- package/plugins/fh-qp/fixtures/reach_known_reached.tsv +4 -0
- package/plugins/fh-qp/fixtures/reach_known_wall.tsv +3 -0
- package/plugins/fh-qp/fixtures/verdicts_known_bad_branch.tsv +2 -0
- package/plugins/fh-qp/fixtures/verdicts_known_bad_vacuous_machine.tsv +2 -0
- package/plugins/fh-qp/fixtures/verdicts_known_good.tsv +4 -0
- package/plugins/fh-qp/fixtures/verdicts_verify_only.tsv +3 -0
- package/plugins/fh-qp/qp_profile.example.yaml +29 -0
- package/plugins/fh-qp/scripts/qp_tools.sh +216 -0
- package/plugins/fh-qp/skills/qp/SKILL.md +65 -0
- package/plugins/fh-qp/skills/qp-plan/SKILL.md +42 -0
- package/plugins/fh-qp/skills/qp-regress/SKILL.md +45 -0
- package/plugins/fh-qp/skills/qp-run/SKILL.md +49 -0
- package/scripts/chamber_run.sh +14 -5
- package/scripts/com.forge-harness.live-eval.plist +84 -0
- package/scripts/compaction_probe.sh +9 -35
- package/scripts/directional_diff_gate.sh +14 -2
- package/scripts/frontier_digest_autopilot.sh +4 -1
- package/scripts/map_postprocess.py +90 -0
- package/scripts/outbound_query_guard.sh +131 -0
- package/scripts/outbound_query_hook.sh +373 -0
- package/scripts/package_coverage_check.sh +55 -16
- package/scripts/pipe_verdict_guard.sh +41 -1
- package/scripts/probe_live_eval.sh +240 -0
- package/scripts/probe_live_eval_lib.py +579 -0
- package/scripts/proposal_hook.sh +120 -17
- package/scripts/push_zone_check.sh +78 -0
- package/scripts/residency_closure_scan.py +252 -0
- package/scripts/selfcheck.sh +41 -1
- package/scripts/session_close_check.sh +100 -0
- package/scripts/sim_isolated_run.sh +98 -2
- package/scripts/test_action_yml_lanes.sh +97 -0
- package/scripts/test_fh_qp_lanes.sh +105 -0
- package/scripts/test_gate_two_verdicts_lanes.sh +139 -0
- package/scripts/test_map_postprocess_lanes.sh +143 -0
- package/scripts/test_marker_affected_lanes.sh +93 -0
- package/scripts/test_marker_crossfamily_lanes.sh +90 -6
- package/scripts/test_marker_oracle_lanes.sh +136 -0
- package/scripts/test_outbound_query_hook_lanes.sh +433 -0
- package/scripts/test_outbound_query_lanes.sh +87 -0
- package/scripts/test_pipe_verdict_guard_lanes.sh +26 -0
- package/scripts/test_preprep_diagram_lanes.sh +87 -0
- package/scripts/test_preprep_drift_anchor.sh +3 -3
- package/scripts/test_preprep_slide_refs_lanes.sh +169 -0
- package/scripts/test_probe_live_eval_lanes.sh +437 -0
- package/scripts/test_proposal_hook_lanes.sh +22 -1
- package/scripts/test_push_zone_lanes.sh +304 -0
- package/scripts/test_residency_closure_lanes.sh +70 -0
- package/scripts/test_sim_isolated_run_lanes.sh +119 -0
- package/scripts/test_utterance_intake_lanes.sh +414 -0
- package/scripts/test_worktree_reclaim_lanes.sh +70 -0
- package/scripts/transcript_utterances.py +222 -0
- package/scripts/utterance_intake.sh +424 -0
- package/scripts/validate_yaml.sh +27 -0
- package/scripts/worktree_reclaim.sh +95 -0
- package/templates/.git-hooks/pre-commit +353 -1
- package/templates/.git-hooks/pre-push +91 -0
- package/templates/RED_TEAM_REPORT.md +49 -0
- package/templates/settings.PreToolUse.snippet.json +65 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_preprep_diagram_lanes.sh โ L12 diagram ๋ ์ธ + diagram_from_json.py ๊ฑฐ๋ถ ๊ฒฝ๋ก์ known-pair.
|
|
3
|
+
#
|
|
4
|
+
# ์ฌ๋ ๊ฒ:
|
|
5
|
+
# K1 known-negative ์์์ฆยท์ง๋ฌธยทvalidateยทํญยทviewBoxยทpad ์ ๋ถ ๋ง์ผ๋ฉด findings 0
|
|
6
|
+
# K2 stale JSON ์ ํ ๊ธ์ ๊ณ ์น๋ฉด ยซ์ง๋ฌธ ๋ค๋ฆยป 1๊ฑด์ ์ด๋ฆ์ผ๋ก ์ง๋
|
|
7
|
+
# K3 resolution PNG ์ค์ ํญ(IHDR)์ด ํํ ๋ฏธ๋ฌ์ด๋ฉด 1๊ฑด โ ์์์ฆ ์๊ธฐ์ ๊ณ (png_size)๊ฐ ์๋๋ผ ํค๋๋ฅผ ์ฝ๋
|
|
8
|
+
# K4 viewbox viewBox ํญ > ์ํ์ด๋ฉด 1๊ฑด(๊ธ์ ๋ฐฐ์จ)
|
|
9
|
+
# K5 unmeasured ์์์ฆ ์๋ PNG ๋ findings 0 + UNMEASURED ๋
ธํธ(0 ์ผ๋ก ์ ์ ๋)
|
|
10
|
+
# K6 not-configured diagram_source ํ๋ฉด์ด ์์ผ๋ฉด NOT_CONFIGURED(ํต๊ณผ ์๋)
|
|
11
|
+
# K7 refuse diagram_from_json.py ๊ฐ validate ์คํจ JSON ์ **๊ตฝ์ง ์๋** โ archify ๋ ์คํ
์ผ๋ก ๋์ฒด
|
|
12
|
+
# (์ค๋ฌผ archifyยทChrome ์์ด ๋๋ ๋ ์ธ์ด์ด์ผ CI ์์ ์ต์ปค๋ค)
|
|
13
|
+
# ๐ฅ archifyยทChrome ์ด ์์ด๋ ๋์์ผ ํ๋ค โ ๊ทธ๋์ K7 ์ ์คํ
archify(ok:false)๋ก ยซ๊ฑฐ๋ถยป๋ง ์ฐ๋ค.
|
|
14
|
+
# ยซ๊ตฝํยป ์ชฝ ์ค์ฌ์ฉ์ tracks-meta/dispatch/2026-09-05_archify-preprep/REPORT.md ๊ฐ ๊ธฐ๋กํ๋ค.
|
|
15
|
+
set -uo pipefail
|
|
16
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
17
|
+
SKILL="$HERE/plugins/fh-commons/skills/preprep"
|
|
18
|
+
PASS=0; FAIL=0
|
|
19
|
+
ok(){ echo " โ
$1"; PASS=$((PASS+1)); }
|
|
20
|
+
ng(){ echo " โ $1"; FAIL=$((FAIL+1)); }
|
|
21
|
+
command -v python3 >/dev/null 2>&1 || { echo " โ ๏ธ UNMEASURED โ python3 unavailable (not a pass)"; echo "diagram lanes: SKIPPED (not passed)"; exit 0; }
|
|
22
|
+
|
|
23
|
+
out=$(cd "$SKILL" && python3 - <<'PY' 2>&1
|
|
24
|
+
import sys, os, json, hashlib, struct, zlib, tempfile, subprocess
|
|
25
|
+
sys.path.insert(0, '.')
|
|
26
|
+
import lane_diagram as LD
|
|
27
|
+
def sha(p): return hashlib.sha256(open(p,'rb').read()).hexdigest()
|
|
28
|
+
def png(path, w, h=2):
|
|
29
|
+
# ์์ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ PNG(ํ์ 1๋ฐ์ดํธ/ํฝ์
) โ ํญ๋ง ์๋ฏธ ์๋ค
|
|
30
|
+
raw = b''.join(b'\x00' + b'\x80'*w for _ in range(h))
|
|
31
|
+
def chunk(t, d): return struct.pack('>I', len(d)) + t + d + struct.pack('>I', zlib.crc32(t+d) & 0xffffffff)
|
|
32
|
+
open(path,'wb').write(b'\x89PNG\r\n\x1a\n' + chunk(b'IHDR', struct.pack('>IIBBBBB', w, h, 8, 0, 0, 0, 0)) + chunk(b'IDAT', zlib.compress(raw)) + chunk(b'IEND', b''))
|
|
33
|
+
d = tempfile.mkdtemp(prefix='l12_')
|
|
34
|
+
spec = os.path.join(d, 'fig.workflow.json'); json.dump({'diagram_type':'workflow','nodes':[{'id':'a','label':'๊ฐ๋ฐ'}]}, open(spec,'w'))
|
|
35
|
+
def receipt(pngp, **kw):
|
|
36
|
+
r = {'spec_sha256': sha(spec), 'validate': {'ok': True, 'profile': 'showcase'}, 'viewbox_w': 720, 'pad': 0.03, 'png_size': [3840, 2]}
|
|
37
|
+
r.update(kw); json.dump(r, open(pngp[:-4] + '.receipt.json', 'w'))
|
|
38
|
+
def run(surfs, extra=None):
|
|
39
|
+
cfg = {'surfaces': surfs, 'diagram': {'min_px': 3840, 'max_viewbox_w': 720, 'min_pad': 0.02}}
|
|
40
|
+
return LD.scan(cfg, d)
|
|
41
|
+
R = {}
|
|
42
|
+
# K1 negative
|
|
43
|
+
p1 = os.path.join(d, 'ok.png'); png(p1, 3840); receipt(p1)
|
|
44
|
+
f, n = run([{'id':'fig','path':'fig.workflow.json','kind':'diagram_source','render':'ok.png'}]); R['K1'] = len(f)
|
|
45
|
+
# K2 stale: JSON ๊ณ ์นจ
|
|
46
|
+
open(spec,'a').write('\n'); f, n = run([{'id':'fig','path':'fig.workflow.json','kind':'diagram_source','render':'ok.png'}])
|
|
47
|
+
R['K2'] = [x[2] for x in f]
|
|
48
|
+
receipt(p1) # ๋ค์ ๋ง์ถค
|
|
49
|
+
# K3 resolution: ์์์ฆ์ 3840 ์ด๋ผ ยซ๋งํ์ง๋งยป ์ค์ ํค๋๋ 1000
|
|
50
|
+
p3 = os.path.join(d, 'small.png'); png(p3, 1000); receipt(p3, png_size=[3840, 2])
|
|
51
|
+
f, n = run([{'id':'fig','path':'fig.workflow.json','kind':'diagram_source','render':'small.png'}]); R['K3'] = [x[2] for x in f]
|
|
52
|
+
# K4 viewbox
|
|
53
|
+
p4 = os.path.join(d, 'wide.png'); png(p4, 3840); receipt(p4, viewbox_w=1137)
|
|
54
|
+
f, n = run([{'id':'fig','path':'fig.workflow.json','kind':'diagram_source','render':'wide.png'}]); R['K4'] = [x[2] for x in f]
|
|
55
|
+
# K5 unmeasured (์์์ฆ ์์)
|
|
56
|
+
p5 = os.path.join(d, 'hand.png'); png(p5, 3840)
|
|
57
|
+
f, n = run([{'id':'fig','path':'fig.workflow.json','kind':'diagram_source','render':'hand.png'}]); R['K5'] = {'n': len(f), 'unm': any('UNMEASURED' in s for s in n)}
|
|
58
|
+
# K6 not configured
|
|
59
|
+
f, n = run([]); R['K6'] = {'n': len(f), 'nc': any('NOT_CONFIGURED' in s for s in n)}
|
|
60
|
+
# K7 refuse โ ์คํ
archify(ํญ์ ok:false) + ๊ฐ์ง chrome. PNG ๊ฐ ยซ์ ์๊ฒจ์ผยป ํ๋ค
|
|
61
|
+
stub = os.path.join(d, 'archify_stub.mjs'); open(stub,'w').write('console.log(JSON.stringify({ok:false,diagnostics:[{code:"stub/fail",message:"stub"}]}))')
|
|
62
|
+
fake_chrome = os.path.join(d, 'chrome'); open(fake_chrome,'w').write('#!/bin/sh\nexit 0\n'); os.chmod(fake_chrome, 0o755)
|
|
63
|
+
outp = os.path.join(d, 'refused.png')
|
|
64
|
+
import shutil
|
|
65
|
+
if shutil.which('node'):
|
|
66
|
+
r = subprocess.run([sys.executable, 'diagram_from_json.py', spec, '--out', outp, '--archify', stub, '--chrome', fake_chrome], capture_output=True, text=True)
|
|
67
|
+
R['K7'] = {'rc': r.returncode, 'png': os.path.exists(outp), 'msg': 'REFUSE' in r.stdout}
|
|
68
|
+
else:
|
|
69
|
+
R['K7'] = {'rc': 'NO_NODE'}
|
|
70
|
+
print(json.dumps(R, ensure_ascii=False))
|
|
71
|
+
PY
|
|
72
|
+
); rc=$?
|
|
73
|
+
[ $rc -eq 0 ] || { echo " โ INSTRUMENT ERROR โ lane_diagram ์คํ ์คํจ (rc=$rc): $out"; exit 1; }
|
|
74
|
+
g(){ python3 -c "import json,sys;v=json.loads(sys.argv[1]);exec('r=v'+sys.argv[2]);print(r)" "$out" "$1"; }
|
|
75
|
+
[ "$(g "['K1']")" = "0" ] && ok "K1 known-negative: ๋ค ๋ง์ผ๋ฉด 0๊ฑด" || ng "K1 ์คํ $(g "['K1']")๊ฑด"
|
|
76
|
+
[ "$(g "['K2']")" = "['stale']" ] && ok "K2 stale: JSON ๊ณ ์น๋ฉด ยซstaleยป 1๊ฑด์ ์ด๋ฆ์ผ๋ก" || ng "K2 ์คํจ: $(g "['K2']")"
|
|
77
|
+
[ "$(g "['K3']")" = "['resolution']" ] && ok "K3 resolution: ์์์ฆ์ด 3840 ์ด๋ผ ํด๋ IHDR 1000 ์ ์ก๋๋ค(์๊ธฐ์ ๊ณ ์๋)" || ng "K3 ์คํจ: $(g "['K3']")"
|
|
78
|
+
[ "$(g "['K4']")" = "['viewbox']" ] && ok "K4 viewbox: 1137 > 720 ์ ์ก๋๋ค(๊ธ์ ๋ฐฐ์จ)" || ng "K4 ์คํจ: $(g "['K4']")"
|
|
79
|
+
[ "$(g "['K5']['n']")" = "0" ] && [ "$(g "['K5']['unm']")" = "True" ] && ok "K5 ์์์ฆ ์๋ ์๊ทธ๋ฆผ = UNMEASURED(0 ์๋ยทํต๊ณผ ์๋)" || ng "K5 ์คํจ: $(g "['K5']")"
|
|
80
|
+
[ "$(g "['K6']['n']")" = "0" ] && [ "$(g "['K6']['nc']")" = "True" ] && ok "K6 ํ๋ฉด ์์ = NOT_CONFIGURED" || ng "K6 ์คํจ: $(g "['K6']")"
|
|
81
|
+
k7=$(g "['K7']")
|
|
82
|
+
case "$k7" in
|
|
83
|
+
*NO_NODE*) echo " โญ K7 ๊ฑฐ๋ถ ๊ฒฝ๋ก โ node ๋ถ์ฌ๋ผ UNMEASURED (ํต๊ณผ ์๋)";;
|
|
84
|
+
*) [ "$(g "['K7']['rc']")" = "1" ] && [ "$(g "['K7']['png']")" = "False" ] && [ "$(g "['K7']['msg']")" = "True" ] && ok "K7 refuse: validate ์คํจ๋ฉด exit 1 + PNG ๋ฏธ์์ฑ + REFUSE ํ๊ธฐ" || ng "K7 ์คํจ: $k7";;
|
|
85
|
+
esac
|
|
86
|
+
echo "diagram lanes: $PASS passed, $FAIL failed"
|
|
87
|
+
[ "$FAIL" -eq 0 ] || exit 1
|
|
@@ -33,7 +33,7 @@ sk(){ echo " โญ $1 โ SKIPPED (**ํต๊ณผ ์๋**)"; SKIP=$((SKIP+1)); }
|
|
|
33
33
|
|
|
34
34
|
# D1 โ ๋จ์ผ ์์ค
|
|
35
35
|
missing=""
|
|
36
|
-
for f in preprep.py interslide_deps.py lane_progression.py lane_adjacent_dup.py lane_promise.py SKILL.md README.md surfaces.example.yaml; do
|
|
36
|
+
for f in preprep.py interslide_deps.py lane_progression.py lane_adjacent_dup.py lane_promise.py lane_diagram.py diagram_from_json.py SKILL.md README.md surfaces.example.yaml; do
|
|
37
37
|
[ -f "$SRC/$f" ] || missing="$missing $f"
|
|
38
38
|
done
|
|
39
39
|
if [ -n "$missing" ]; then ng "D1 ๋จ์ผ ์์ค ๊ฒฐ์:$missing"
|
|
@@ -41,10 +41,10 @@ elif ! command -v python3 >/dev/null 2>&1; then
|
|
|
41
41
|
sk "D1 ๊ตฌ๋ฌธ ๊ฒ์ฌ โ python3 ๋ถ์ฌ๋ผ ยซ๋ ์ ์๋ยป๋ฅผ ๋ชป ์๋ค(UNMEASURED)"
|
|
42
42
|
else
|
|
43
43
|
synerr=""
|
|
44
|
-
for f in preprep.py interslide_deps.py lane_progression.py lane_adjacent_dup.py lane_promise.py; do
|
|
44
|
+
for f in preprep.py interslide_deps.py lane_progression.py lane_adjacent_dup.py lane_promise.py lane_diagram.py diagram_from_json.py; do
|
|
45
45
|
python3 -c "import ast,sys;ast.parse(open(sys.argv[1],encoding='utf-8').read())" "$SRC/$f" 2>/dev/null || synerr="$synerr $f"
|
|
46
46
|
done
|
|
47
|
-
[ -z "$synerr" ] && ok "D1 ๋จ์ผ ์์ค
|
|
47
|
+
[ -z "$synerr" ] && ok "D1 ๋จ์ผ ์์ค 10ํ์ผ ์ค์ฌ + python 7ํ์ผ ๊ตฌ๋ฌธ ํต๊ณผ" \
|
|
48
48
|
|| ng "D1 ๊ตฌ๋ฌธ ์คํจ:$synerr"
|
|
49
49
|
fi
|
|
50
50
|
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_preprep_slide_refs_lanes.sh โ preprep L13(๋ฆฌํฐ๋ด ์ฅ ๋ฒํธ ์ฐธ์กฐ)์ ๊ณ์ฝ์ ๊ณ ์ ํ๋ค.
|
|
3
|
+
#
|
|
4
|
+
# WHY: ์ฅ์ ๋นผ๋ฉด ๋ค ๋ฒํธ๊ฐ ํต์งธ๋ก ๋น๊ฒจ์ง๋ค. ์ด์ ๋ ์ธ๋ค์ ยซ๋ง๋ก ๊ฐ๋ฆฌํค๋ ๊ฒยป๋ง ๋ณธ๋ค
|
|
5
|
+
# (L5/L8 ํ๋ฉด ์ฌ์ฌ์ฉยท์๊ฐ ์ง์ ยท R1~R5 ์ ์์ดยท์์ ๊ถ) โ ์ซ์ ์ฐธ์กฐ๋ ์๋ฌด๋ ์ ๋ดค๋ค.
|
|
6
|
+
#
|
|
7
|
+
# ๐ฅ ์ด ๋ ์ธ์ ๋์ ์ ๊ฒ์ถ์ด ์๋๋ผ **๋ถ๋ฅ**๋ค. ๊ฐ์ ยซ์ซ์+๋จ์ยป๊ฐ ์
์ ๊ฐ๋ฆฌํจ๋ค:
|
|
8
|
+
# SLIDE(์ง์ง ์ฅ) ยท SECTION(์น์
๋ฒํธ) ยท CITATION(๋
ผ๋ฌธ ์ชฝ์)
|
|
9
|
+
# ์ค์ธก ์ฝํผ์ค(if(kakao) 121์ฅ)์ ์
์ด **์ ๋ถ** ์์๊ณ , SLIDE ๋ **0๊ฑด**์ด์๋ค.
|
|
10
|
+
# โ ์ค๋ฌผ์ known-**negative** ๋ง ๋์ค๋ค. ์์ฑ์ ์๋ ํฝ์ค์ฒ๊ฐ ๋๋ค โ ๊ทธ๋์ ๋ ๋ค ์๋ค.
|
|
11
|
+
#
|
|
12
|
+
# ์ข
๋ฃ์ฝ๋: 0 pass ยท 1 ๋ ์ธ ์คํจ ยท 2 ๋์ ๋ถ์ฌ ยท 10 setup ์คํจ
|
|
13
|
+
set -uo pipefail
|
|
14
|
+
cd "$(dirname "$0")/.." || exit 10
|
|
15
|
+
LANE=plugins/fh-commons/skills/preprep/lane_slide_refs.py
|
|
16
|
+
[ -f "$LANE" ] || { echo "โ $LANE absent โ subject missing (NOT a pass)"; exit 2; }
|
|
17
|
+
command -v python3 >/dev/null 2>&1 || { echo "โ python3 absent โ setup broke"; exit 10; }
|
|
18
|
+
T=$(mktemp -d) || exit 10; trap 'rm -rf "$T"' EXIT
|
|
19
|
+
# ๐ฅ ํ์ด์ฌ ๋ฐ์ดํธ์ฝ๋ ์บ์๋ฅผ ์ง์ฐ๊ณ ์์ํ๋ค. ๋๋๋ฆผ ํ๋ก๋ธ์์ ์ค์ธกํ๋ค(2026-09-06):
|
|
20
|
+
# ํ๋ณ์๋ฅผ ๋ฎคํดํธ๋ก ๋ฐ๊ฟจ๋ค๊ฐ ์๋ณธ์ ๋ณต์ํ๋๋ฐ **์คํ
์ผ __pycache__ ๊ฐ ๋ฎคํดํธ๋ฅผ ๊ณ์
|
|
21
|
+
# ๋จน์ฌ** ใ๋ณต์ํ๋๋ฐ๋ ์ ์ใ์ด ๋์๋ค. ๋ฐ๋ ๋ฐฉํฅ์ด ๋ ์ํํ๋ค โ ๋ฎคํดํธ๋ฅผ ๋ฃ์๋๋ฐ
|
|
22
|
+
# ์บ์๊ฐ ์๋ณธ์ ๋จน์ด๋ฉด ยซ๋๋๋ ค๋ ์ด๋กยป ์ด ๋์ด **์ต์ปค๊ฐ ์ฃฝ์ ๊ฒ์ ๋ชป ๋ณธ๋ค**.
|
|
23
|
+
rm -rf plugins/fh-commons/skills/preprep/__pycache__
|
|
24
|
+
PASS=0; FAIL=0
|
|
25
|
+
ok(){ echo " โ
$1"; PASS=$((PASS+1)); }
|
|
26
|
+
ng(){ echo " โ $1"; FAIL=$((FAIL+1)); }
|
|
27
|
+
|
|
28
|
+
RUN=$T/run.py
|
|
29
|
+
cat > "$RUN" <<'PY'
|
|
30
|
+
import sys, os, types
|
|
31
|
+
sys.path.insert(0, 'plugins/fh-commons/skills/preprep')
|
|
32
|
+
|
|
33
|
+
# ๐ฅ python-pptx ๋ฅผ **์์กดํ์ง ์๋๋ค**. ์ด ์ค์ํธ๋ ยซ๋ถ๋ฅยป ๋ฅผ ์ฌ๋ ๊ฒ์ด๊ณ , ๊ทธ ์
๋ ฅ์
|
|
34
|
+
# ใ์ฅ โ ํ๋ฉด ํ
์คํธ ยท ๋
ธํธใ๋ผ๋ **์์ ์ค๋ฆฌ ํ์
**์ด ์ ๋ถ๋ค(lane ์ _texts/
|
|
35
|
+
# section_numbers ๊ฐ ์ฝ๋ ํ๋ฉด). ์ค๋ฌผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์๊ตฌํ๋ฉด ๋ฌ๋์ ์์ ๋
|
|
36
|
+
# HARNESS ERROR(exit 10)๋ก ์ฃฝ๊ณ โ ๊ทธ๊ฑด ยซ์ธก์ ๋ชป ํ๋คยป๋ผ ์ณ์ ๋ฐฉํฅ์ด์ง๋ง, ๊ทธ ๋๊ฐ๋ก
|
|
37
|
+
# **CI ์์ ์ด ๋ ์ธ์ด ์์ ์ ๋๊ฒ** ๋๋ค. ์ ๋๋ ์ต์ปค๋ ์ต์ปค๊ฐ ์๋๋ค.
|
|
38
|
+
# โ `sys.modules['pptx']` ์ ์คํ
์ ์ฌ์ด lane ์ `from pptx import Presentation`
|
|
39
|
+
# ๊ฒฝ๋ก๊น์ง **๊ทธ๋๋ก ํต๊ณผ์ํจ๋ค** โ scan() ์ ๊ฒฝ๋ก ํด์ยท๋
ธํธ ์ฝ๊ธฐ๊ฐ ๋ค ๋ฎ์ธ๋ค.
|
|
40
|
+
class _Run:
|
|
41
|
+
def __init__(self, t): self.text = t
|
|
42
|
+
class _Para:
|
|
43
|
+
def __init__(self, t): self.runs = [_Run(t)]
|
|
44
|
+
class _TF:
|
|
45
|
+
def __init__(self, t): self.paragraphs = [_Para(t)]; self.text = t
|
|
46
|
+
class _Shape:
|
|
47
|
+
def __init__(self, t): self.has_text_frame = True; self.text_frame = _TF(t)
|
|
48
|
+
class _Notes:
|
|
49
|
+
def __init__(self, t): self.notes_text_frame = _TF(t)
|
|
50
|
+
class _Slide:
|
|
51
|
+
def __init__(self, scr, note):
|
|
52
|
+
self.shapes = [_Shape(x) for x in scr]
|
|
53
|
+
self.has_notes_slide = True
|
|
54
|
+
self.notes_slide = _Notes(note)
|
|
55
|
+
class _Prs:
|
|
56
|
+
def __init__(self, spec): self.slides = [_Slide(a, b) for a, b in spec]
|
|
57
|
+
|
|
58
|
+
_SPEC = {}
|
|
59
|
+
def _Presentation(path):
|
|
60
|
+
key = os.path.normpath(path)
|
|
61
|
+
if key not in _SPEC:
|
|
62
|
+
raise FileNotFoundError(key) # ์ค๋ฌผ ์์ ๊ฒฝ๋ก๋ lane ์ด UNMEASURED ๋ก ๋ธ๋ค
|
|
63
|
+
return _Prs(_SPEC[key])
|
|
64
|
+
_fake = types.ModuleType('pptx'); _fake.Presentation = _Presentation
|
|
65
|
+
sys.modules['pptx'] = _fake
|
|
66
|
+
|
|
67
|
+
def build(path, slides):
|
|
68
|
+
"""slides = [(ํ๋ฉด ์ค๋ค, ๋๋ณธ)] โ ํ๋ฉด ์ค์ ๊ฐ๊ฐ ๋ณ๋ ํ
์คํธ ์์."""
|
|
69
|
+
_SPEC[os.path.normpath(path)] = slides
|
|
70
|
+
open(path, 'w').close() # os.path.exists ๊ฒ์ดํธ๋ฅผ ์ค์ ๋ก ์ง๋๊ฐ๊ฒ
|
|
71
|
+
|
|
72
|
+
def scan(path, mutate=None):
|
|
73
|
+
import importlib, lane_slide_refs as L
|
|
74
|
+
importlib.reload(L)
|
|
75
|
+
if mutate == 'kill-cite':
|
|
76
|
+
L.CITE = __import__('re').compile(r'(?!x)x') # ์ ๋ ์ ๋ง๋๋ค
|
|
77
|
+
if mutate == 'kill-section':
|
|
78
|
+
L.section_numbers = lambda prs: set()
|
|
79
|
+
cfg = {'surfaces_by_id': {'built_deck': {'path': path}}}
|
|
80
|
+
return L.scan(cfg, '.')
|
|
81
|
+
|
|
82
|
+
if __name__ == '__main__':
|
|
83
|
+
build(sys.argv[1], eval(sys.argv[2]))
|
|
84
|
+
f, n = scan(sys.argv[1], sys.argv[3] if len(sys.argv) > 3 else None)
|
|
85
|
+
print('FINDINGS', len(f))
|
|
86
|
+
for x in f: print('F|', x[2])
|
|
87
|
+
for x in n: print('N|', x)
|
|
88
|
+
PY
|
|
89
|
+
|
|
90
|
+
SEC=$'3. ์ด๋ป๊ฒ ๋ง๋ค์๋'
|
|
91
|
+
# ์น์
๋ผ๋ฒจ์ ยซ์ฌ๋ฌ ์ฅ์ ๋ฐ๋ณตยป ๋์ด์ผ ์น์
์ผ๋ก ์ธ์ ๋๋ค โ ํฝ์ค์ฒ๋ ๊ทธ ์กฐ๊ฑด์ ๋ง์กฑ์ํจ๋ค
|
|
92
|
+
DECK_SEC="[(['$SEC','3์ฅ์์ ๊ฒ์ดํธ๋ถํฐ ๋ณด๊ฒ ์ต๋๋ค'],''),(['$SEC'],''),(['$SEC'],'')]"
|
|
93
|
+
DECK_CITE="[(['1) Ye, ใCoding with Enemyใ, arXiv:2606.05647, 2026 (4p)'],''),([''],''),([''],'')]"
|
|
94
|
+
DECK_DANGLING="[(['๋ณธ๋ฌธ'],'130p ๋ฅผ ๋ณด์ธ์'),(['๋ณธ๋ฌธ'],''),(['๋ณธ๋ฌธ'],'')]"
|
|
95
|
+
DECK_OK="[(['๋ณธ๋ฌธ'],'2p ์์ ๋ณด์
จ๋ฏ์ด'),(['๋ณธ๋ฌธ'],''),(['๋ณธ๋ฌธ'],'')]"
|
|
96
|
+
DECK_SELF="[(['๋ณธ๋ฌธ'],''),(['๋ณธ๋ฌธ'],'2p ๋ฅผ ๋ณด์ธ์'),(['๋ณธ๋ฌธ'],'')]"
|
|
97
|
+
DECK_DECIMAL="[(['62.5% ๊ฐ์ '],'62์ฅ์ ๋ณด์ธ์'),(['62.5% ๊ฐ์ '],''),(['๋ณธ๋ฌธ'],'')]"
|
|
98
|
+
DECK_EMPTY="[(['์๋ฌด ์ฐธ์กฐ๋ ์๋ค'],''),(['๋ณธ๋ฌธ'],''),(['๋ณธ๋ฌธ'],'')]"
|
|
99
|
+
|
|
100
|
+
run(){ python3 "$RUN" "$T/$1.pptx" "$2" ${3:-} 2>&1; }
|
|
101
|
+
|
|
102
|
+
echo "== L13 โ ๋ถ๋ฅ๊ฐ ํ์ค์ ์ง๋ค =="
|
|
103
|
+
|
|
104
|
+
OUT=$(run pos "$DECK_DANGLING")
|
|
105
|
+
echo "$OUT" | grep -q '^FINDINGS 1' && echo "$OUT" | grep -q '๋ฒ์ ๋ฐ' \
|
|
106
|
+
&& ok "L1 known-positive: 3์ฅ ๋ฑ์ ยซ130pยป โ ๋ฒ์ ๋ฐ finding" \
|
|
107
|
+
|| ng "L1 known-positive ์คํจ: $(echo "$OUT" | head -2 | tr '\n' ' ')"
|
|
108
|
+
|
|
109
|
+
OUT=$(run sec "$DECK_SEC")
|
|
110
|
+
echo "$OUT" | grep -q '^FINDINGS 0' && echo "$OUT" | grep -q 'SECTION 1' \
|
|
111
|
+
&& ok "L2 known-negative: ใ3์ฅ์์ใ = SECTION, finding 0" \
|
|
112
|
+
|| ng "L2 ์น์
์ด ์ ๊ฐ๋ ธ๋ค: $(echo "$OUT" | grep '^N|' | head -1)"
|
|
113
|
+
|
|
114
|
+
OUT=$(run cite "$DECK_CITE")
|
|
115
|
+
echo "$OUT" | grep -q '^FINDINGS 0' && echo "$OUT" | grep -q 'CITATION 1' \
|
|
116
|
+
&& ok "L3 known-negative: ์ธ์ฉ ใ(4p)ใ = CITATION, finding 0" \
|
|
117
|
+
|| ng "L3 ์ธ์ฉ์ด ์ ๊ฐ๋ ธ๋ค: $(echo "$OUT" | grep '^N|' | head -1)"
|
|
118
|
+
|
|
119
|
+
OUT=$(run okref "$DECK_OK")
|
|
120
|
+
echo "$OUT" | grep -q '^FINDINGS 0' && echo "$OUT" | grep -q 'SLIDE 1' \
|
|
121
|
+
&& ok "L4 ์ ์ SLIDE ์ฐธ์กฐ๋ ๋
ธํธ๋ก๋ง(๋ฒ์ ์์ด๋ผ finding ์๋)" \
|
|
122
|
+
|| ng "L4 ์ ์ ์ฐธ์กฐ ์ฒ๋ฆฌ ์ด์: $(echo "$OUT" | grep '^N|' | head -1)"
|
|
123
|
+
|
|
124
|
+
OUT=$(run self "$DECK_SELF")
|
|
125
|
+
echo "$OUT" | grep -q '์๊ธฐ ์์ ์ฐธ์กฐ' \
|
|
126
|
+
&& ok "L5 ์๊ธฐ ์์ ์ฐธ์กฐ๋ โ ๏ธ ๋ก ํ๋ฉดํ(์ฐจ๋จ์ ์๋)" \
|
|
127
|
+
|| ng "L5 ์๊ธฐ ์ฐธ์กฐ๊ฐ ์ ๋ฌ๋ค"
|
|
128
|
+
|
|
129
|
+
OUT=$(run dec "$DECK_DECIMAL")
|
|
130
|
+
if echo "$OUT" | grep -q '^FINDINGS 1' && echo "$OUT" | grep -q '๋ฒ์ ๋ฐ'; then
|
|
131
|
+
ok "L6 ยซ62.5%ยป ๋ฅผ ์น์
์ผ๋ก ์ ๋จน๋๋ค โ ใ62์ฅใ์ด SLIDE ๋ก ๋จ์ ๋ฒ์ ๋ฐ์ผ๋ก ์กํ๋ค"
|
|
132
|
+
else
|
|
133
|
+
ng "L6 ์์์ ์ด ์น์
์ผ๋ก ๋จนํ๋ค(์ค์ธก ์ฌ๊ณ ์ฌ๋ฐ): $(echo "$OUT" | grep '^N|' | head -1)"
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
OUT=$(run empty "$DECK_EMPTY")
|
|
137
|
+
echo "$OUT" | grep -q 'UNMEASURED' \
|
|
138
|
+
&& ok "L7 ์ฐธ์กฐ 0๊ฑด โ ยซ๊นจ๋ํ๋คยป๊ฐ ์๋๋ผ UNMEASURED ๋ก ์ ๋๋ค" \
|
|
139
|
+
|| ng "L7 0๊ฑด์ ํต๊ณผ๋ก ์ฝ๋๋ค(๋ฏธ์ธก์ โ 0 ์๋ฐ)"
|
|
140
|
+
|
|
141
|
+
echo "== ์ปจํธ๋กค โ ํ๋ณ์๋ฅผ ์ฃฝ์ด๋ฉด ๋นจ๊ฐ์ง๋๊ฐ(๋๋๋ฆผ) =="
|
|
142
|
+
OUT=$(run cite2 "$DECK_CITE" kill-cite)
|
|
143
|
+
echo "$OUT" | grep -q '^FINDINGS 1' \
|
|
144
|
+
&& ok "CTRL-A ์ธ์ฉ ํ๋ณ์๋ฅผ ์ฃฝ์ด๋ฉด L3 ๊ฐ ์คํ์ผ๋ก ๋ค์งํ๋ค(ํ๋ณ์๊ฐ ํ์ค์ ์ง๋ค)" \
|
|
145
|
+
|| ng "CTRL-A ๋ฎคํดํธ์ธ๋ฐ ์๋ฌด ์ผ๋ ์๋ค โ ์ธ์ฉ ๋ถ๊ธฐ๊ฐ ์ฅ์์ด๋ค"
|
|
146
|
+
|
|
147
|
+
OUT=$(run sec2 "$DECK_SEC" kill-section)
|
|
148
|
+
echo "$OUT" | grep -q 'SLIDE 1' \
|
|
149
|
+
&& ok "CTRL-B ์น์
ํ๋ณ์๋ฅผ ์ฃฝ์ด๋ฉด ใ3์ฅ์์ใ๊ฐ SLIDE ๋ก ๋์ด๊ฐ๋ค" \
|
|
150
|
+
|| ng "CTRL-B ๋ฎคํดํธ์ธ๋ฐ ๋ถ๋ฅ๊ฐ ๊ทธ๋๋ก โ ์น์
๋ถ๊ธฐ๊ฐ ์ฅ์์ด๋ค"
|
|
151
|
+
|
|
152
|
+
echo "== ๋ฐฐ์ ยทํดํ =="
|
|
153
|
+
OUT=$(python3 -c "
|
|
154
|
+
import sys; sys.path.insert(0,'plugins/fh-commons/skills/preprep')
|
|
155
|
+
import lane_slide_refs as L
|
|
156
|
+
print(L.scan({}, '.')[1][0])
|
|
157
|
+
print(L.scan({'surfaces_by_id':{'built_deck':{'path':'/nope/none.pptx'}}}, '.')[1][0])
|
|
158
|
+
" 2>&1)
|
|
159
|
+
echo "$OUT" | grep -q 'NOT_CONFIGURED' && echo "$OUT" | grep -q 'UNMEASURED' \
|
|
160
|
+
&& ok "L8 ๋ฏธ์ ์ธ โ NOT_CONFIGURED ยท ์ค๋ฌผ ์์ โ UNMEASURED (๋ ๋ค 0 ์๋)" \
|
|
161
|
+
|| ng "L8 ํดํ ํ๊ธฐ๊ฐ 0 ์ผ๋ก ์ ํ๋ค: $OUT"
|
|
162
|
+
|
|
163
|
+
grep -q "import lane_slide_refs" plugins/fh-commons/skills/preprep/preprep.py \
|
|
164
|
+
&& ok "L9 ๋ฐฐ์ : preprep.py ๊ฐ ์ด ๋ ์ธ์ ๋ถ๋ฅธ๋ค" \
|
|
165
|
+
|| ng "L9 ๋ฐฐ์ ์์ โ ์ ์๋ง ์๊ณ ์๋ฌด๋ ์ ๋ถ๋ฅธ๋ค(built-but-not-wired)"
|
|
166
|
+
|
|
167
|
+
echo "โโ preprep slide-refs lanes: PASS=$PASS FAIL=$FAIL"
|
|
168
|
+
[ "$FAIL" -eq 0 ] || exit 1
|
|
169
|
+
exit 0
|