# Phase W — Stage 1.8 before→filled→after 파이프라인 완성 > 작성일: 2026-04-03 > 근거: Phase V 이후 코드 조립/연결 과정에서 발견된 문제 8건 > 선행: Phase V (적합성 검증 + Kei 에스컬레이션) --- ## 배경 Phase V에서 설계한 before→filled→after 프로세스가 실제로 동작하지 않음. 코드는 부분적으로 존재하지만 연결이 안 되어 있고, 각 단계가 따로 놀고 있음. ### 발견된 문제 8건 1. **before→filled→after 파이프라인 연결 안 됨** — filled Selenium 측정 실패, 판단 미동작 2. **space_allocator 불안정** — weight 비율로 전체 공간 100% 사용해야 하는데 안 됨 3. **공통 조립 함수 불완전** — filled/assembled/stage_2가 각각 다른 코드 사용 4. **Kei 에스컬레이션 결정 미반영** — trim/popup/restructure 결정이 컨테이너에 반영 안 됨 5. **본심 OVERFLOW 미해소** — 재배분이 충분하지 않음 6. **filled 시각화 품질** — font_hierarchy, 카드 간격, 팝업 링크 등 미반영 7. **이미지 SVG 샘플 사용** — 실제 이미지(samples/images/) 대신 하드코딩 샘플 8. **Sonnet/코드 조립 경로 분리** — 두 경로가 각각 존재. Phase W 완성 후 판단 --- ## 핵심 프로세스 ``` before: weight 비중대로 전체 가용 공간 100% 배정 (초기 컨테이너) ↓ filled: before 컨테이너에 블록+텍스트 채움 (block_assembler 공통 함수) ↓ 측정: Selenium으로 실제 넘침 확인 ↓ 판단: 1. 다른 블록으로 바꿀 수 있나? (font_hierarchy 유지) 2. sidebar 넘침 → 세로 확장 (예외) 3. body 넘침 → 배경↔본심 재배분 4. 그래도 안 되면 → Kei 에스컬레이션 (trim/popup/restructure) 5. Kei 결정을 컨테이너에 실제 반영 6. 텍스트 85% 보존 우선 ↓ after: 조정된 컨테이너 ↓ assembled = after 기준으로 block_assembler 공통 함수로 최종 조립 ``` --- ## 절대 원칙 1. **하드코딩 금지** — 어떤 MDX가 들어와도 동일하게 동작 2. **결과물 HTML 직접 수정 안 함** — 파이프라인 프로세스를 수정 3. **이미지는 실제 파일만** — samples/images/에서 가져옴. SVG 샘플 금지 4. **텍스트 85% 보존** — 공간 부족 시 컨테이너를 늘림. 그래도 안 되면 Kei가 팝업 분리 5. **weight = 초기 배정 비율 + 충돌 시 우선순위** — 최종 높이가 아님 6. **배치는 시선 흐름** — 좌→우, 상→하 (배경 상단좌, 본심 중앙좌, 첨부 우측, 결론 하단) 7. **font_hierarchy 준수** — 핵심 14px, 본심 12px, 배경 10-12px, 첨부 9-11px 8. **조립 로직은 한 곳** — block_assembler.py. filled/assembled/stage_2 모두 이 함수 사용 9. **임의로 코드 되돌리지 않음** — 이해 안 되면 물어보고, 제안하고, 허락받고 실행 --- ## W-1: space_allocator — weight 비율 초기 배정 > 파일: `src/space_allocator.py` | Task | 완료 기준 | |------|-----------| | W-1-1 | `calculate_container_specs()`에서 zone_budget을 `전체가용 × zone_weight_sum / all_weight_sum`으로 계산하는 코드가 있음 | | W-1-2 | weight 합 1.0일 때 모든 컨테이너 높이 합 ≥ 전체 가용 공간의 95% | | W-1-3 | 배경이 본심보다 위에, 첨부가 우측에, 결론이 하단에 배치되는 좌표 계산이 `_calc_coords()`에서 동작함 | --- ## W-2: block_assembler — 공통 조립 함수 완성 > 파일: `src/block_assembler.py` | Task | 완료 기준 | |------|-----------| | W-2-1 | `assemble_role_html()`이 블록 CSS의 font-size를 font_hierarchy 값으로 override한 HTML을 반환함 | | W-2-2 | 팝업 `[팝업: 제목]` 마커가 별도 줄이 아니라 이전 불릿 텍스트 옆에 `[제목→]` 형태로 붙어있음 | | W-2-3 | sidebar 역할일 때 상단에 꼭지 title 라벨이 있음 | | W-2-4 | card-numbered에서 주불릿(indent=0)만 카드 제목, 하위불릿(indent=1)은 카드 설명으로 분리됨 | | W-2-5 | 카드 내 불릿 사이에 빈 줄(엔터) 없음 — white-space: normal 적용 | | W-2-6 | 이미지는 `ctx.slide_images`의 실제 파일(base64)만 사용, design_reference_html의 SVG 샘플 미사용 | | W-2-7 | `_gen_stage_1_8_filled()`와 assembled가 모두 `assemble_slide_html()`을 호출함 | | W-2-8 | Stage 0에서 팝업 콘텐츠의 마크다운 테이블이 HTML ``로 변환되어 `stage_0_context.json`의 popups에 저장됨 | --- ## W-3: filled → Selenium 측정 연결 > 파일: `src/block_assembler.py`, `src/pipeline.py` | Task | 완료 기준 | |------|-----------| | W-3-1 | `assemble_slide_html()` 출력 HTML에 `.slide` 클래스가 최외곽 div에 있음 | | W-3-2 | 각 역할 컨테이너에 `.area-body`, `.area-sidebar`, `.area-footer` 클래스가 있음 | | W-3-3 | filled HTML을 `measure_rendered_heights()`에 넣으면 `{'error': 'slide not found'}`가 아닌 정상 측정 결과가 반환됨 | | W-3-4 | steps 폴더에 `stage_1_8_fit_before.html` → `stage_1_8_filled.html` → `stage_1_8_fit_after.html` 순서로 생성되고, before의 컨테이너 크기 → filled의 넘침 → after의 조정된 크기가 시각적으로 확인 가능 | --- ## W-4: 측정 결과 기반 조정 판단 > 파일: `src/pipeline.py`, `src/fit_verifier.py`, `src/kei_client.py` | Task | 완료 기준 | |------|-----------| | W-4-1 | pipeline.py Stage 1.8에서 filled 측정 후 sidebar overflow 감지 시 해당 role의 컨테이너 height_px가 scrollHeight로 확장됨 | | W-4-2 | body overflow 감지 시 `redistribute()`가 호출되어 배경↔본심 간 높이가 재배분됨 | | W-4-3 | 재배분 후에도 overflow이면 `call_kei_fit_escalation()`이 호출됨 | | W-4-4 | Kei가 trim 결정 시 해당 role의 structured_text가 축약되거나, popup 결정 시 해당 콘텐츠가 팝업으로 분리됨 | | W-4-5 | Kei가 restructure 결정 시 해당 role의 컨테이너 height_px가 변경됨 | | W-4-6 | after의 컨테이너 크기가 stage_1_8_context.json에 저장됨 | | W-4-7 | Stage 1.8에서 `call_kei_enhancement_review()`가 호출되고, Kei 응답이 `enhancement_result`에 저장됨 | --- ## W-5: after 기반 최종 조립 + 검증 > 파일: `src/pipeline.py`, `src/block_assembler.py` | Task | 완료 기준 | |------|-----------| | W-5-1 | stage_2의 `generated_html`이 after 컨테이너 크기를 기준으로 생성됨 (stage_2_context.json의 containers == stage_1_8_context.json의 containers) | | W-5-2 | stage_3_rendered.html에서 overflow가 없음 (Stage 4 측정에서 모든 zone excess ≤ 0) | | W-5-3 | final.html에 모든 역할의 structured_text가 85% 이상 포함됨 | --- ## 의존 관계 ``` W-1 (weight 초기 배정) ↓ W-2 (공통 조립 함수) ← 독립적으로 병행 가능 ↓ W-3 (filled → Selenium 측정) ← W-1 + W-2 필요 ↓ W-4 (측정 기반 판단) ← W-3 필요 ↓ W-5 (after 기반 최종) ← W-4 필요 ``` --- ## 미결정 사항 - **Sonnet vs 코드 조립**: Stage 2를 Sonnet으로 유지할지 block_assembler 코드 조립으로 교체할지는 W-1~W-5 완성 후 결과를 보고 판단. --- ## 입력 데이터 - MDX 파일: `samples/mdx/01. 건설산업 DX의 올바른 이해(0127).mdx` - 이미지: `samples/images/` (DX1.png 등) - 테스트: `scripts/run_from_stage1b.py` — Stage 1B 데이터 고정 실행 - 좋았던 Kei 데이터: `data/runs/20260403_133746` (또는 `20260403_163508`)