@chrono-meta/fh-gate 3.4.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.
Files changed (66) hide show
  1. package/.claude-plugin/marketplace.json +9 -3
  2. package/README.md +2 -2
  3. package/docs/codex-compat.md +1 -1
  4. package/knowledge/shared/learnings/subagent_invocations_log.yaml +89 -4
  5. package/package.json +8 -1
  6. package/plugins/fh-commons/.claude-plugin/plugin.json +2 -2
  7. package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
  8. package/plugins/fh-meta/CHANGELOG.md +22 -0
  9. package/plugins/fh-preprep/.claude-plugin/plugin.json +22 -0
  10. package/plugins/fh-preprep/README.md +24 -0
  11. package/plugins/{fh-commons → fh-preprep}/skills/preprep/SKILL.md +75 -3
  12. package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/mk_slide_fixtures.py +152 -1
  13. package/plugins/fh-preprep/skills/preprep/lane_attr_consistency.py +370 -0
  14. package/plugins/fh-preprep/skills/preprep/lane_geometry.py +339 -0
  15. package/plugins/fh-preprep/skills/preprep/lane_screen_parity.py +232 -0
  16. package/plugins/fh-preprep/skills/preprep/oox.py +305 -0
  17. package/plugins/{fh-commons → fh-preprep}/skills/preprep/preprep.py +52 -4
  18. package/plugins/fh-preprep/skills/preprep/safe_install.py +369 -0
  19. package/plugins/{fh-commons → fh-preprep}/skills/preprep/surfaces.example.yaml +29 -0
  20. package/plugins/fh-preprep/skills/preprep/test_preprep_lanes_rp.py +1417 -0
  21. package/plugins/fh-preprep/skills/preprep/usage_ledger.py +191 -0
  22. package/plugins/fh-qp/.claude-plugin/plugin.json +1 -1
  23. package/scripts/fh-run.sh +13 -0
  24. package/scripts/field_canon_preload.sh +1 -1
  25. package/scripts/selfcheck.sh +12 -8
  26. package/scripts/test_fh_run_resolver_lanes.sh +116 -0
  27. package/scripts/test_files_manifest_shipping_lanes.sh +31 -0
  28. package/scripts/test_preprep_adjacent_dup_lanes.sh +1 -1
  29. package/scripts/test_preprep_diagram_lanes.sh +1 -1
  30. package/scripts/test_preprep_drift_anchor.sh +27 -12
  31. package/scripts/test_preprep_drift_anchor_lanes.sh +4 -2
  32. package/scripts/test_preprep_font_lanes.sh +7 -7
  33. package/scripts/test_preprep_progression_lanes.sh +1 -1
  34. package/scripts/test_preprep_promise_lanes.sh +1 -1
  35. package/scripts/test_preprep_retired_lanes.sh +45 -2
  36. package/scripts/test_preprep_slide_refs_lanes.sh +5 -5
  37. package/scripts/test_usage_ledger_lanes.sh +243 -0
  38. package/scripts/usage_report.py +211 -0
  39. package/plugins/fh-commons/skills/preprep/lane_geometry.py +0 -181
  40. package/plugins/fh-commons/skills/preprep/test_preprep_lanes_rp.py +0 -184
  41. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/README.md +0 -0
  42. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/canon_terms.example.yaml +0 -0
  43. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/diagram_from_json.py +0 -0
  44. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/adjdup_known_negative.md +0 -0
  45. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/adjdup_known_positive.md +0 -0
  46. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/fixture_R3_negative.pptx +0 -0
  47. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/fixture_R3_positive.pptx +0 -0
  48. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/font_revert_probe.py +0 -0
  49. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/progression_known_negative.md +0 -0
  50. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/progression_known_positive.md +0 -0
  51. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/promise_known_negative.md +0 -0
  52. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/fixtures/promise_known_positive.md +0 -0
  53. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/interslide_deps.py +0 -0
  54. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/jargon_terms.example.yaml +0 -0
  55. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/lane_adjacent_dup.py +0 -0
  56. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/lane_diagram.py +0 -0
  57. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/lane_font.py +0 -0
  58. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/lane_progression.py +0 -0
  59. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/lane_promise.py +0 -0
  60. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/lane_slide_refs.py +0 -0
  61. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/lane_slide_relations.py +0 -0
  62. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/ooxml/c1_baseline.txt +0 -0
  63. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/ooxml/gate.py +0 -0
  64. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/preprep_wire.py +0 -0
  65. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/presentation_checklist.md +0 -0
  66. /package/plugins/{fh-commons → fh-preprep}/skills/preprep/test_lane_font.py +0 -0
