# Phase Q Audit & Salvage **작성일**: 2026-05-08 (frontend 통합 session 직후 첫 번째 audit axis) **문서 역할**: Phase Z 보완 항목을 *기준 (lens)* 으로 기존 Phase Q 코드의 **Keep / Migrate / Reference / Delete** 결정. **진행 상태**: §1 Audit Lens 채움 — §2 모듈별 audit / §3 Salvage Plan / §4 우선순위 재정렬은 후속 turn 에서 채움. --- ## 0. 목적 Phase Z 가 fresh rewrite 처럼 진행되면서 Phase Q 의 기존 자산 (mdx_normalizer / section_parser / fit_verifier / slide_measurer / content_editor 등) 을 충분히 흡수하지 못한 부분을 정리한다. 이번 frontend 통합 session 에서 *frontend 가 임시로 채운 영역* (§7-B 표) 이 사실은 Phase Q 에 비슷한 코드가 있었을 가능성이 큼. **무엇을 새로 만들고 / 무엇을 가져오고 / 무엇을 참고만 하고 / 무엇을 버릴지** 결정하는 것이 본 audit 의 목적. 진행 방식: 1. **§1 Audit Lens** — Phase Z 보완 항목 (§7-B 의 A/B/D 그룹) 별 *목적 / input / output / Phase Q 후보 파일* 정리. 이게 audit 의 기준. 2. **§2 모듈별 Audit** — Phase Q 모듈 10 개를 § 1 의 lens 로 검토. 결정 = Keep / Migrate / Reference / Delete. 3. **§3 Salvage Plan** — §1 항목별 *어떤 Phase Q 자산을 어떤 방식으로 가져올지* 매핑. 4. **§4 우선순위 재정렬** — audit 결과로 [`PHASE-Z-ROADMAP.md`](PHASE-Z-ROADMAP.md) §5 / §7-B 우선순위 재조정. > §7-B 의 **C 그룹 (Frontend 일관성 정리)** 은 Phase Q 와 무관하므로 본 audit 대상 외. 별도 axis 에서 처리. --- ## 0-A. Phase Q Salvage 원칙 (lock) Phase Z 는 본체이고, Phase Q 는 부품 창고 / 참고 자산이다. Phase Q 의 *전체 흐름* 을 Phase Z 위에 덮어쓰지 않는다. Phase Z 의 22-step 도면 / step artifact / V4 catalog / frame contract / visual check / status board 가 *기준 구조*. Phase Q 의 빈 부분만 부품 단위로 가져온다. 본 audit / salvage 의 모든 결정 (§1 lens / §2 module / §3 salvage / §4 reorder) 은 아래 10 원칙을 따른다. 1. **Phase Z 22-step 구조는 유지한다.** 2. **Phase Q 코드는 Step 단위 빈칸을 채울 때만 가져온다.** 3. **가져올 때는 adapter 를 둔다.** 4. **기존 Phase Z artifact schema 를 깨지 않는다.** 5. **모든 migration 은 one-axis 단위로 한다.** 6. **MDX03 regression 을 매번 확인한다.** 7. **HTML-heavy MDX 는 신규 케이스로 별도 확인한다.** 8. **Reference 판정된 코드는 직접 연결하지 않는다.** 9. **Migrate 시 dual-write / shadow 검증을 먼저 한다.** 10. **Reference 항목은 활성화 axis 가 없으면 나중에 Delete 후보로 재분류한다.** > 꼬임 방지의 핵심 = **3 (adapter)** + **4 (artifact schema 보존)** + **9 (dual-write 검증)**. 나머지 7 개는 그 위의 안전장치. --- ## 1. Audit Lens — Phase Z 보완 항목별 목적 / input / output 각 항목은 [`PHASE-Z-ROADMAP.md`](PHASE-Z-ROADMAP.md) §7-B 와 1:1 매핑. | id | 보완 항목 | 목적 | input | output | Phase Q 후보 파일 | 우선순위 | |---|---|---|---|---|---|---| | **A-1** | Stage 0 normalize 통합 | HTML-heavy / 비정형 raw MDX 를 Phase Z canonical input 으로 변환 | raw MDX text | `{clean_text, title, images, popups, tables, sections}` (frontmatter / 코드블록 보호 / list/table HTML 변환 / AST 구조 추출) | `mdx_normalizer.py`, `section_parser.py` | 높음 | | **A-2** | Catalog 확장 (frame_contracts + frame_partials) | V4 32 후보 중 backend 적용 가능한 frame 수 증가 (현재 3 → 32 목표) | `figma_to_html_agent/blocks/{frame_id}/` 의 index.html / assets / analysis.md | `templates/phase_z2/catalog/frame_contracts.yaml` entry + `templates/phase_z2/frames/{template_id}.html` partial | `block_reference.py`, `block_selector.py` | 높음 | | **A-3** | Frame preview png 일관성 | 모든 catalog frame 의 일관된 preview.png 자동 생성 (현재 figma_previews 우회) | frame partial HTML + assets | `figma_to_html_agent/blocks/{frame_id}/preview.png` | `renderer.py`, `html_generator.py` (selenium 캡처 흔적 추정) | 중 | | **A-4** | slide-base.html iframe-friendly mode | iframe embed 시 body padding / centering / min-height 미적용 (frontend CSS injection 제거) | slide-base.html template + query string `?embedded=1` 같은 시그널 | conditional CSS (standalone vs embedded) | `html_generator.py` | 중 | | **A-5** | V4 후보 자동 fallback | rank-1 capacity / cardinality / structure mismatch 시 자동 rank-2/3 시도 | V4 후보 list + 각 frame contract 의 cardinality + 추출된 content items | 통과한 frame template_id (모두 fail 시 filtered_capacity) | `fit_verifier.py` | 높음 | | **A-6** | Zone DOM 좌표 export | backend 가 zone 절대 px 좌표를 step08 / 별도 step 에 export (frontend 측정 우회) | layout_css + slide-base 좌표 | `zone_geometries_px: [{position, x, y, w, h}]` | `slide_measurer.py` | 중 | | **B-1** | Zone-section assignment override | 사용자 drag drop 결과를 backend 가 받아 composition planner 의 자동 결정 강제 변경 | `--override-section-assignment ZONE_ID=section_id,section_id` (CLI multi) | units 배치가 사용자 매핑 따름 | `pipeline.py`, `content_editor.py` | 중 | | **B-2** | Edited HTML → MDX 역변환 | frontend 편집 모드의 텍스트 변경이 새 final.html 에 반영 | edited HTML (iframe contentDocument outerHTML) | 새 MDX text 또는 patched mapper input | 글벗 `fmt_slide.py html_to_slide_mdx`, `content_editor.py` | 중 | | **B-3** | Sub-section (### 단위) drag drop backend 처리 | backend 가 sub-section id 를 인식해서 zone 에 sub-section 단위로 매핑 | sub-section id (e.g., "03-1-sub-2") + zone_id | 그 sub-content 단위로 unit 분할 | `section_parser.py` | 낮 | | **B-4** | 다른 layout 의 zone-geometry override 확장 | top-1-bottom-2 / top-2-bottom-1 / left-1-right-2 / left-2-right-1 / grid-2x2 도 사용자 ratio override 적용 (현재 horizontal-2 / vertical-2 만) | `--override-zone-geometry` 인자 + 새 layout_preset 분기 | build_layout_css 의 grid 표현 (areas / cols / rows) | `space_allocator.py` | 낮 | | **D-1** | filtered_section_reasons 노출 UI | 사용자가 어떤 섹션이 왜 빠졌는지 즉시 인지 (Step 8 coverage UI) | `step20_slide_status.json.data.filtered_section_reasons` | frontend header / 패널 UI | N/A (frontend 만) — Phase Q audit 외 | 중 | | **D-2** | Zone resize 시 frame min_height 한계 표시 | pendingLayout 모드 resize 시 frame 한계 인지 + visual hint | frame_contracts.yaml `min_height_px` → step09 trace | frontend resize limit + 한계 도달 시 붉은 outline | `fit_verifier.py` (간접 참고) | 낮 | | **D-3** | 데모 / 업로드 흐름 시각 차별 | 자동 mdx03 vs 사용자 직접 업로드 구분 표시 | uploadedFile state 의 origin (auto-loaded vs user) | 헤더 / 좌측 패널 표시 | N/A (frontend 만) | 낮 | | **D-4** | Step 20 status badge 의미 안내 | PASS / RENDERED_WITH_VISUAL_REGRESSION / PARTIAL_COVERAGE 의 사용자 친화 설명 | runMeta.status | tooltip / popup | N/A (frontend 만) | 낮 | **우선순위 분포**: - **높음** (4): A-1, A-2, A-5 + (보고 후 결정) - **중** (6): A-3, A-4, A-6, B-1, B-2, D-1 - **낮** (4): B-3, B-4, D-2, D-3, D-4 **§2 audit 대상이 되는 Phase Q 후보 파일** (위 표에서 추출, 중복 제거): 1. `mdx_normalizer.py` (A-1) 2. `section_parser.py` (A-1, B-3) 3. `slide_measurer.py` (A-6) 4. `fit_verifier.py` (A-5, D-2 간접) 5. `space_allocator.py` (B-4) 6. `content_editor.py` (B-1, B-2) 7. `content_verifier.py` (검증 — B-2 후속) 8. `renderer.py` (A-3, A-4) 9. `html_generator.py` (A-3, A-4) 10. `block_reference.py` / `block_selector.py` (A-2) 11. (마지막) `pipeline.py` / `pipeline_context.py` (orchestration 큼 — 처음부터 보면 피곤. 1~10 보고 마지막에) --- ## 2. Phase Q 모듈별 Audit > 후속 turn 에서 1 모듈씩 채움. 형식 : > > ``` > ### 2.X {파일명} > - **역할** : ... > - **관련 §7-B 항목** : ... > - **현재 Phase Z 와 겹치는 영역** : ... > - **재사용 가능성** : ... > - **결정** : Keep / Migrate / Reference / Delete > - **후속 작업** : ... > ``` ### 2.1 `mdx_normalizer.py` #### 역할 Stage 0 — raw MDX 를 4 Layer 파서로 정규화하여 `{clean_text, title, images, popups, tables, sections}` 반환. `normalize_mdx_content(raw_mdx) -> dict` 단일 entry. 동반 `validate_stage0(result, raw_mdx) -> list[errors]` 검증 함수. | Layer | 역할 | 핵심 처리 | |---|---|---| | L1 | frontmatter 분리 | `python-frontmatter` 로 YAML metadata + title 추출 | | L2 | 코드블록 보호 + MDX 전용 패턴 처리 | (1) backtick 10→3 순서로 fenced code 보호 + inline code 보호 (placeholder 치환) → 후처리 후 복원. (2) `
` → popups 추출 + 마커 (`[팝업: 제목]`). (3) `:::note[...]` directive → `[핵심요약: ...]` 또는 `## 승격`. (4) **markdown list (`* / - `) → HTML `