Files
C.E.L_Slide_test2/docs/architecture/PHASE-Z-FIT-CLASSIFIER-ROUTER-SPEC.md
kyeongmin e7848b602d Add Phase Z runtime foundation
- add visual fit classifier, router, retry, and failure routing modules
- add composition planner and catalog-driven mapper
- add Phase Z pipeline orchestration and architecture docs
2026-05-04 08:21:28 +09:00

221 lines
11 KiB
Markdown

# 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 의 <details> 원칙 활성)
- 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 `<table>` | `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 제약 있음) |
| `<img>`, `<svg>`, `*-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` (`<details>/<summary>` 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` 구현 | `<details>/<summary>` 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.