@@ -11,21 +11,27 @@
11
11
  "plugins": [
12
12
  {
13
13
  "name": "fh-meta",
14
- "version": "3.4.0",
14
+ "version": "3.5.0",
15
15
  "description": "New in 2.2.0: BREAKING (gate): chamber step 6 now reads ACTUAL.md, not BUDGET.md — an in-flight chamber run whose actual cost sits in BUDGET.md blocks until the ACTUAL: line moves to tracks/_chamber/<slug>/ACTUAL.md (the runner prints the path). Why: BUDGET.md's pre-verdict hash IS the ordering witness, and step 6 hard-blocked until that same file changed, so every run that reached COMPLETE necessarily mutated a witnessed artifact and verify returned TAMPERED — the chamber's promotion condition was unsatisfiable by construction, not by strictness. Two roles (immutable witness / post-verdict calibration sink) had collided in one file; each was correct alone, so neither side's code showed the conflict. Also: ko-tech-writer Step 2/4-b scans are now calibration-backed (known-pair fixtures + reproducible command, shipped) — discrimination is proven, 'zero residue' is explicitly NOT; chamber lane suite 12 -> 33 including the runner x witness seam no test covered; chamber_run.sh now teaches the two-commit discipline (gate hashes and verdict hash must land in separate commits/PRs — it previously advised the opposite). New in 2.1.0: BREAKING (gate): `crossfamily: declined` in an Axes 2-3 marker now requires grounds naming a record path that RESOLVES on disk — bare `declined`, and `declined` justified by author judgment, are blocked at commit. Remedy: cite where the operator decision lives (e.g. `.. — operator declined sidecars, per knowledge/shared/rules/operational_adaptation.md`), or use `DEGRADED_PANEL_UNUSED` if a panel was reachable and you chose not to recruit it — which is what author judgment actually is. `declined` was the only enum value with no grounds requirement; a cross-family review then broke the first (vocabulary-grep) fix three ways — self-validating on the value's own token, vacuous keyword passes, and over-blocking real declinations in natural prose — so the check asserts a resolvable record instead of words. Also: standpoint axis gains `tier1b` (a STATIC read of a target repo, executed nothing) plus a decide-in-order procedure, after blind floor-tier sims graded pure cold-reads as `tier2` three rounds running; steel-quench Wave 1's sixth angle (gate-locality) gains the output-template row it never had, so a mandatory angle stops being structurally unreportable; verify-bidirectional gains category 5 (prescriptive doctrine statement); Sister Asset Protocol gains an active-adoption trigger; new resident doctrine — Mechanization Boundary, Local Execution First, Skeleton-not-Muscle, Expedition track, and this package's versioning policy. Hub meta-operations toolkit — 35 skills + 7 agents. New in 2.0.1: harness-doctor cadence hook, portability lint wired into pre-commit, branch_claim.sh claim-count-vs-tree-count warning, louder confidentiality-scan fail-open notice, fh-gate.sh missing-package.json survival, identity ① reclassified 🟢 (cross-harness adapters + relay argument channel). New in 1.4.53: `fh-codex-doctor` (npm bin) — Codex adapter drift scanner; reads the documented M1/M2/M3 skill tier map + skill/agent source and reports codex-native/adapter-required/claude-native/unclassified per unit, wired into `npm test`/`prepublishOnly` (fail-closed on unclassified Claude-native primitives). New in 1.4.49: steel-quench gains Step 0.6 Verdict-Invariance Probe (groundedness axis — a load-bearing judged gate's verdict must track behavior, not rubric phrasing; measured flip-count over cross-family paraphrases; arXiv:2605.06161 Policy Invariance anchor); multi_model_sidecar_strategy §Vendor-native harness (a model is strongest in its own vendor CLI — Claude/CC, GPT/codex, Gemini/Antigravity; a universal router degrades all of them, so it stays an autocomplete/QA sidecar, never orchestration); predelete_check.sh fail-closed rewrite; memory-hygiene A-TMA anchor. New in 1.4.48: phantom-quench + steel-quench gain external frontier anchors (arXiv:2607.02052 package-hallucination; arXiv:2607.02057 prompt-coverage-adequacy); README model-flat claim reframed from a per-release point-curve to structural invariants (operation flattens across tiers; depth tier-order fixed within a generation). New in 1.4.47: onboarding step ① surfaces the Mode D companion-store session-start load in the auto-read salience anchor (previously only in the local binding + rules, so a greeting could skip the load). New in 1.4.46: context-doctor command-output axis (route to rtk/proxy for verbose CLI stdout, complementing .claudeignore; risk-gated to token-scarce envs). New in 1.4.41: context-doctor 2026 trigger vocab (context engineering/rot/collapse) + phantom-citation hardening; hub measurement-integrity-checklist (cross-model measurement pre-flight: display-name pin/reps≥3/discriminating probe). New in 1.4.40: install-wizard queryable-wiki scaffold (INDEX + session-start read + R/W/C ingest). New in 1.4.39: auto-decorrelation (cross-family verifier sidecar recruitment) + video-ingest (capability-routed video ingestion). New in 1.4.x: verify-axis check-class taxonomy (mandatory-pass/measured/judged), no-reinvention Tier-0 inventory, 7-class failure taxonomy, Destructive-Op Gate, Wave-T (Temper), tier-floor governance, Mode D Model Notice, FC consent lane, default-Sonnet guidance. New in 1.3.0: public-surface-audit, field-harvest Mode B auto-trigger, 4-axis gate scope ext. Validated cross-CLI: Claude Code, Codex, Gemini.",
16
16
  "source": "./plugins/fh-meta"
17
17
  },
18
18
  {
19
19
  "name": "fh-commons",
20
- "version": "3.4.0",
20
+ "version": "3.5.0",
21
21
  "description": "Project-agnostic utility skills — 5 skills (convergence-loop · deliberation · mcp-circuit-breaker · token-budget-gate · ko-tech-writer) + 1 agent (quench-challenger). Domain-independent utilities transplantable into any project.",
22
22
  "source": "./plugins/fh-commons"
23
23
  },
24
24
  {
25
25
  "name": "fh-qp",
26
- "version": "3.4.0",
26
+ "version": "3.5.0",
27
27
  "description": "QP (Quality Platform) — the generic edition of a field QA harness's Prepare→Automation→Regression loop as an FH plugin: 4 skills (qp router · qp-plan · qp-run · qp-regress) + qp_tools.sh (target-class · adapter-probe · mask · surface-reach · mtm-check · run-verbs, typed exit codes) + a zero-domain-constant profile slot + 29 known-pair lanes. Drives web targets through the session's Playwright MCP and desktop targets through computer-use MCP (mobile deferred); calls a registered qasp typed capability when one exists (strictest-wins) — none is registered today, so the MCP fallback is the first edition. Verdict contract: a MACHINE closure requires a recorded assertion; a failed first step is attributed BLOCKED, not FAIL; surface_reach counts every TC in the denominator. Born as chamber run #18 (EMIT, 2026-09-05).",
28
28
  "source": "./plugins/fh-qp"
29
+ },
30
+ {
31
+ "name": "fh-preprep",
32
+ "version": "3.5.0",
33
+ "description": "preprep — a presentation-preparation harness. One CLI over 17 lanes (7 built-in + 10 module) that reads a declared surface manifest and checks a talk's assets against each other: canonical terms, jargon glosses, retired vocabulary, inter-slide dependencies, pacing, get/put round-trips, adjacent duplication, slide relations, geometry, attribute consistency, fonts, diagrams, slide refs, and screen-parity between manuscript and deck. Verdict contract: exit 0 pass / 1 findings / 2 cannot-judge — a surface that could not be read is UNMEASURED, never 0. Promoted out of fh-commons 2026-09-13 after the skill grew past one role (routing / lane modules / fixtures).",
34
+ "source": "./plugins/fh-preprep"
29
35
  }
30
36
  ]
31
37
  }
package/README.md CHANGED
@@ -101,7 +101,7 @@ cd ~/projects/forge-harness && claude # then type a greeting: hi · 안
101
101
  - You stop having to pick the check. The harness reads what you are about to do — publish, delete,
102
102
  rewrite history, open a PR — and names the gate for that moment. ① is one command you remember; ②
103
103
  is the layer that remembers for you.
104
- - **41 skills · 8 agents** you can call in plain language: diagnose a project, accelerate one, wire a
104
+ - **40 skills · 8 agents** you can call in plain language: diagnose a project, accelerate one, wire a
105
105
  new one up.
106
106
  - `tracks/` keeps what each session learned, so **session 2 starts where session 1 stopped**. This is
107
107
  the part that compounds — and the part you cannot judge on day one.
@@ -382,7 +382,7 @@ sidecar posture: [`docs/MODEL_SETUP.md`](docs/MODEL_SETUP.md).
382
382
 
383
383
  ---
384
384
 
385
- ## 41 skills · 8 agents
385
+ ## 40 skills · 8 agents
386
386
 
387
387
  Count = non-deprecated skills. Clustered as verification · orchestration · diagnosis · harvesting ·
