Files
C.E.L_Slide_test2/PHASE-V.md
kyeongmin 1f7579cf64 Phase W + V' 완료: before→filled→after 파이프라인 + 조립 로직 수정
Phase W:
- weight 비율 초기 배정 (space_allocator header 높이 반영)
- block_assembler 공통 조립 함수 (filled/assembled 통합)
- filled → Selenium 측정 → context 저장
- sidebar overflow 확장 + body 재배분
- sub_layouts 사전 계산 (이미지 누락 해결)

Phase V':
- 팝업 링크 우측상단 배치 (인라인 → position:absolute)
- 표 내용 Kei 판단 (공란 크기 계산 → 행/열 산출 → Kei 요약)
- 출처 라벨 삭제 + 이미지 아래 캡션 배치
- after 공란 제거 (결론 바로 위까지 body/sidebar 채움)

추가:
- V-10 bold 키워드: 기계적 추출 → Kei 문맥 판단
- ** 마크다운 → <strong> 변환
- [이미지:] 마커 제거 (bold 변환 전 처리)
- grid-template-rows AFTER 크기 반영 (Sonnet final)
- assemble_stage2 CSS font-size override, white-space fix
- 하드코딩 전수 검토 완료
- 본심 여러 topic 텍스트 합침

Phase X 계획 문서 작성 (동적 역할 구조)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 05:00:52 +09:00

