# Phase Z-2 — fit_classifier / overflow_router spec **Status** : v0 spec (2026-04-29). 정의만. 구현은 별도 step (사용자 승인 후). --- ## 0. 목적 / 위치 자동 파이프라인이 Selenium 으로 *detect 한* overflow / clipping 을 *어떤 pipeline action 으로 routing 할지* 결정하는 layer 의 spec. 현재 파이프라인은 detection 까지 정상 작동 (Selenium + debug.json 으로 신호 캡처). 그러나 detection 결과를 받은 직후 `sys.exit(1)` 으로 abort — **detection 과 action 사이의 decision layer 가 비어 있음**. ``` parse_mdx → align → composition (v0.2: capacity_fit) → render (Jinja2) ↓ Selenium visual_runtime_check ← 기존 (detection) ↓ 🆕 fit_classifier ← 신규 (사실 분류) ↓ 🆕 overflow_router ← 신규 (정책 결정) ↓ action : - zone_ratio_retry ← 신규 미구현 - layout_adjust ← 신규 미구현 - details_popup_escalation ← 신규 미구현 (CLAUDE.md 의
원칙 활성) - frame_reselect ← 신규 미구현 (V4 top-k 활용) - adapter_needed ← composition v0.1.1 partial - abort ← 기존 (현재 default) ``` **핵심 원칙** : classifier = *사실 분류* (이 overflow 가 어떤 종류인가), router = *정책 결정* (그 종류면 무엇을 할 것인가). 두 layer 분리 — 같은 분류가 context (retry 횟수 등) 에 따라 다른 action 을 요구할 수 있음. --- ## 1. fit_classifier 입력 schema ### 1.1 detection-side (기존 — 이미 캡처됨) | 입력 | 출처 | 비고 | |---|---|---| | `clipped_inner: [{class_name, excess_x, excess_y, scrollWidth/Height, clientWidth/Height}]` | `run_overflow_check` Selenium JS | ✅ | | zone 별 `overflowed`, `excess_y/x` | 같음 | ✅ | | slide / slide_body level overflow | 같음 | ✅ | ### 1.2 composition-side (기존 — composition v0.2) | 입력 | 출처 | 비고 | |---|---|---| | `unit.frame_template_id` / `contract_id` | composition + pipeline | ✅ | | `capacity_fit` (item count, fit_status) | `mapper.compute_capacity_fit` | ✅ (v0.2) | | `content_truncated_count` (zone 별) | pipeline 의 mapper 호출 후 | ✅ (v0.1.1) | | zone size (`height_px`, `min_height_px`, `content_weight`) | `compute_zone_layout` | ✅ | ### 1.3 신규 입력 (이번 spec 에서 정의) | 입력 | 비고 | |---|---| | `semantic_content_type` | className → 의미적 분류. §2 registry 참조 | | `line_equivalent` | excess_y / 해당 element 의 line-height (1 줄 단위로 환산) | | `structural_unit_drop_count` | structural_unit 중 *완전히 또는 부분적으로 잘린* 개수 | | `retry_budget_used` (router 의 상태) | 같은 slide 에 대해 router 가 이미 시도한 retry 횟수 | --- ## 2. className → semantic content_type registry | className 패턴 | semantic type | 설명 | |---|---|---| | `transform-block`, `transform-block__*`, `transform-row*` | `structural_unit` | paired comparison (AS-IS/TO-BE 한 쌍이 의미 단위). 행 단위 자르면 의미 깨짐 | | `text-line`, `text-line--bullet`, `text-line--indent-*` | `text_flow` | 자유 wrap, 줄 단위 자르기 가능 | | `*table*`, native `` | `tabular` | 행/열 단위 의미 — 행 잘리면 의미 손실 | | `f29b`, `f13b`, `f16b` (frame-family root) | `frame_internal` | frame 자체가 zone 안에 못 들어감 (zone level 문제) | | `*__cell`, `*__pillar`, `*__quadrant` 등 frame 내부 cell | `frame_internal_cell` | frame 내부 cell 단위 (cell 내부 content 가 cell 경계 초과) | | `*__title`, `*__section-title`, `*__banner`, `*__label` 등 | `frame_label` | 제목/라벨 단위 (text 와 비슷하지만 wrap 제약 있음) | | ``, ``, `*-bg` 등 | `visual_asset` | 시각 자산 (cropping 가능) | | 매칭 안 됨 | `unknown` | classifier 가 보수적으로 처리 (가장 안전한 action 선택) | 본 registry 는 신규 module (예: `src/phase_z2_classifier.py`) 의 상수 또는 catalog yaml entry 로 구현. --- ## 3. fit_classifier 출력 taxonomy ### 3.1 카테고리 정의 (계산 가능한 룰) | 카테고리 | 판정 룰 | |---|---| | `frame_capacity_mismatch` | composition 단계의 `capacity_fit.fit_status` ∈ {`strict_mismatch`, `exceeds_max`, `below_min`, `exceeds_truncate`}. → 이미 v0.2 가 잡고 있는 영역. 본 카테고리는 *post-render 검증 / 누락된 케이스 캐치* 용 | | `structural_major_overflow` | content_type = `structural_unit` 또는 `tabular` AND `structural_unit_drop_count` ≥ 1 (1 개 이상 *완전 단위* 잘림) | | `structural_minor_overflow` | content_type = `structural_unit` 또는 `tabular` AND `structural_unit_drop_count` < 1 (마지막 1 단위가 *부분만* 잘림, 즉 boundary spill) | | `tabular_overflow` | content_type = `tabular` (위와 별도 — 표는 행 1개라도 잘리면 popup) | | `layout_zone_mismatch` | content_type = `frame_internal` (frame root 자체 overflow) — zone 이 frame 을 못 담음 | | `moderate_overflow` | content_type ∈ {`text_flow`, `frame_label`} AND `line_equivalent` ∈ (1.5, 4] | | `minor_overflow` | content_type ∈ {`text_flow`, `frame_label`} AND `line_equivalent` ≤ 1.5 | | `hard_visual_fail` | 위 어디에도 매핑 안 됨 OR retry budget 소진 | ### 3.2 분류 우선순위 (위에서 아래로) 1. `frame_capacity_mismatch` (composition 결과 우선) 2. `tabular_overflow` (표는 즉시 popup 영역) 3. `structural_major_overflow` (1+ 완전 단위 잘림) 4. `layout_zone_mismatch` (frame root level) 5. `structural_minor_overflow` (boundary spill — 양 작음) 6. `moderate_overflow` 7. `minor_overflow` 8. `hard_visual_fail` (fallback) ### 3.3 핵심 구분 - **structural_minor vs structural_major** : 부분만 잘렸나 (`< 1` unit) vs 완전 단위가 잘렸나 (`≥ 1` unit). 부분 잘림은 zone 을 조금 더 주면 fit 가능. 완전 단위 잘림은 의미 손실 — popup escalation. - **structural vs moderate vs minor** : content type 이 *구조적 의미 단위* 인지 여부. 같은 px 양이라도 text_flow 는 minor, structural_unit 은 structural_minor 이상. --- ## 4. overflow_router action mapping | 카테고리 | action | retry budget | fallback (안 풀리면) | |---|---|---|---| | `minor_overflow` | `zone_ratio_retry` (양보 가능 zone 식별 → compute_zone_layout 재실행) | 1 | escalate → `moderate_overflow` 처리 | | `moderate_overflow` | `layout_adjust` (8-preset 중 다른 preset 검토 + zone ratio 재분배) | 1 | escalate → `structural_major_overflow` 처리 | | `structural_minor_overflow` | `zone_ratio_retry` (구조 자르지 않도록 zone 키움) | 1 | escalate → `structural_major_overflow` 처리 | | `structural_major_overflow` | `details_popup_escalation` (`
/` path) | N/A | popup 미구현 시 → `frame_reselect` → `adapter_needed` | | `tabular_overflow` | `details_popup_escalation` 또는 `frame_reselect` (table-friendly frame 후보) | N/A | 없으면 `adapter_needed` | | `frame_capacity_mismatch` | `frame_reselect` (V4 top-k rank 2+ 평가) | 1 | 없으면 `adapter_needed` | | `layout_zone_mismatch` | `layout_adjust` 또는 `zone_ratio_retry` | 1 | escalate → `frame_reselect` | | `hard_visual_fail` | `abort` (현재 sys.exit(1) 그대로) | — | — | --- ## 5. current code gap ### 5.1 이미 있어서 *재사용* 할 것 - detection (Selenium `run_overflow_check`) — clipped_inner / excess_y / className 모두 캡처 - composition v0.2 의 `compute_capacity_fit` (item count level) - composition v0.1.1 의 `adapter_needed` catch (mapper FitError) - debug.json 의 `slide_status` / `zones` / `candidates_summary` (입력 자료원) - 8-preset layout vocabulary + `compute_zone_layout` ### 5.2 새로 만들어야 할 것 | 신규 항목 | 비고 | |---|---| | **content_type registry** (§2) | className → semantic type. classifier 의 핵심 입력 | | **`fit_classifier` 모듈** | §1 입력 → §3 카테고리 | | **`overflow_router` 모듈** | §4 카테고리 → action | | `zone_ratio_retry` action 구현 | compute_zone_layout 의 retry path | | `layout_adjust` action 구현 | preset 동적 변경 | | `details_popup_escalation` 구현 | `
/` runtime + slot_payload 분리 룰 (큰 작업) | | `frame_reselect` 구현 | V4 top-k 사용 (rank-2+ 평가) | ### 5.3 정의 vs 구현 분리 본 spec 은 **정의만**. 위 신규 항목 중 어느 axis 부터 구현할지는 *별도 step* 에서 사용자 승인 후. 한꺼번에 다 만들지 X. --- ## 6. 본 spec 의 활용 — *visual fix 결정의 검증 자료* 본 spec 은 *룰북*. 향후 overflow 발생 시 : 1. classifier 가 *어떤 카테고리* 인지 결정 2. router 가 *어떤 action* 인지 결정 3. action 이 *현재 구현되어 있나* 확인 4. 미구현이면 → "본 spec 의 이 path 미구현이라 처리 불가" 로 명확히 보고 **중요 활용** : 누군가 (Claude 든 사람이든) "padding 줄여서 끼우자" 같은 fix axis 를 제시하면 — 이 fix 가 본 spec 의 어느 action 에도 매핑되지 않음 → **자동 반려**. 본 spec 을 검증 자료로 가지면 *symptom-silencing fix* 가 들어올 자리가 없어짐. --- ## 7. MDX 03 의 10px clipping 을 본 spec 으로 분류 (검증용 sample, fix 대상 X) > MDX 03 = sample instance. spec 룰의 *작동 검증* 용 — fix 대상 아님. ### 측정값 - excess_y = 10px (~0.6 줄, transform-row line-height 15.95px 기준) - clipped element className = `transform-block` 의 마지막 row (cell 내부) - semantic content_type = `structural_unit` (transform-row pair) - structural_unit_drop_count = 0.6 (1 개의 마지막 row 가 부분만 잘림 — 완전 단위 1 개가 아님) - composition `capacity_fit.fit_status` = `ok` ### 분류 적용 (§3.2 우선순위) 1. `frame_capacity_mismatch`? — capacity_fit ok → ✗ 2. `tabular_overflow`? — content_type 이 tabular 아님 → ✗ 3. `structural_major_overflow`? — drop_count `< 1` → ✗ 4. `layout_zone_mismatch`? — frame_internal 아님 → ✗ 5. `structural_minor_overflow`? — content_type = structural_unit AND drop_count `< 1` → **✓** **카테고리 = `structural_minor_overflow`** ### Action mapping 적용 (§4) → `zone_ratio_retry` (구조 자르지 않도록 F29 zone 을 더 키움) ### 현재 구현 상태 → `zone_ratio_retry` 는 **MISSING**. 따라서 본 spec 기준으로 MDX 03 은 *classifier 가 정상 작동하면 structural_minor_overflow 로 분류되고 zone_ratio_retry 로 routing 되어야 하는 case* 인데, *그 path 가 현재 미구현*. 따라서 정직한 상태는 `RENDERED_WITH_VISUAL_REGRESSION` 그대로 유지. ### 반례 검증 (이전 잘못된 fix) 이전에 시도했던 "transform-block padding 6→4 + transform-row padding 3→2" : - 본 spec 의 어떤 action 에도 매핑되지 X (`density_reduce` 같은 action 자체가 없음) - 따라서 본 spec 기준으로 **자동 반려되는 fix axis** — 들어올 자리 없음 이게 본 spec 이 *visual fix 결정의 검증 자료* 로 작동한다는 증거. --- ## 8. 다음 step (구현 우선순위 — 사용자 결정 영역) 본 spec 정의 후 구현 axis 후보 (사용자가 우선순위 결정): - A. content_type registry + fit_classifier (분류 layer) - B. overflow_router (정책 layer) - C. `zone_ratio_retry` action (가장 자주 트리거될 action) - D. `details_popup_escalation` (큰 작업 — 새 path) - E. `frame_reselect` (V4 top-k 사용 layer) 각 axis 는 *별도 step* 으로 한 단위씩. 한꺼번에 묶지 X.