388
388
  gates · discovery · simulation · setup, plus 8 agents (`challenger` · `quench-challenger` · `beginner`
@@ -64,7 +64,7 @@ Resolution order:
64
64
 
65
65
  | Unit type | Lookup |
66
66
  |---|---|
67
- | `--skill name` | `plugins/fh-meta/skills/name/SKILL.md`, then `plugins/fh-commons/skills/name/SKILL.md` |
67
+ | `--skill name` | `plugins/fh-meta/skills/name/SKILL.md`, then `plugins/fh-commons/skills/name/SKILL.md`, then **every other `plugins/*/skills/name/SKILL.md`** (alphabetical). Before 3.5.0 the search stopped after the first two, so skills in `fh-qp` and `fh-preprep` were unreachable by bare name. |
68
68
  | `--agent name` | `.claude/agents/name.md`, then `plugins/fh-meta/agents/name.md`, then `plugins/fh-commons/agents/name.md` |
69
69
  | `--agent plugin:name` | `plugins/plugin/agents/name.md` first |
70
70
  | `--unit path` | explicit file path |
@@ -2948,7 +2948,7 @@
2948
2948
  다음번 처방: 병렬 디스패치는 **완료 알림을 받은 그 턴에** 한 줄이라도 적는다.
2949
2949
  - date: 2026-09-01
2950
2950
  agent: fh-meta:beginner / fh-meta:challenger / general-purpose(디자이너 렌즈)
2951
- count: 3
2951
+ count: 5
2952
2952
  context: >-
2953
2953
  if(kakao) 덱 v1.1 비공개 사례 구간(68~75p) 재구축 직후 3렌즈 병렬 감사. 운영자가
2954
2954
  «발표준비하네스 능력을 100% 활용해서 … 테크니컬라이터도 부르고 디자이너도 불러서» 로
@@ -2997,7 +2997,7 @@
2997
2997
  신호: tracks/_meta/fh_signal_2026-09-02_dispatch-tally-attribution.md
2998
2998
  - date: 2026-09-02
2999
2999
  agent: general-purpose (2) + codex exec sidecar (1) — 프로 세션(Fable 5.1 거버너)
3000
- count: 3
3000
+ count: 5
3001
3001
  context: >-
3002
3002
  운영자 «자체개발 거버너모드 · 병렬로». ⓐ ⑤ 증폭자 측정 사전등록 초안(블라인드) ⓑ 카드 미결 2건
3003
3003
  (eligcheck _tsv_pipe fail-open · target_pin mtime 분기) 조사+패치 제안(패치는 텍스트로 회신,
@@ -3033,7 +3033,7 @@
3033
3033
  같은 디렉터리에 있어 오염 → 폐기·재실행. 전 회신을 거버너가 실행으로 재현했고 codex 기각 5건은 소스 근거.
3034
3034
  - date: 2026-09-03
3035
3035
  agent: UNATTRIBUTED — 이 세션(forge-harness-d5, 프로)이 띄운 것이 아니다
3036
- count: 2
3036
+ count: 5
3037
3037
  context: >-
3038
3038
  ④-e 가 «오늘 디스패치 2 · 원장 0» 으로 ❌. 자정 이후 이 세션의 Agent 호출은 0 (도구 호출은 Bash·gh·npm 뿐).
3039
3039
  tally 실물 `tracks/_meta/.subagent_dispatch_tally` 에 2026-09-03 두 줄. 같은 시각 이 머신에서 sim 추출기
@@ -3049,7 +3049,7 @@
3049
3049
  agent: fh-meta:expert
3050
3050
  invoker: forge-harness-27 (Fable 5.1 거버너, 야간 위임)
3051
3051
  task: 웹 QA 에이전트 프런티어(2025-2026) ↔ qasp 1/2/3막 대응표 + 2주 내 비저자 첫 완주 델타 5
3052
- count: 2
3052
+ count: 5
3053
3053
  context: >-
3054
3054
  운영자 «qasp 세계에 물어보고 개발 위탁, 다다음주 AX lobby QA 투입». 1차(기본 모델)는 API 529 로 조기 종료,
3055
3055
  2차 sonnet 재시도 성공(검색 12·fetch 1·155,866 tokens). 읽기 전용, 회사 내부 정보 검색어 배제.
@@ -3607,6 +3607,29 @@
3607
3607
  cost: tokens 46,877 · 거버너 UNMEASURED
3608
3608
  note: 🟥 부분 완주 — 한도 소진으로 중단(리셋 15:44), 남은 표면(run_pair.sh phase2 동시성) 미검. 자력 적발 0 — 내가 20분 전에 쓴 코드였다. 회사 데이터 0(FH 자기 스크립트)
3609
3609
  - date: 2026-09-11
3610
+ agent: general-purpose ×5 (Claude, 로컬) + sidecar-codex(gpt-6-astra high) ×7 호출(R3·R4×3·R5·R6×2·preprep R1 — 그중 REFUSED 1 · 한도 3)
3611
+ task: ① 3.2.0 발행 게이트 security-review(v3.1.4..main 18커밋, 출하 443파일) ② qasp-dev PR #286 루프 분모 게이트 검증(격리 워크트리·뮤턴트·합성 데이터) ③ PR #287 픽스처 상대시각(TZ×시각×지평 스윕 → 일중 뒤집힘 S 발견, BLOCK → 재검 승인) ④ PR #288 스탬프 이동(종료경로 전수·관통 프로브·합의 레인 단독 뮤턴트) ⑤ PR #289 머지 후 검증(실판정기 형제 오매치 S·준비점검 READY 무근거 S·장식 레인) · codex = finding 파이프라인 cross-family R3~R5(17+3건) + preprep 4 레인 감사 R1(S5·A9·B4)
3612
+ count: 5
3613
+ outcome: accepted
3614
+ evidence: security-review «No HIGH/MEDIUM»(후보 8 전부 «이미 닫힘» 기각) · #286 APPROVE + B1(규율 셋 잔여 2줄) · codex 라운드는 fh_axes23 마커 두 개(verifier-roster · preprep) 에 건별 기록
3615
+ cost: subagent_tokens 187k+181k · codex ≈ 116k(REFUSED)+130k(한도)+60k(한도)+116k+88k+… · 거버너 UNMEASURED
3616
+ note: 통합 1항목. 🟥 codex 사이버 필터가 «attack/spoof/bypass» 어휘 프롬프트를 116k 소모 후 거부 — REFUSED 는 0건이 아니다. 어휘 치환 후 같은 diff 로 8건 실적발. 🟥 «순차» 를 어겨 codex 잡 둘을 동시에 돌렸다가 한도(07:41)로 둘 다 죽음 — 이후 preflight 사슬로 순차화. 🟥 대기 사슬이 자기 명령줄을 grep 으로 잡아 영원 대기(«codex exec … model_reasoning» 문자열이 zsh -c 인자에 있다) — 파일 존재/토큰으로 판별해야 한다
3617
+ - date: 2026-09-12
3618
+ agent: general-purpose(opus) ×1 — preprep R8 «Opus 팔» (탈상관 A/B 의 같은 계열 팔, 격리 워크트리 wt_r8 @4f738ad, 실행 허용)
3619
+ task: codex R8 과 같은 프롬프트로 5파일 적대 감사. 12 프로브 스크립트 실행(p1~p12), R7 수리 3건 재검(S2·A4·A5 건전) + 1S·4A·7B
3620
+ count: 1
3621
+ outcome: accepted
3622
+ evidence: 거버너가 12 스크립트 중 8개를 재실행해 S1(rc=1 인데 정본 교체+이전 바이트 폐기)·A2(shape_type NotImplementedError 교착)·A3(intended 원소형 TypeError 로 P4 전체 UNMEASURED)·A4(공백 경계·br 오탐, «1자@Nonept» 거짓 증거)·B6(빈 Choice 진리값)·B7·B11 전부 재현. 전건 수리 대상
3623
+ cost: subagent_tokens 251,268 · 15분 · codex 팔은 QUOTA_HIT(UNMEASURED, 03:55 재발송)
3624
+ residual: A/B 판정은 codex 팔이 와야 성립 — 지금 말할 수 있는 것은 «같은 계열 + 실행» 이 0 이 아니라는 것뿐. 자력 적발 0/12
3625
+ - date: 2026-09-12
3626
+ agent: general-purpose(opus) ×1 — O_nosent 산문 24런 → typed 주장 추출(부록4 봉인 규칙)
3627
+ task: 답변 본문에서 «위치+결함» 단언을 1건씩, quote 축자 ≥15자 필수, 반증조건·파이프라인 메타 제외, 빈 런은 empty 기록
3628
+ count: 1
3629
+ outcome: accepted
3630
+ evidence: 94 주장 + 빈 7런 · quote 축자 검증 0 실패(에이전트 자체 기계 검증) · 컨트롤(축② 판정 수 대조) |차|>2 = 3런(g03 r1 빈 답 · g04 r3 · h05 r1) → 손대조 대상으로 기록
3631
+ cost: subagent_tokens 183,138 · 5분
3632
+ residual: 추출은 판단이다(F_gen 은 파이프라인 typed) — O 유리 방향 비대칭을 부록 4 잔여로 명시. 추출자 결정 2건(위치별 분할 · 자기부정 항목 제외) 그대로 채택
3610
3633
  agent: agy(gemini-3.1-pro-high · gemini-3.8-flash-high) · cross-family adversarial reviewer
3611
3634
  mode: sidecar
3612
3635
  context: "preprep L15 서체 일관성 레인 신설(PR #697, MERGED bffa1a0) 의 적대 검토. 4라운드 연속."
@@ -3647,3 +3670,65 @@
3647
3670
  dispatch_count_measured: 위 2026-09-11 엔트리에 통합 (클래스 통합)
3648
3671
  residual: "NOT-CONVERGED — 5라운드가 남았다"
3649
3672
  notes: "🟥 이 엔트리가 따로 있는 이유는 세션이 자정을 넘겼기 때문이다. ④-e 는 «오늘» 기준으로 세므로, 날짜 경계를 넘긴 세션은 양쪽 날짜에 기록이 필요하다 — 원장을 하루 단위로 읽는 계기의 성질이고, 통합 서술만 두면 오늘 자가 0 으로 보인다"
3673
+
3674
+ - date: 2026-09-12
3675
+ agent: Explore ×2 (local Claude) · general-purpose ×1 (local Claude) · agy(gemini) ×2 CLI
3676
+ mode: sidecar
3677
+ context: "clawd-on-desk «destructive-action reminder» PR — 필드 게이트 ②단(cross-family + 독립 출처)과 배선 지도"
3678
+ dispatched_by: governor
3679
+ outcome: accepted
3680
+ evidence: |
3681
+ 🟥 **자력 적발 0. 두 축이 서로 다른 HIGH 를 잡았고 겹치지 않았다.**
3682
+ ⓐ **독립 출처 팔**(general-purpose, owner 사양만 주고 체크리스트를 먼저 쓰게 함, 내 diff 서술
3683
+ 미제공) → **원격전용(버블 비활성) 경로가 통째로 스탬프 안 됨.** 알려진 짝으로 실측을 제시했고
3684
+ (`A 통상 stamp=hold` / `B 원격전용 stamp=undefined · sweep gate=true`), 내 «호출수 동일»
3685
+ 불변식이 그 형태를 **구조적으로 못 본다**는 것까지 지목(둘 다 없으면 수가 같다).
3686
+ ⓑ **계열 팔**(agy) → **`execute_bash`·`powershell`·`run_shell_command` 가 자동승인 적격인데
3687
+ 매처의 `SHELL_TOOLS` 에 없어 스캔 자체가 안 됨**(거버너가 3/3 재현). 추가로 과차단 3건
3688
+ (`--help` · `rm -rf dist/*` · automation-off 에서 거짓 «내가 멈췄다» 표시).
3689
+ ⓒ Explore ×2 → 설정 배선 6파일 순서·i18n 7로케일 패리티 테스트·라우트 테스트 헬퍼 지도.
3690
+ 그중 «수락 기준과 똑같은 단언이 이미 레포에 있다」(`server-route-permission.test.js:353`)가
3691
+ 테스트 설계를 바꿨다 — 새로 짓지 않고 그 형태를 재사용.
3692
+ tokens_subagent: "Explore 91,253 + 102,170 · general-purpose 211,666 · agy UNMEASURED · 거버너 UNMEASURED"
3693
+ dispatch_count_measured: 5 # 서브에이전트 3 + agy 2(1차는 print-timeout 5분에 절단 → --print-timeout 25m 재발주)
3694
+ residual: |
3695
+ 🟥 **codex 는 한도 소진**(15:44 창을 F_pair 사전등록 실험이 선점) ⇒ 외부 계열은 **agy 단독**.
3696
+ 그래서 `crossfamily: panel(gemini-agy) evidence=MIXED` 로 기록했다 — 계열은 SHARED(같은 diff),
3697
+ 출처는 INDEPENDENT(사양만). arXiv:2609.10969 의 40.9%p vs 11.3%p 를 읽고 의도적으로 축을 갈랐고,
3698
+ **이번 실측이 그 방향과 일치한다**(출처 팔이 내 불변식의 사각을 잡았고 계열 팔이 매처의 사각을 잡았다).
3699
+ ⚠️ n=1 PR 이므로 «출처 축이 더 세다» 로 일반화하지 않는다 — 겹침 0 이라는 사실만 기록한다.
3700
+ notes: "agy 1차는 기본 print-timeout 5분에 절단돼 출력 1줄이었다(메모리에 있던 함정 재현). `--print-timeout 25m` 로 해소. 모든 지적은 거버너가 소스에서 재현한 뒤 수용했고, 수리 11건 전부 fail-before 되돌림 프로브로 앵커 생존을 확인했다."
3701
+
3702
+ - date: 2026-09-13
3703
+ invoked_by: "FH 세션 773e7d1d (자율주행)"
3704
+ agent: "workflow: qasp-maturity-diagnostic (렌즈 9 + 건별 적대 반증)"
3705
+ purpose: |
3706
+ 운영자 요청 «qasp dev 를 전체적으로 성숙화» → FH §Field-Harness Diagnostic 의 9 렌즈를
3707
+ 워크플로로 팬아웃하고, 원시 발견마다 **반증 전용 팔 1개**를 붙여 살아남은 것만 랭크했다.
3708
+ 🟥 전 에이전트에 «쓰기 금지 · 웹 금지 · 제한 환경 리터럴 반출 금지 · 벤더 경로 제외» 를 박았다(제한 환경 레포).
3709
+ outcome: accepted
3710
+ evidence: |
3711
+ 렌즈 9(residency · split · salience · structure · degrade · loop · unwired · triad · reps)
3712
+ → 원시 발견 83 → 적대 반증 → **생존 48(M 3 · S 25 · R 20) · 탈락 35(42 %)**.
3713
+ 🟥 **M 3건이 서로 다른 렌즈에서 나왔고 겹치지 않았다**:
3714
+ ⓐ loop/residency — 무장된 가드 사본이 최신 보호경로 3건을 모른다(**유출 방향 fail-open, 현재 열림**).
3715
+ 설치 스냅샷 38 vs 소스 41. 훅 자신의 drift 통지는 경고만 내고 차단하지 않는다(의도된 설계).
3716
+ ⓑ degrade — 회귀 판정이 부분집합 공집합을 `all([])=True` 로 접어 결함을 **거짓 CLOSED**.
3717
+ 🟢 소비처가 이미 3값(`Optional[bool]`, None→보류)인데 호출부가 안 쓴다 = 순수 배선 결함.
3718
+ 형제 드라이버에는 같은 가드가 주석까지 달려 있다 — **반쪽-픽스 미전파**.
3719
+ ⓒ structure — 스킬 1개 Done-When 부재(레포 10개 중 유일). 성문 기준상 자동 M.
3720
+ 🟢 **탈락 35건의 이유를 전건 보존**했다 — 안 남기면 다음 진단이 같은 35건을 재발견으로 올린다.
3721
+ 🟢 **미측정 1건을 그 자리에서 닫았다**: R 등급 «origin 공개 여부 미측정» → `gh repo view` 로
3722
+ **PRIVATE 실측**. 그 칸이 잔류 판정 전부를 떠받치고 있었고, 확인되자 내부 호스트 리터럴 96건이
3723
+ 발견에서 빠졌다(설계상 정상). 🟥 렌즈의 처방(가시성을 fail-closed 게이트로)은 여전히 유효하다.
3724
+ tokens_subagent: "12,248,795 (92 에이전트 · tool_uses 483 · 벽시계 약 33분, 2회 실행)"
3725
+ dispatch_count_measured: 92
3726
+ residual: |
3727
+ 🟥 **계열 축이 비어 있다** — 전 팔이 같은 계열(Claude)이다. 제한 환경 레포라 외부 계열로 못 보낸다(잔류).
3728
+ `[[feedback_decorrelation_axis_is_what_you_send]]` 기준으로 **탈상관은 «렌즈»(관점)로만 걸렸고
3729
+ «계열»로는 안 걸렸다.** 그래서 계열 공통 사각은 이 진단이 구조적으로 못 본다.
3730
+ 🟥 **읽기 전용이라 「돌려보니」는 미측정**이다. M 중 둘(degrade·loop)은 실행 재현이 다음 단계다.
3731
+ 🟥 **렌즈당 상한 14건** — 넘은 렌즈는 뒤가 잘렸다(기록에 표시). 잘린 것은 «없다» 가 아니다.
3732
+ ⚠️ 1차 실행이 세션 한도로 27 검증 팔에서 죽었고 `resumeFromRunId` 로 재개했다 — 캐시 65 재생 + 27 재실행.
3733
+ **한도 소진이 「발견 0」으로 렌더될 수 있는 자리**였고, 워크플로가 failures 를 명시해서 갈렸다.
3734
+ notes: "발견은 전부 파일:줄로만 지목하고 제한 환경 값은 옮기지 않았다. 기록 = tracks/_meta/qasp_maturity_diagnostic_2026-09-13.md(gitignored). 아무것도 자동 수정하지 않았다 — 진단의 산출은 랭크된 목록이고 실행은 운영자 승인 뒤다."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrono-meta/fh-gate",
3
- "version": "3.4.0",
3
+ "version": "3.5.0",
4
4
  "description": "FH runtime adapters — run FH governance, skills, and agents via Claude or Codex with machine-parseable gates.",
5
5
  "homepage": "https://github.com/chrono-meta/forge-harness#readme",
6
6
  "bugs": {
@@ -28,6 +28,7 @@
28
28
  "fh-codex-doctor": "bin/fh-codex-doctor.js"
29
29
  },
30
30
  "scripts": {
31
+ "prepack": "find plugins -type d -name __pycache__ -prune -exec rm -rf {} + 2>/dev/null; true",
31
32
  "prepare": "chmod +x bin/fh-gate.js bin/fh-run.js bin/fh-goal.js bin/fh-codex-doctor.js scripts/fh-gate.sh scripts/fh-run.sh scripts/fh-goal.sh",
32
33
  "postinstall": "node scripts/postinstall_notice.js",
33
34
  "test": "bash scripts/selfcheck.sh",
@@ -75,6 +76,9 @@
75
76
  "scripts/version_lockstep_check.sh",
76
77
  "scripts/test_selfcheck_state_lanes.sh",
77
78
  "scripts/test_lane_runner_lanes.sh",
79
+ "scripts/usage_report.py",
80
+ "scripts/test_usage_ledger_lanes.sh",
81
+ "scripts/test_fh_run_resolver_lanes.sh",
78
82
  "scripts/test_version_lockstep_lanes.sh",
79
83
  "scripts/package_coverage_check.sh",
80
84
  "scripts/files_manifest_shipping_check.sh",
@@ -323,6 +327,9 @@
323
327
  "scripts/test_utterance_intake_lanes.sh",
324
328
  "scripts/worktree_reclaim.sh",
325
329
  "scripts/test_worktree_reclaim_lanes.sh",
330
+ "plugins/fh-preprep/.claude-plugin/plugin.json",
331
+ "plugins/fh-preprep/README.md",
332
+ "plugins/fh-preprep/skills",
326
333
  "plugins/fh-qp/.claude-plugin/plugin.json",
327
334
  "plugins/fh-qp/README.md",
328
335
  "plugins/fh-qp/qp_profile.example.yaml",
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "fh-commons",
3
- "version": "3.4.0",
3
+ "version": "3.5.0",
4
4
  "engines": {
5
5
  "claudeCode": ">=1.0.0"
6
6
  },
7
- "description": "Cross-project utility skill bundle — 6 skills + 1 agent. Domain-agnostic and portable: convergence-loop, deliberation, mcp-circuit-breaker, token-budget-gate, ko-tech-writer, preprep + quench-challenger agent.",
7
+ "description": "Cross-project utility skill bundle — 5 skills + 1 agent. Domain-agnostic and portable: convergence-loop, deliberation, mcp-circuit-breaker, token-budget-gate, ko-tech-writer, preprep + quench-challenger agent.",
8
8
  "author": {
9
9
  "name": "chrono-meta",
10
10
  "email": "chrono-meta@users.noreply.github.com"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fh-meta",
3
- "version": "3.4.0",
3
+ "version": "3.5.0",
4
4
  "engines": {
5
5
  "claudeCode": ">=1.0.0"
6
6
  },
@@ -1,5 +1,27 @@
1
1
  # forge-harness (fh-meta) Changelog
2
2
 
3
+ ### [3.5.0] — 2026-09-13 — preprep 를 자기 플러그인으로 승격 (`fh-preprep`)
4
+
5
+ **BREAKING (gate):** 스킬 경로가 **이동한다**.
6
+ `plugins/fh-commons/skills/preprep/` → `plugins/fh-preprep/skills/preprep/`
7
+ 🟥 **그 경로를 직접 부르던 스크립트·문서·설정은 깨진다.** 처방 = 경로 한 곳 치환:
8
+ `sed -i '' 's|fh-commons/skills/preprep|fh-preprep/skills/preprep|g' <파일>`
9
+ 플러그인으로 설치해 쓰는 경우(스킬 이름으로 호출)는 영향이 없다 — 이름은 `preprep` 그대로다.
10
+
11
+ **왜 옮겼나**: 스킬 하나가 레인 17개(내장 7 + 모듈 10)·픽스처군·오라클을 들면서
12
+ **역할이 갈렸다**(라우팅 / 판정 / 오라클). 규모가 아니라 역할 분기가 근거다.
13
+
14
+ - 새 `plugins/fh-preprep/` — 자기 `plugin.json` · README(종료코드 계약 명시) · `skills/preprep/`
15
+ - marketplace 항목 3 → **4**. 버전 락스텝 8개 문자열 전부 3.5.0
16
+ - `files[]` 에 `plugins/fh-preprep/{plugin.json,README.md,skills}` 등재 — 빼먹으면 조용한 출하 누락
17
+ - 드리프트 앵커·레인 12개·`field_canon_preload.sh`·`selfcheck.sh` 의 경로 참조를 같이 옮겼다
18
+
19
+ **승격 전에 닫은 것**(순서가 근거다 — 앵커 수리와 이동이 한 diff 에 섞이면 귀속이 안 갈린다):
20
+ ① #702 머지로 드리프트 앵커 초록 · ② 실물 14 표면 완주(rc=1 확정) 로 Done-When #1·#6 닫힘
21
+ (#705 에서 「설정을 못 읽음」이 `rc=1`(=발견 있음)으로 나가던 선재 결함을 `rc=2` 로 수리).
22
+
23
+ 🟥 **승격이 닫지 않는 것**: preprep Done-When #5(죽은 선언 12개)는 여전히 미충족이고 다른 축이다.
24
+
3
25
  ### [3.4.0] — 2026-09-12 — 탈상관의 «강한 축» 을 기계로 옮긴다 (외부 실측 기인)
4
26
 
5
27
  **BREAKING (gate):** 마커 두 자리가 조인다. 둘 다 **grace date 로 소급하지 않는다** — 그 날짜 이전
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "fh-preprep",
3
+ "version": "3.5.0",
4
+ "engines": {
5
+ "claudeCode": ">=1.0.0"
6
+ },
7
+ "description": "preprep — a presentation-preparation harness. One CLI over 17 lanes (7 built-in + 10 module) that reads a declared surface manifest and checks a talk's assets against each other: canonical-term ledger, jargon glosses, retired vocabulary, inter-slide dependencies, pacing, get/put round-trips, adjacent duplication, slide relations, geometry, attribute consistency, fonts, diagrams, slide refs, and screen-parity between manuscript and deck. Verdict contract: exit 0 pass / 1 findings / 2 cannot-judge — a surface that could not be read is UNMEASURED, never 0. Every lane ships a known-pair fixture.",
8
+ "author": {
9
+ "name": "chrono-meta",
10
+ "email": "chrono-meta@users.noreply.github.com"
11
+ },
12
+ "keywords": [
13
+ "presentation",
14
+ "deck-review",
15
+ "ooxml",
16
+ "pptx",
17
+ "terminology",
18
+ "screen-parity",
19
+ "known-pair",
20
+ "verdict-contract"
21
+ ]
22
+ }
@@ -0,0 +1,24 @@
1
+ # fh-preprep — 발표 준비 하네스
2
+
3
+ 기획서·원고·덱·노트가 **서로 어긋나는지**를 기계로 본다. 표면 목록(`surfaces.yaml`)이 단일
4
+ 소스이고, 코드에 경로를 안 박는다.
5
+
6
+ ```
7
+ python3 skills/preprep/preprep.py <설정.yaml>
8
+ ```
9
+
10
+ ## 종료코드 계약
11
+
12
+ | 값 | 뜻 |
13
+ |---|---|
14
+ | `0` | 통과 |
15
+ | `1` | 발견 있음 |
16
+ | `2` | **판정불가** — 계기 오류 · 미측정 |
17
+
18
+ 🟥 **`2` 와 `0` 을 섞지 않는다.** 선언된 표면이 디스크에 없으면 그 레인은 «0건»이 아니라
19
+ **UNMEASURED** 다. 설정을 못 읽어도 `2` 다(«발견 있음»인 `1` 이 아니다).
20
+
21
+ ## 왜 별도 플러그인인가
22
+
23
+ 스킬 하나에 레인 17개·모듈 10개·픽스처군이 들어가면서 **역할이 갈렸다** — 라우팅(스킬),
24
+ 판정(레인 모듈), 오라클(픽스처). 자세한 근거와 승격 비용은 승격 PR 본문에 있다.
@@ -63,10 +63,11 @@ model: sonnet
63
63
 
64
64
  | 운영자 예시 | 지금 | 무엇이 보나 |
65
65
  |---|---|---|
66
- | **위치가 어긋나 있다** | 🟢 덮인다 | `ooxml/gate.py` ⑥ 화면 밖 도형 ⚠️ 그 검사에 붙은 사고 수치(`26p · 1,854,400 EMU`)는 **`gate.py` 자신의 인라인 주석 말고 대조할 기록이 없다** — 거짓이라는 뜻이 아니라 독립 출처가 없다는 뜻이다 |
66
+ | **위치가 어긋나 있다** | 🟢 덮인다 | `ooxml/gate.py` ⑥ 화면 밖 도형 · P1/P3 (넘길 때 튐 · 인접 어긋남) · **P4 algn/mirror**(같은 줄인데 정렬이 갈림 · 좌우 대칭인데 크기가 갈림) ⚠️ 그 검사에 붙은 사고 수치(`26p · 1,854,400 EMU`)는 **`gate.py` 자신의 인라인 주석 말고 대조할 기록이 없다** — 거짓이라는 뜻이 아니라 독립 출처가 없다는 뜻이다 |
67
67
  | **번역투** | 🟢 덮인다 | ⑤에서 `ko-tech-writer` Step 2 |
68
68
  | **주어가 빠져 있다** | 🔴 **미구현** (판별자는 있다) | 🟥 «패턴만으로는 못 한다»고 적었던 것을 정정한다 — 판별자가 이미 실재한다: **주어 생략의 가부는 문장이 아니라 화면이 정한다.** 그 문장이 뜨는 화면에 참조 대상이 있으면 생략이 맞고, **참조 대상이 사라지는 장에서 복원**한다. 화면 텍스트는 이미 추출되므로 이건 judged 가 아니라 **measured 후보**다. 다만 복원/유지는 4등급으로 갈린다 (목적어=복원 · 주어+신정보=복원 · **주어+구정보=그대로 둔다** · SOV 재배열) — 전부 복원하면 번역투를 **새로 만든다**. 미구현 상태다 |
69
69
  | **화면이 너무 비어 보인다** | 🔴 **미구축** | gate.py 는 «밖으로 나갔나»를 보지 «안이 비었나»를 안 본다. 🟥 **정정 2026-09-01 — 「pptx→PDF 가 끊긴다」는 거짓이었고, 그 오진이 이 항목을 막고 있었다.** known-pair: `soffice` 부재는 **참**(`which soffice` → not found · LibreOffice.app 없음) · `pdftoppm` 실재는 **참**(`/opt/homebrew/bin/pdftoppm`) — **그런데 PowerPoint AppleScript `save as PDF` 로 120장 덱이 실제로 12회 렌더됐다**(`/tmp/v11*.pdf` 12개, 최신본 120페이지 `%PDF-1.3`, 2026-09-01 실측). ⇒ 끊긴 것은 **「LibreOffice 경로」**지 **「pptx→PDF」**가 아니다. 🟥 **미구축 사유가 바뀐다**: 「렌더가 안 된다」(장애물 실재) → **「기준선을 아직 안 쟀다」**(장애물 없음). 두 상태는 다르고, 전자는 «못 한다»로 후자는 «안 했다»로 읽힌다. [[feedback_rule_misdescribes_its_own_machine]] 기준선 형태는 정해져 있다: **레퍼런스 덱을 실측해 장 유형별 화면 자수 기준선을 세우고 미달 장을 센다** — 값은 발표마다 다시 재야 한다. 🟥 **정정 2026-09-04 — 기준선을 실제로 쟀다, 다만 반대편(과밀)이다.** 121장 한국어 덱 실측: 화면 자수 중앙 118 · 평균 133 · P90 207 · 최대 808. 읽는 부담(신규 글자 ÷ 발화 초) 중앙 10.0 · P90 16.9(§레인 R4·R5). ⚠️ 값은 발표마다 다시 재야 한다 — 임계를 코드에 안 박고 **그 덱 자기 분포**에서 뽑는 이유가 이것이다. 그리고 이건 게이트가 아니라 **순위표**다 — 정의상 항상 약 10%가 뜬다, 「후보 0건」을 목표로 삼으면 오독이다(§정직한 한계 참조). **「비어 보인다」쪽(하한)은 여전히 기준선이 없다** — 잰 것은 과밀(상한) 뿐이라 이 행은 아직 🔴 미구축으로 남는다 |
70
+ | **원고와 화면이 갈린다** | 🟢 **덮인다** (2026-09-10 신설) | **L14 screen-parity**. 그 전까지는 이 하네스의 정체(「표면 사이」)에서 **가장 큰 쌍이 비어 있었다** |
70
71
  | **흐름이 매끄럽지 않다** | 🟡 **절반 덮인다** | L8 은 «지시 대상이 해소되나»를 볼 뿐 «논지가 서나»를 못 본다. 🟢 그 부분집합 하나는 이제 덮인다 — **L9 progression** 이 «선언된 단계가 화면에서 빠졌나»를 본다(압축에서 첫 칸이 떨어지는 실사고). ⚠️ «논지가 서나» 자체는 여전히 미해결이고, 되읊음 대조(리뷰어가 요약한 뜻이 저자 의도와 반대인가)는 **사람이 한다** |
71
72
 
72
73
  🟥 **미구축 셋을 「advisory 라서 괜찮다」로 덮지 않는다.** 지금은 ④·⑤ 단계에서 **사람이** 하고
@@ -156,7 +157,7 @@ model: sonnet
156
157
  ## 쓰는 법
157
158
 
158
159
  ```bash
159
- SKILL=plugins/fh-commons/skills/preprep # 이 스킬의 위치
160
+ SKILL=plugins/fh-preprep/skills/preprep # 이 스킬의 위치
160
161
  cd "$ASSET_ROOT" # 발표 자산군의 루트
161
162
  cp "$SKILL/surfaces.example.yaml" surfaces.yaml # 표면 목록은 사람이 적는다
162
163
  python3 "$SKILL/preprep.py"
@@ -182,6 +183,7 @@ python3 "$SKILL/preprep.py"
182
183
  | **L10 adjacent-dup** | **인접 장이 같은 문장을 다시 읽나** — 쪼갤 때 뒤 프레임에 원본을 통째로 남기면 앞 장에서 읽은 문장을 또 읽는다. 실측 117자 중 82자(70%) | 차단 (임계는 사람이 적는다. 미기재면 측정만) |
183
184
  | **L9 progression** | **선언된 단계 중 하나가 화면에서 빠졌나** — 실사고: ①②③④ 중 ①이 화면에서 떨어져 리뷰어가 논지를 **정반대로** 읽었다. 개별 문장은 전부 옳았다 | 차단 (선언된 것만 본다) |
184
185
  | **L12 diagram** | **타입 JSON 도해가 «지금 JSON» 에서 validate 를 거쳐 구워졌나** — `kind: diagram_source` 표면의 굽기 영수증(`*.receipt.json`)으로 JSON 지문 일치 · showcase validate ok · PNG 실제 폭(IHDR 직독) ≥ 3840 · viewBox 폭 ≤ 720 · 여백. 실사고 후보 셋: JSON 고치고 PNG 안 구움 · 미검증 JSON 손렌더 · 픽셀은 충분한데 글자 12pt | 차단 (선언된 표면만 · 영수증 없는 손그림은 **UNMEASURED**, 0 아님) |
186
+ | **L14 screen-parity** | **원고의 화면 블록(🖥)과 덱 화면이 갈렸나** — `surfaces.yaml` 이 원고를 「화면 문자열의 정본」이라 선언해 놓고 **그 선언을 집행하는 레인이 없었다**. 실사고: 운영자가 PowerPoint 에서 7곳을 직접 고쳤는데 `preprep` 이 `rc=0` 을 냈다 — 덱은 새 문장, 원고는 옛 문장을 든 채로 통과 | 🟥 advisory · known-pair(덱 한 벌 · 원고 두 벌) — 🟥 **「짝 없는 줄」과 「문구만 다른 줄」을 갈라서 낸다**. 이 덱 실측 53/38건이라 «후보 0» 이 목표가 아니다 |
185
187
  | **L15 font** | **서체가 그 덱의 배포 템플릿이 정한 집합을 벗어났나** — `slides/` 안에서 «명시된» 서체만 판정하고, `slideLayouts`·`slideMasters`·`theme` 은 **템플릿과 해시가 같으면 상속분으로 보고 판정하지 않는다**. `fonts.allow` 미선언이면 **템플릿 자신의 `slides/` 분포에서 허용 집합을 유도**한다. 테마가 이탈이면 `+mn-*` 참조 전부가 거기서 오므로 **테마 한 곳만** 지적한다 | 차단 (🟥 `fonts.template` 미선언 = 상속과 저자 이탈을 **구분 불가** → 그 사실을 노트에 적는다 · 서체 토큰 0 = **UNMEASURED**, 「이탈 없음」 아님 · `fonts.intended` 로 선언한 것은 «면제됨»으로 출력 — 면제도 **가족 단위**다 · 🟥 렌더되지 않는 것은 안 센다: 빈 런 · `endParaRPr`(빈 문단 끝서식) · 테마의 `<a:font script=…>` 폴백) |
186
188
  | **G ooxml** | pptx 구조 9종 — 관계 참조 무결성 · 죽은 Content_Types Override · **화면 밖 도형** · 열린 도형 채움 · 선 굵기 토큰 래칫 | 차단 (`python3 ooxml/gate.py <풀린 pptx 트리>`) |
187
189
  | **R1 orphan-connective** | 선두 줄이 «그러나/그래서»로 뒤집는데 그 문장의 주어가 **직전 장 어디에도 없다** | 🟥 advisory · **실물 known-positive**(실제 백업에서 뜬 결함) |
@@ -189,8 +191,9 @@ python3 "$SKILL/preprep.py"
189
191
  | **R3 lead-term-unbacked** | 선두 줄이 **강조색으로 선언한 낱말**이 그 장 다른 어디에도 없다 — 화면이 자기 부제를 못 받친다 | 🟥 advisory · **합성 뮤턴트 known-pair**(자연 발생본이 파일로 안 남아 실물 2장을 줄여 만든 픽스처로만 계량됨 — R1·R2·P1·P3 과 증거 등급이 다르다) |
190
192
  | **R4 screen-heavy** | 화면 자수가 **이 덱 자기 분포**(P90)의 상위권 — 한 장에 너무 많이 얹었다 | 🟥 advisory · **순위표**(게이트가 아니다 — 늘 자기 덱의 상위 약 10%가 뜬다, 「후보 0」이 목표가 아니다) |
191
193
  | **R5 read-load** | **직전 장 대비 새로 뜬 글자** ÷ 발화 초 — 화면은 늘었는데 말은 짧다. 무대 지시([간지]/[진행])는 발화로 안 센다 | 🟥 advisory · **순위표**(R4 와 같은 성질) |
192
- | **P1 build-jitter** | 같은 이름 도형이 **연속한 두 장**에서 조금 다른 자리에 있다 — 넘길 때 튄다 | 🟥 advisory · **실물 known-positive** — 🟥 대조된 도형-쌍이 0 건이면 「튐 없음」이 아니라 **UNMEASURED**(이름 중복 도형은 대조 통째로 뺀다) |
194
+ | **P1 build-jitter** | 같은 이름 도형이 **연속한 두 장**에서 조금 다른 자리에 있다 — 넘길 때 튄다 | 🟥 advisory · **실물 known-positive** — 결박 내역을 **셋으로 갈라 낸다**(이름+글자 / 이름만(글자 없는 도형) / 글자 불일치=제외). 🟥 둘이 0 이면 「튐 없음」이 아니라 **UNMEASURED**. 의도된 연출은 `geometry.intended` 로 **선언**해서 면제하고, 면제된 것은 «면제됨»으로 출력된다. 🟥 **뒤집힘(flipH/flipV)은 임계와 무관하게 낸다** 상자가 EMU도 안 움직여도 화면에서는 화살표가 반대를 가리킨다(2026-09-10 실사고) |
193
195
  | **P3 adjacency** | 한 도형의 **오른쪽 끝**과 다른 도형의 **왼쪽 끝**이 맞닿으려다 어긋났다 | 🟥 advisory · **실물 known-positive** · 🟥 절대 목록으로 읽지 마라(기저 오탐 76건 실측) — `surfaces.yaml` 의 `geometry.baseline`(이전 판 pptx)을 주면 **편집 전/후 델타**로 돈다. **P2(near-miss, 같은 종류 모서리만 대조)는 미채택** — 구조적으로 실제 결함(다른 종류 모서리의 인접)을 못 잡아서다 |
196
+ | **P4 attr-consistency** | **덱 자신의 분포가 판정 기준이다** — 같은 역할끼리 묶어 «혼자 다른 것»을 낸다. 축 넷: `dash`(같은 점선인데 굵기가 갈림) · `algn`(같은 줄인데 정렬이 갈림) · `mirror`(좌우 대칭인데 크기가 갈림) · `echo`(**장을 가로질러** — 같은 문장이 매번 다른 크기·자리) | 🟥 advisory · known-pair 8팔(축 넷 × 양팔, 컨트롤 살아 있음) — 🟥 이 레인은 «다르다»만 본다, «틀렸다»는 사람이 판정한다. 의도는 `attr_consistency.intended` 로 선언 |
194
197
 
195
198
  🟥 **L15 의 본체는 검출이 아니라 «상속과 저작의 분리»다.** 순진하게 서체 이름만 세면 이 코퍼스에서
196
199
  **오탐 85건**이 나온다 — 그 85건은 전부 `slideMasters/` 안에 있고, **배포 템플릿의 마스터에도 정확히
@@ -287,10 +290,54 @@ Office 기본값(Arial·Calibri)을 흔히 들고 있어서, 강등하면 저자
287
290
  한 장을 지우자 다음 장 부제의 「그러나」가 받을 말을 잃었다. 삭제 전 **뒤 장의 전제인지**
288
291
  확인한다 — ⑤ 단계 규율.
289
292
 
293
+ ### ⓘ 이름이 겹치는 도형을 «순번»으로 집지 마라 — 자리로 결박해라
294
+ 2026-09-10 실사고. 한 장에 `a-a1` 이라는 **같은 이름의 화살표가 셋** 있었고, 그것들을 대칭으로
295
+ 맞추려고 `occurrence=0/1/2` 로 집어 자리를 배정했다. **순번을 잘못 짚어 `flipH` 가 엉뚱한 도형에
296
+ 남았고, 바깥 화살표 둘이 상자 «바깥쪽»을 가리켰다** — 모이는 그림이 벌어지는 그림이 됐다.
297
+
298
+ 🟥 **그리고 아무 계기도 안 잡았다.** 기하 레인은 그때 flip 을 안 읽었고(지금은 읽는다), 편집
299
+ 전/후 델타는 「새 어긋남 0건」을 냈으며, **렌더도 봤는데 내가 잘못 읽었다.** 운영자가 눈으로 잡아
300
+ 손으로 고쳤다. 계기와 눈이 같이 놓친 자리다.
301
+
302
+ ⇒ **규율**: 편집 대상을 이름으로 못 특정하면 **지금 자리(x, y)로 결박**한다. 순번은 XML 문서
303
+ 순서라는 «저자가 모르는 사실»에 기대고, 틀려도 값이 다 들어가므로 **집합으로는 옳아 보인다**.
304
+ 그리고 편집 뒤에는 **바꾸려던 그 도형이 바뀌었는지**를 이름이 아니라 자리로 되읽어 확인한다.
305
+ (P1 이 「이름은 필요조건이지 충분조건이 아니다」로 배운 것과 같은 교훈의 **편집 쪽** 얼굴이다.)
306
+
290
307
  ### ⓕ 「알고도 남긴 것」과 「몰라서 남은 것」을 갈라 적어라
291
308
  팔레트 밖 색을 정해서 남긴 자리가 있었다. 그렇게 적지 않으면 다음 감사자가 다시 «발견»한다
292
309
  (실측: 지운 색이 다른 색번호로 되살아나 있었다). 체크리스트 항목: `presentation_checklist.md` C6.
293
310
 
311
+ 🟥 **2026-09-10 — 이게 산문 규율만으로는 안 지켜졌고, 그래서 채널이 생겼다.** 그날 49p→50p 의
312
+ 격리 막대가 두꺼워지는 것을 P1 이 «튐»으로 냈고, 나는 그것을 «고쳤다». 운영자가 *"49~50으로
313
+ 격리 선이 두꺼워지는 거였는데 지금 그게 발생하지 않음"* 이라고 지적해서 되돌렸다.
314
+ **ⓕ 는 그때 이미 이 문서에 적혀 있었다** — 읽고도 안 걸렸다. 규율이 사람 기억에 얹혀 있으면
315
+ 다음 세션에서 또 새어 나간다([[feedback_half_fix_propagation_boundary]]).
316
+
317
+ ⇒ **선언은 이제 `surfaces.yaml` 이 받는다** — `geometry.intended` · `attr_consistency.intended`.
318
+ 세 가지가 규율이다:
319
+
320
+ ① 면제는 «도형»이 아니라 «그 변화»에 걸린다 선언한 속성 말고 다른 게 바뀌면 그대로 뜬다
321
+ ② `why` 가 비면 «면제»가 아니라 «오류»다 사유 없는 면제는 이 채널의 존재 이유를 지운다
322
+ ③ 면제된 것은 «면제됨»으로 출력된다 조용히 사라지면 다음 감사자가 다시 «발견»한다
323
+
324
+ 🟥 기계는 **«선언되었나»만 보고 «옳은가»는 안 본다**(CLAUDE.md §Mechanization Boundary —
325
+ 채널은 굳히고 판단은 안 굳힌다). 그러니 이건 «연출을 자동으로 알아본다»가 아니다. 못 알아본다.
326
+ 알아보는 것은 사람이고, 기계는 그 판단이 **다음 세션까지 살아 있게** 할 뿐이다.
327
+
328
+ ### ⓗ 「만들기」와 「설치」를 한 호출에 묶지 마라 — `safe_install.py`
329
+ 2026-09-10 실사고: 편집 스크립트가 assert 로 죽었는데 **뒤따르는 `cp` 가 그대로 돌아** 120장
330
+ 정본을 **102장으로 잘린 파일**이 덮었다(git 에서 복구). 근본 원인은 assert 가 아니라 **앞이
331
+ 실패해도 뒤가 도는 형태**다 — 셸에서 `;` 로 이은 줄은 전부 그 형태다.
332
+
333
+ python3 safe_install.py <후보> <정본> # zip 무결성 · 필수 부품 · 장 수 · 파일↔목록 일치
334
+ # · 복사 후 해시 재확인. 통과해야만 복사한다
335
+ ... --dry-run 검사만 ... --expect N 장 수를 못 박는다
336
+ ... --allow-shrink --why "<사유>" 의도한 축소(기본은 거부 — 비가역 표면은 fail-closed)
337
+
338
+ ⚠️ **구조만 본다.** 120장이 그대로 120장이면서 전부 빈 장이 된 경우는 통과한다 — 좁아진 것이지
339
+ 닫힌 게 아니고, 그 자리는 레인과 렌더가 본다.
340
+
294
341
  ### ⓖ 외부 가이드를 인용할 때 — «정의»는 «금지문»이 아니다
295
342
  같은 레퍼런스를 두고 두 판정이 갈렸다: 폰트 크기 「위반」을 유보했는데 원문에 **숫자가 한
296
343
  개도 없어서** 위반이 아닌 것으로 확정됐고, 색 「위반」은 스와치(정의)를 근거로 올렸다가
@@ -316,6 +363,31 @@ read-load)과 신규 기하 레인(build-jitter · adjacency)에 대한 캘리
316
363
  - **L8 오탐 진단(«저희»·«그대로»)은 이 한 덱에서 나왔다.** 다른 한국어 코퍼스에서 같은 비율인지는
317
364
  미측정.
318
365
 
366
+ ### 2026-09-10 추가분 (P1 결박 정정 · P4 신설)
367
+
368
+ - 🟥 **RETRACTED — 「P1 결박을 «이름 AND 글자»로 좁혀라」는 처방은 틀렸다.** 2026-09-10 인계 노트가
369
+ 그렇게 적었고 그대로 구현했는데, **원적 사건 재현 프로브가 그 자리에서 빨개졌다**: 35p→36p 의
370
+ 게이트 막대 `s5017` 은 **글자 없는 도형**이라 좁힌 결박이 P1 의 존재 이유였던 그 결함을 통째로
371
+ 잘라냈다. 오탐을 없애면서 자기 known-positive 를 같이 죽인 형태다.
372
+ **살아남은 처방은 좁히기가 아니라 «신뢰도로 가르기»**다 — 버리는 것은 «글자가 서로 다른 쌍»
373
+ 하나뿐이고, 글자 없는 도형은 **[이름만 결박] 이름표를 달고 후보로 남는다.**
374
+ ⇒ 인계 노트나 요약에서 「이름 AND 글자로 좁혔다」를 보면 **그건 낡은 줄이다.**
375
+ - **P1 결박 분포는 이 덱 한 벌 수치다** — 824쌍 중 이름+글자 425 · 이름만 334 · 불일치 65.
376
+ 다른 덱은 도형 이름 규약이 달라 비율이 통째로 달라진다. **인용하려면 자기 덱에서 다시 재라.**
377
+ - **P4 의 known-pair 는 reps=1 이고 생성 픽스처다.** 축 넷 × 양팔이 각각 한 번씩이고, 이 저장소
378
+ 자기 바(≥3) 미달이다. 다만 컨트롤이 죽지 않았음은 확인했다 — 두 팔의 도형 수·이름·자리가 같고
379
+ **갈리는 값 하나씩만** 다르다(negative 가 «볼 게 없어서» 조용한 것이 아니다).
380
+ - **P4 가 실물에서 잡은 것은 손검증 1건뿐이다.** 완성 덱에서 후보 16건이 떴고 그중
381
+ `71p [mirror]`(나란한 두 카드가 28pt ↔ 22/28pt)를 XML 로 직접 열어 **실재 확인**했다. 나머지
382
+ 15건은 미검증이다 — 「16건 잡았다」로 인용하지 마라.
383
+ - **`algn`·`mirror` 축은 키가 엄격하다**(정확히 같은 폭·같은 y / 허용 오차 안의 거울 자리).
384
+ 한 픽셀 어긋난 «같은 줄»은 구조적으로 안 잡힌다. 실측에서 이 덱은 `dash 0 · algn 0` 이었는데,
385
+ 그건 **「그런 결함이 없다」가 아니라 「이 키로는 못 본다」일 수도 있다.**
386
+ - **크기 축은 명시 `sz` 만 읽는다.** 플레이스홀더·테마 상속 크기는 XML 에 없어 **UNMEASURED** 다.
387
+ 실측: 도형 1,901개 중 명시 크기가 있는 것은 **990개(52%)** — 나머지 절반은 이 축의 사각이다.
388
+ - **`echo` 축의 오탐률은 높은 쪽이다.** 같은 문장이 요약 장에서 작게 다시 나오는 것은 정상이고,
389
+ 이 레인은 그것과 «실수로 갈린 것»을 **못 가른다**. 순위표로 읽고 선언으로 걷어내라.
390
+
319
391
  ## Done When
320
392
 
321
393
  | 조건 | check-class |