318 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase V (Verification) — 콘텐츠-컨테이너 적합성 검증 + Kei 에스컬레이션
> 작성일: 2026-04-02
> 근거: Phase T' 디버깅 과정에서 발견된 파이프라인 구조적 결함
> 선행: Phase T (파이프라인 구조), Phase T' (시각 품질)
---
## 배경
### 발견된 구조적 문제
Phase T' 디버깅 중 step-by-step 시각 검토를 진행하면서 다음이 드러남:
1. **컨테이너 크기가 콘텐츠 분량과 무관하게 결정됨**
- Stage 1.5a에서 weight(0.6, 0.2, 0.1, 0.1) 고정 배분
- 배경에 꼭지 2개(220자)가 배정되었으나 117px밖에 안 됨 → 넘침
- 본심은 345px인데 실제 필요 260px → 85px 남음
2. **"들어가는지" 검증 단계가 없음**
- 블록 선택(Stage 1.7) 후 바로 HTML 생성(Stage 2)으로 넘어감
- 콘텐츠가 컨테이너에 실제로 들어가는지 아무도 확인하지 않음
- Sonnet이 넘치는 내용을 받아서 overflow/스크롤/잘림 발생
3. **안 될 때 판단하는 주체가 없음**
- 공간 부족 시 옵션(합치기, 축약, 팝업 이동, 구조 변경)을 생성하고
- Kei 페르소나에게 결정을 요청하는 프로세스가 없음
- 현재는 그냥 Sonnet에게 "넣어라"만 함
4. **영역당 블록 1개만 선택됨**
- 배경에 꼭지 2개가 있어도 블록 1개(callout-warning)만 선택
- 1꼭지 = 1블록 원칙이 지켜지지 않음
---
## 절대 원칙
1. **하드코딩 금지** — font-size 외 모든 수치는 동적 계산. 어떤 MDX가 들어와도 동일하게 동작
2. **스크롤 절대 금지** — overflow:auto/scroll 어떤 영역에서도 불허
3. **1꼭지 = 1블록** — 컨테이너에 꼭지 N개면 블록 N개가 개별 선택
4. **콘텐츠 분량 → 컨테이너 크기** — weight 고정이 아니라 콘텐츠 필요 높이 기반 배분
5. **AI가 옵션 생성, Kei가 결정** — 안 될 때 하드코딩 대응이 아니라 Kei 판단 요청
---
## 개선된 파이프라인
```
기존:
1A → 1B → 1.5a(weight고정) → 1.5b → 1.7(영역당1블록) → 2(HTML) → 3 → 4
여기서 넘치거나 잘림
개선:
1A → 1B → 1.7(꼭지별1블록) → 1.8★(적합성검증) → 2(HTML) → 3 → 4
├ 필요 높이 계산
├ 컨테이너 재배분
└ 안 되면 → Kei 에스컬레이션
```
### 변경 사항 요약
| Stage | 기존 | 개선 |
|-------|------|------|
| 1.5a | weight 고정 배분 | 삭제 — 1.8에서 콘텐츠 기반 계산 |
| 1.7 | 영역당 블록 1개 | **꼭지당 블록 1개** |
| **1.8 (신규)** | 없음 | **적합성 검증 + 재배분 + Kei 에스컬레이션** |
---
## Stage 1.8: 적합성 검증 (신규)
### 입력
- 꼭지 목록 + 영역 배정 (Stage 1A/1B)
- 꼭지별 선택된 블록 + 블록 최소 높이 (Stage 1.7)
- 슬라이드 크기 (1280×720), padding, gap 등 고정 스펙
### 처리 흐름 (AI가 자동 — 하드코딩 아님)
```
Step 1: 필요 높이 계산
각 컨테이너별로:
- 배정된 꼭지들의 텍스트 분량(자수) 파악
- 해당 영역 font-size + line-height로 필요 줄 수 계산
- 선택된 블록의 padding, 제목, 마진 등 오버헤드 합산
- → 필요 최소 높이(px) 산출
Step 2: 슬라이드 공간 배분
720px 에서:
- header(고정) + footer(고정) + gap 빼기
- 남은 공간을 각 영역의 필요 높이 비율로 배분
- sidebar는 body와 같은 row이므로 sidebar 높이 = body 영역 합계
Step 3: 적합성 검증
각 컨테이너별로:
- 배분된 높이 ≥ 필요 높이 → 통과
- 배분된 높이 < 필요 높이 → Step 4로
Step 4: 재배분 시도
여유 있는 영역에서 부족한 영역으로 공간 이동:
- 각 영역의 (배분 높이 - 필요 높이) = 여유분 계산
- 여유분 > 0인 영역에서 부족 영역으로 재분배
- 재배분 후 모든 영역이 필요 높이 이상 → 통과
- 아직 부족 → Step 5로
Step 5: Kei 에스컬레이션
AI가 현황 + 시도 결과 + 옵션을 정리하여 Kei에게 요청:
[현황]
- 어떤 영역이 몇 px 부족한지
- 어떤 영역에 여유가 있는지
[시도 결과]
- 재배분으로 해결 가능한지/불가능한지
- 해결 가능하면 어떤 영역에서 얼마를 가져오는지
[옵션]
A. 꼭지 합치기 — 여러 꼭지를 하나의 블록 안에서 흐름으로 연결
B. 인라인 축약 — 사례 등을 괄호 한 줄로 축약
C. 팝업 이동 — 상세 내용을 팝업으로 빼고 링크만 남김
D. 컨테이너 재조정 — 다른 영역에서 공간을 가져옴
E. 그리드 구조 변경 — 배경 전체폭 등 레이아웃 자체 변경
F. 기타 (Kei 판단)
[결정 요청]
위 옵션 중 선택하거나 다른 방향을 제시해주세요.
```
### Stage 1.8 내부 루프
```
Step 1: 부족/여유 검증 (calculate_fit)
Step 2: 재배분 시도 (redistribute)
Step 3: 부족 시 → Kei 에스컬레이션 (call_kei_fit_escalation)
Step 4: 여유 시 → 보충 콘텐츠 탐색 (analyze_enhancements)
├ 관련 팝업에 구조화 콘텐츠(표/비교) 있으면 제안
├ 영역 핵심 결론 → 강조 블록 제안
└ 텍스트 핵심 키워드 → bold 목록 생성
Step 5: Kei 확인 (AI가 제안, Kei가 승인/수정)
Step 6: 보충 블록 선택 + fit 재검증
├ Kei가 승인한 보충 콘텐츠에 맞는 블록을 catalog에서 선택
├ 추가 블록의 높이가 여유 공간에 들어가는지 재검증
└ 안 들어가면 축소 (행 수 줄이기) 또는 제외
Step 7: 세부 컨테이너 배치 계산
├ 메인 컨테이너 안에서 세부 컨테이너 배치 (SVG/텍스트/표/key-msg)
├ 각 세부 컨테이너 크기를 콘텐츠에서 동적 계산
├ 빈 공간 측정 → 보충 콘텐츠 크기 결정 (표 행 수 등)
├ 세부 컨테이너 간 정렬 (좌우 높이 다르면 짧은 쪽 중앙맞춤)
└ 최종 overflow 검증
Step 8: 확정 출력
```
### 출력
- 확정된 컨테이너 크기 (재배분 반영)
- 각 컨테이너별 꼭지-블록 매핑 (Kei 결정 반영)
- 보충 블록 목록 (여유 공간에 추가된 블록)
- 강조 블록 목록 (핵심 결론용)
- bold 키워드 목록 (Stage 2 프롬프트에 전달)
- 콘텐츠 정리 방향 (합치기/축약/팝업 등 — Kei 결정 반영)
---
## 태스크 목록
### V-1: Stage 1.7 수정 — 꼭지별 블록 선택
- **현재:** `select_reference_block()`이 영역(배경/본심/첨부/결론) 단위로 1개 블록 선택
- **변경:** 각 영역 내 꼭지마다 개별적으로 블록 선택
- **파일:** `src/block_reference.py`, `src/pipeline.py` (Stage 1.7 호출부)
- **완료 기준:** 배경에 꼭지 2개 → 블록 2개 선택. 꼭지 1개면 블록 1개.
### V-2: Stage 1.8 신규 구현 — 적합성 검증
- **파일:** `src/fit_verifier.py` (신규)
- **내용:**
- Step 1~3: 필요 높이 계산 + 공간 배분 + 적합성 검증
- 모든 계산은 동적 (font-size, line-height, 블록 padding 등에서 도출)
- **완료 기준:** 배경 117px → 부족 감지 → 재배분 시도
### V-3: Stage 1.8 재배분 로직
- **파일:** `src/fit_verifier.py`
- **내용:**
- Step 4: 여유 영역 → 부족 영역으로 공간 재분배
- 재배분 후 결과를 Stage 2에 전달
- **완료 기준:** 배경 117→151px, 본심 345→311px 자동 재배분
### V-4: Stage 1.8 Kei 에스컬레이션
- **파일:** `src/fit_verifier.py`, `src/kei_client.py`
- **내용:**
- Step 5: 재배분으로도 안 될 때 옵션 생성 + Kei API 호출
- Kei 응답 파싱 → 결정에 따라 컨테이너/콘텐츠 조정
- **의존성:** V-2, V-3
- **완료 기준:** Kei에게 옵션 전달 → Kei 결정 수신 → 파이프라인 계속
### V-5: Stage 1.5a 리팩터
- **파일:** `src/space_allocator.py`, `src/pipeline.py`
- **내용:**
- 기존 weight 고정 배분 로직을 V-2의 콘텐츠 기반 계산으로 교체
- 또는 1.5a를 삭제하고 1.8이 컨테이너 계산을 전담
- **완료 기준:** weight 하드코딩 제거. 콘텐츠 분량 기반 동적 배분.
### V-6: 통합 검증 — 완료
- 전수 하드코딩 스캔: 레이아웃 하드코딩 0개
- 통합 테스트: 31/31 통과
- step-by-step HTML: step1~step3 생성 + 시각 검토
---
## Phase V-2: 콘텐츠 품질 강화 (Step 3 디버깅에서 발견)
> Step 3 시각 검토에서 발견된 4가지 개선 사항.
> 모두 "AI가 분석, Kei가 확인"하는 동일 프로세스.
### V-7: 주종 관계 블록 내 종속 꼭지 처리
- **발견:** 배경에 꼭지1(intro)+꼭지2(supporting) → 블록 1개로 합쳤지만, 종속 꼭지를 어떻게 표현할지 미정
- **규칙 (동적):**
- 종속 꼭지 콘텐츠 분량 확인 (fit_verifier의 텍스트 분량 계산 활용)
- 짧으면 (팝업 참조 1~2줄) → 인라인 (주 블록 안에 한 줄)
- 길거나 구조 있으면 → 하위 블록 (블록 안의 블록)
- 독립성 있으면 → 보조 블록 (나란히)
- **판단 기준:** 종속 꼭지의 source_data 길이 + 팝업 참조 여부 + purpose
- **Kei 확인:** AI가 "인라인/하위블록/보조블록" 중 제안 → Kei가 확인
- **파일:** `src/fit_verifier.py`, `src/block_reference.py`
### V-8: 여유 공간 콘텐츠 보충
- **발견:** 본심 컨테이너에 ~53px 여유. 관련 팝업(DX vs BIM 비교표 1135자)이 있는데 활용 안 됨
- **규칙 (동적):**
- 재배분 후 여유 공간 감지 (shortfall < -threshold)
- 해당 영역의 꼭지에 관련 팝업이 있는지 확인 (source_data에 [팝업:] 참조)
- 팝업에 구조화 콘텐츠(표, 비교, 목록)가 있으면
- 여유 공간에 맞는 블록 추가 선택 (catalog에서)
- 팝업 핵심만 요약하여 배치 제안
- **Kei 확인:** "53px 여유. 비교표 핵심 3행을 넣을까요?" → Kei가 확인
- **파일:** `src/fit_verifier.py`
### V-9: 영역 핵심 결론 강조 블록
- **발견:** 배경의 "체계적 정립 필요"가 단순 불릿과 동급. 시각적 강조 없음
- **규칙 (동적):**
- 각 영역의 꼭지 purpose에서 핵심 결론 추출
- purpose=문제제기 → 마지막 문장이 결론적 패턴("~필요", "~해야")이면 강조 블록
- purpose=핵심전달 → core_message와 관련된 문장이면 강조 블록
- 강조 블록: highlight-strip, callout 내 강조 div 등 catalog에서 선택
- **Kei 확인:** "이 문장을 강조 블록으로 처리할까요?" → Kei가 확인
- **파일:** `src/fit_verifier.py`, `src/block_reference.py`
### V-10: 텍스트 핵심 키워드 bold
- **규칙 (동적):**
- source_data에서 핵심 용어 추출 (꼭지 title에 포함된 키워드, **bold** 마크된 텍스트)
- 해당 키워드를 HTML 생성 시 bold 처리하도록 Sonnet에게 전달
- **파일:** `src/html_generator.py` (Stage 2 프롬프트에 키워드 목록 포함)
---
## 의존 관계
```
Phase V-1 (완료):
V-1 → V-2 → V-3 → V-4 → V-5 → V-6 ✅
Phase V-2 (신규):
V-7 (종속 꼭지 처리) ← fit_verifier 활용
V-8 (여유 공간 보충) ← fit_verifier 확장
V-9 (강조 블록) ← purpose 분석
V-10 (bold 키워드) ← source_data 분석
→ 전체 통합 검증 (step-by-step HTML 재생성)
```
---
## 하드코딩 전수 점검 결과
### 반드시 제거 (Phase V에서 동적 계산으로 교체)
| # | 파일 | 라인 | 값 | 문제 | 교체 방향 |
|---|------|------|-----|------|----------|
| 1 | `space_allocator.py` | 161 | `474` | body zone 높이 고정 | 슬라이드에서 header+footer+gap 빼고 동적 계산 |
| 2 | `space_allocator.py` | 160 | `0.35*0.85` | sidebar 비율+패딩 고정 | container_ratio + 실제 padding에서 계산 |
| 3 | `html_generator.py` | 543 | `720-80-66-footer_h-40` | body zone 높이 산술 하드코딩 | containers에서 받은 height_px 사용 |
| 4 | `html_generator.py` | 604,921 | `380` | sidebar width fallback | containers에서 받은 width_px 사용 (fallback 제거) |
| 5 | `html_generator.py` | 621 | `1200` | footer width fallback | containers에서 받은 width_px 사용 |
| 6 | `design_director.py` | 314 | `490` | FRAME_AVAILABLE_HEIGHT 고정 | 슬라이드 스펙에서 동적 계산 |
| 7 | `design_director.py` | 328-366 | `budget_px` 다수 | 프리셋별 zone budget 고정 | Stage 1.8에서 콘텐츠 기반 재계산 |
| 8 | `space_allocator.py` | 301,583 | `0.85` | 패딩 비율 고정 | `(slide_width - padding*2) / slide_width` 로 계산 |
### fallback 값 (정상 흐름에서는 도달하면 안 됨)
| # | 파일 | 라인 | 값 | 비고 |
|---|------|------|-----|------|
| 9 | `space_allocator.py` | 299 | `490` | zone_budget 기본값 — preset에서 반드시 와야 함 |
| 10 | `space_allocator.py` | 304,313 | `0.25` | weight 기본값 — Kei가 반드시 제공해야 함 |
| 11 | `pipeline.py` | 960 | `490` | budget_px fallback |
### 교체 원칙
- **모든 px 값:** 이전 Stage의 계산 결과(containers, font_hierarchy 등)에서 받아 사용
- **비율 값(0.85 등):** 실제 padding/gap에서 역산하여 계산
- **fallback:** 정상 흐름에서 절대 도달하지 않도록 이전 Stage에서 반드시 값을 제공
- **font-size만 예외:** 디자인 토큰으로 정의된 텍스트 크기는 하드코딩 허용
---
## 이전 Phase와의 관계
- **Phase T:** 파이프라인 Stage 0~5 구조 완성 → Phase V는 Stage 1.7~1.8 개선
- **Phase T' (TP-1~6):** 시각 품질 개선 (프롬프트, 후처리) → Phase V 적용 후 재검증 필요
- **Phase T' 후처리:** sidebar width:100%, overflow 제거, bold 변환, 폰트 캡 → 유지