@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
|
@@ -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-
|
|
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** — 🟥
|
|
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 |
|
|
@@ -12,8 +12,11 @@
|
|
|
12
12
|
"""
|
|
13
13
|
import os
|
|
14
14
|
from pptx import Presentation
|
|
15
|
-
from pptx.util import Emu
|
|
15
|
+
from pptx.util import Emu, Pt
|
|
16
16
|
from pptx.dml.color import RGBColor
|
|
17
|
+
from pptx.enum.text import PP_ALIGN
|
|
18
|
+
from pptx.enum.shapes import MSO_CONNECTOR
|
|
19
|
+
from pptx.enum.dml import MSO_LINE_DASH_STYLE as MSO_LINE
|
|
17
20
|
|
|
18
21
|
SLIDE_W, SLIDE_H = Emu(24384000), Emu(13716000)
|
|
19
22
|
ACCENT = RGBColor(0xFA, 0xE1, 0x00)
|
|
@@ -155,6 +158,146 @@ def geometry_baseline_pair(base_path, edited_path):
|
|
|
155
158
|
prs2.save(edited_path)
|
|
156
159
|
|
|
157
160
|
|
|
161
|
+
# ── P1 결박 (이름 AND 글자) known-pair ───────────────────────────────────────
|
|
162
|
+
def p1_binding(path):
|
|
163
|
+
"""네 도형 전부 **같은 크기의 소폭 이동**(+30,000 EMU, JIT 안)을 한다 — 즉 기하만 보면
|
|
164
|
+
넷 다 후보다. 갈리는 것은 **결박 가능성**뿐이다:
|
|
165
|
+
|
|
166
|
+
bound_bar 양쪽 글자 같음 → bound. 후보로 뜬다
|
|
167
|
+
silent_bar 양쪽 글자 없음 → name_only. 🟥 **후보로 뜬다** — 막대·선이 여기 산다.
|
|
168
|
+
P1 의 원적 사건이 바로 이 부류라 잘라내면 안 된다
|
|
169
|
+
remix_bar 양쪽 글자 다름 → mismatch. 다른 내용이 같은 이름을 입었다 → 제외
|
|
170
|
+
halftext 한쪽만 글자 있음 → mismatch. 같은 이유로 제외
|
|
171
|
+
|
|
172
|
+
🟥 이 픽스처의 핵심은 «이동량이 넷 다 같다»는 것이다 — 결박 규칙 말고는 변수가 없으므로
|
|
173
|
+
임계·이동량이 아니라 **결박 규칙만** 재는 계기가 된다."""
|
|
174
|
+
prs = _new_deck()
|
|
175
|
+
for k in (0, 1):
|
|
176
|
+
s = _blank(prs)
|
|
177
|
+
d = 30000 * k
|
|
178
|
+
_box(s, 'bound_bar', 1000000 + d, 1000000, 900000, 400000, '같은 막대')
|
|
179
|
+
_box(s, 'remix_bar', 1000000 + d, 2000000, 900000, 400000, '앞 도해' if k == 0 else '뒤 도해')
|
|
180
|
+
_box(s, 'silent_bar', 1000000 + d, 3000000, 900000, 400000, None)
|
|
181
|
+
_box(s, 'halftext', 1000000 + d, 4000000, 900000, 400000, None if k == 0 else '뒤늦은 글자')
|
|
182
|
+
prs.save(path)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def p1_intended(path):
|
|
186
|
+
"""선언 면제(`geometry.intended`) known-pair. 두 도형이 **같은 소폭 이동**을 한다.
|
|
187
|
+
|
|
188
|
+
staged_bar x 와 cx 가 함께 바뀐다 → `attrs: [x, cx]` 선언과 정확히 일치 → 면제 대상
|
|
189
|
+
stray_bar y 만 바뀐다 → 선언과 속성이 다르다 → **면제되면 안 된다**
|
|
190
|
+
|
|
191
|
+
🟥 둘째가 이 픽스처의 요점이다 — 면제를 «도형 이름»에 걸면 stray 도 삼켜진다."""
|
|
192
|
+
prs = _new_deck()
|
|
193
|
+
s1 = _blank(prs)
|
|
194
|
+
_box(s1, 'staged_bar', 1000000, 1000000, 900000, 400000, '격리')
|
|
195
|
+
_box(s1, 'stray_bar', 1000000, 3000000, 900000, 400000, '옆칸')
|
|
196
|
+
s2 = _blank(prs)
|
|
197
|
+
_box(s2, 'staged_bar', 970000, 1000000, 1030000, 400000, '격리') # x -30,000 · cx +130,000
|
|
198
|
+
_box(s2, 'stray_bar', 1000000, 3030000, 900000, 400000, '옆칸') # y +30,000 뿐
|
|
199
|
+
prs.save(path)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# ── P4 attr-consistency known-pair ───────────────────────────────────────────
|
|
203
|
+
def _sized(slide, name, x, y, w, h, text, pt, algn=None):
|
|
204
|
+
"""명시 `sz` 를 가진 상자. 🟥 P4 의 크기 축은 명시 sz 만 읽으므로 픽스처도 명시해야 한다
|
|
205
|
+
— 상속 크기로 만들면 known-positive 가 UNMEASURED 로 새서 계기가 죽는다."""
|
|
206
|
+
tb = slide.shapes.add_textbox(Emu(x), Emu(y), Emu(w), Emu(h))
|
|
207
|
+
tb.name = name
|
|
208
|
+
p = tb.text_frame.paragraphs[0]
|
|
209
|
+
if algn is not None:
|
|
210
|
+
p.alignment = algn
|
|
211
|
+
r = p.add_run()
|
|
212
|
+
r.text = text
|
|
213
|
+
r.font.size = Pt(pt)
|
|
214
|
+
return tb
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _dashed(slide, name, x, y, w, pt_w, dash):
|
|
218
|
+
ln = slide.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, Emu(x), Emu(y), Emu(x + w), Emu(y))
|
|
219
|
+
ln.name = name
|
|
220
|
+
ln.line.width = Pt(pt_w)
|
|
221
|
+
ln.line.dash_style = dash
|
|
222
|
+
return ln
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def p4(path, positive):
|
|
226
|
+
"""축 넷을 한 벌로. positive 는 넷 다 갈리고, negative 는 **같은 구조로 전부 일치**한다.
|
|
227
|
+
|
|
228
|
+
🟥 negative 를 «도형을 뺀 것»으로 만들지 않는다 — 그러면 「안 떴다」가 「볼 게 없었다」와
|
|
229
|
+
구분이 안 되고(이 저장소가 이름 붙인 「레인이 초록인 이유는 셋」의 ②), 죽은 컨트롤이 된다.
|
|
230
|
+
두 팔의 도형 수·이름·자리는 같고 **갈리는 값 하나씩만** 다르다."""
|
|
231
|
+
prs = _new_deck()
|
|
232
|
+
s = _blank(prs)
|
|
233
|
+
# dash — 같은 종류 점선 두 개, 굵기만 갈린다
|
|
234
|
+
_dashed(s, 'rule_a', 1000000, 1000000, 6000000, 1.0, MSO_LINE.DASH)
|
|
235
|
+
_dashed(s, 'rule_b', 1000000, 1600000, 6000000, 3.0 if positive else 1.0, MSO_LINE.DASH)
|
|
236
|
+
# algn — 같은 폭·같은 y 한 줄, 정렬만 갈린다
|
|
237
|
+
_sized(s, 'row_l', 2000000, 4000000, 5000000, 800000, '왼쪽 칸', 24, PP_ALIGN.LEFT)
|
|
238
|
+
_sized(s, 'row_r', 9000000, 4000000, 5000000, 800000, '오른 칸', 24,
|
|
239
|
+
PP_ALIGN.CENTER if positive else PP_ALIGN.LEFT)
|
|
240
|
+
# mirror — 화면 중심 기준 거울(폭 5,000,000 · 중심 ±5,192,000), 크기만 갈린다
|
|
241
|
+
_sized(s, 'mir_l', 4000000, 8000000, 5000000, 800000, '만든 쪽', 32, PP_ALIGN.CENTER)
|
|
242
|
+
_sized(s, 'mir_r', 15384000, 8000000, 5000000, 800000, '받는 쪽',
|
|
243
|
+
28 if positive else 32, PP_ALIGN.CENTER)
|
|
244
|
+
# echo — 둘째 장에 같은 문장, 크기만 갈린다
|
|
245
|
+
_sized(s, 'echo_1', 2000000, 11000000, 9000000, 800000, '되돌릴 수 없는 곳', 32, PP_ALIGN.LEFT)
|
|
246
|
+
s2 = _blank(prs)
|
|
247
|
+
_sized(s2, 'echo_2', 2000000, 11000000, 9000000, 800000, '되돌릴 수 없는 곳',
|
|
248
|
+
26 if positive else 32, PP_ALIGN.LEFT)
|
|
249
|
+
prs.save(path)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# ── P1 뒤집힘 known-pair ─────────────────────────────────────────────────────
|
|
253
|
+
def p1_flip(path):
|
|
254
|
+
"""두 장 · 상자 기하는 **한 EMU도 안 바뀐다**. 바뀌는 것은 flipH 하나뿐이다.
|
|
255
|
+
|
|
256
|
+
🟥 이 픽스처의 요점: 자리·크기만 보는 계기는 여기서 «어긋남 0» 을 낸다.
|
|
257
|
+
실사고가 정확히 그 모양이었다(화살표가 반대를 가리키는데 델타가 0건).
|
|
258
|
+
`steady` 는 양쪽 다 무플립이라 뜨면 안 된다(과차단 컨트롤)."""
|
|
259
|
+
from pptx.enum.shapes import MSO_CONNECTOR as _C
|
|
260
|
+
prs = _new_deck()
|
|
261
|
+
for k in (0, 1):
|
|
262
|
+
s = _blank(prs)
|
|
263
|
+
a = s.shapes.add_connector(_C.STRAIGHT, Emu(2000000), Emu(2000000),
|
|
264
|
+
Emu(2800000), Emu(2800000))
|
|
265
|
+
a.name = 'turned'
|
|
266
|
+
if k == 1: # 자리는 그대로, 방향만 뒤집는다
|
|
267
|
+
a._element.spPr.xfrm.set('flipH', '1')
|
|
268
|
+
b = s.shapes.add_connector(_C.STRAIGHT, Emu(6000000), Emu(2000000),
|
|
269
|
+
Emu(6800000), Emu(2800000))
|
|
270
|
+
b.name = 'steady'
|
|
271
|
+
prs.save(path)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
# ── L14 screen-parity known-pair ─────────────────────────────────────────────
|
|
275
|
+
def screen_parity(deck_path, man_ok, man_bad):
|
|
276
|
+
"""덱 2장 + 원고 두 벌. 🟥 덱은 **한 벌만** 만든다 — 원고만 바꿔서 「원고 쪽이 갈렸다」를
|
|
277
|
+
한 변수로 재기 위해서다(덱까지 같이 바꾸면 무엇이 신호인지 못 가른다).
|
|
278
|
+
|
|
279
|
+
man_ok 덱과 완전히 같다 → 짝 없음 0 · 문구 다름 0
|
|
280
|
+
man_bad ① 「사라진 줄」 을 원고만 들고 있다 → 짝 없음 (원고에만)
|
|
281
|
+
② 「받아들인다」 를 덱만 들고 있다 → 짝 없음 (화면에만)
|
|
282
|
+
③ 「증거 없이 반박을 믿는 셈이다」 ↔ 덱 「…믿는 셈」 → 문구 다름
|
|
283
|
+
"""
|
|
284
|
+
prs = _new_deck()
|
|
285
|
+
s1 = _blank(prs)
|
|
286
|
+
_box(s1, 'h1', 1000000, 500000, 14000000, 800000, '첫 장 제목')
|
|
287
|
+
_box(s1, 'b1', 1000000, 2000000, 14000000, 800000, '증거 없이 반박을 믿는 셈')
|
|
288
|
+
s2 = _blank(prs)
|
|
289
|
+
_box(s2, 'h2', 1000000, 500000, 14000000, 800000, '둘째 장 제목')
|
|
290
|
+
_box(s2, 'b2', 1000000, 2000000, 14000000, 800000, '받아들인다')
|
|
291
|
+
prs.save(deck_path)
|
|
292
|
+
|
|
293
|
+
open(man_ok, 'w', encoding='utf-8').write(
|
|
294
|
+
'### S1 · 첫 장 제목\n🖥\n첫 장 제목\n증거 없이 반박을 믿는 셈\n🗣\n말.\n\n'
|
|
295
|
+
'### S2 · 둘째 장 제목\n🖥\n둘째 장 제목\n받아들인다\n🗣\n말.\n')
|
|
296
|
+
open(man_bad, 'w', encoding='utf-8').write(
|
|
297
|
+
'### S1 · 첫 장 제목\n🖥\n첫 장 제목\n증거 없이 반박을 믿는 셈이다\n사라진 줄\n🗣\n말.\n\n'
|
|
298
|
+
'### S2 · 둘째 장 제목\n🖥\n둘째 장 제목\n🗣\n말.\n')
|
|
299
|
+
|
|
300
|
+
|
|
158
301
|
def build_all(outdir):
|
|
159
302
|
os.makedirs(outdir, exist_ok=True)
|
|
160
303
|
r1(os.path.join(outdir, 'r1_pos.pptx'), True)
|
|
@@ -166,7 +309,15 @@ def build_all(outdir):
|
|
|
166
309
|
r5(os.path.join(outdir, 'r5_pos.pptx'), True)
|
|
167
310
|
r5(os.path.join(outdir, 'r5_neg.pptx'), False)
|
|
168
311
|
p1(os.path.join(outdir, 'p1.pptx'))
|
|
312
|
+
p1_binding(os.path.join(outdir, 'p1_binding.pptx'))
|
|
313
|
+
p1_intended(os.path.join(outdir, 'p1_intended.pptx'))
|
|
314
|
+
p1_flip(os.path.join(outdir, 'p1_flip.pptx'))
|
|
315
|
+
screen_parity(os.path.join(outdir, 'sp_deck.pptx'),
|
|
316
|
+
os.path.join(outdir, 'sp_ok.md'),
|
|
317
|
+
os.path.join(outdir, 'sp_bad.md'))
|
|
169
318
|
p3(os.path.join(outdir, 'p3.pptx'))
|
|
319
|
+
p4(os.path.join(outdir, 'p4_pos.pptx'), True)
|
|
320
|
+
p4(os.path.join(outdir, 'p4_neg.pptx'), False)
|
|
170
321
|
geometry_baseline_pair(os.path.join(outdir, 'geo_base.pptx'),
|
|
171
322
|
os.path.join(outdir, 'geo_edited.pptx'))
|
|
172
323
|
return outdir
|