Files
C.E.L_Slide_test2/docs/architecture/PHASE-Z-CONTENT-OBJECT-SUBZONE-SPEC.md
kyeongmin 2ec8fc5a77 Add Phase Z Layer A planning scaffold
- add Internal Region model to Phase Z architecture docs and specs
- add frame contract content type and Frame Slot declarations
- add dormant content object extractor and internal region planner
2026-05-04 08:21:50 +09:00

850 lines
35 KiB
Markdown
Raw Permalink 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 Z-2 — content composition planning spec (Layer A: Internal Region + Layer B: Frame Slot)
**Status** : v1 spec (2026-04-30 refactor — Layer A / Internal Region 추가 + Layer B / Frame Slot 명확화). 정의만. 구현은 별도 step (사용자 승인 후).
> **v0 → v1 변경 요약**
> - Zone Internal Region (Layer A) 를 first-class entity 로 추가 (§2 신규)
> - 기존 §2 ~ §8 → §3 ~ §9 로 renumber (v0 의 ## 9 다음 step → v1 의 ## 10)
> - 기존 `sub_zone` 단어 = *Frame Slot (Layer B)* 의미로 일관 정리. *YAML 필드명 `sub_zones` 는 코드 reality 로 유지 — 의미만 명시*
> - placement algorithm (§4) 을 *2-stage* (Stage A: content → Internal Region / Stage B: Internal Region content → Frame Slot) 로 재작성
> - content_object schema (§1) / display strategy 어휘 (§5) / telemetry 구조 (§6) 는 *layer-agnostic 공유 개념* 으로 보호 — substantive 미변경
> - code / module / HTML marker 이름은 *implementation step* 으로 defer
---
## §0. 목적 / 위치
본 spec 은 **render *전* composition planning layer** 의 정의. fit_classifier / overflow_router / zone_ratio_retry 같은 *post-render telemetry* 가 아니라, *애초에 content 를 어디에 어떻게 배치할지* 결정하는 *진짜 fit policy 의 중심*.
```
1. PLANNING (composition) ← 본 spec 의 영역
- section raw_content → content_object 정규화
- Zone Internal Region (Layer A) 분할 — text/table/image/details 에 따라
- frame contract → accepted_content_types + Frame Slot (Layer B) 선언
- content_object → Internal Region → Frame Slot 배치 (compatibility 기반)
- inline preview vs details/popup 표시 전략
2. RENDER (Jinja2 + frame partial — Frame Slot aware)
3. POST-RENDER TELEMETRY (A1~A4) ← 별 spec, 이미 구축됨
- Selenium → fit_classifier → router → retry → failure_classifier → next_action
- 1 단계 (본 spec 영역) 가 정밀하면 거의 trigger 안 됨
- exception 케이스의 *진단 + 다음 capability 안내*
```
### Layer 구분
```
Slide → Zone → Internal Region → Frame → Frame Slot → Content
──────────── ──────────
Layer A Layer B
```
- **Layer A — Zone Internal Region** : Zone *내부* 영역, frame **. content type 기반 분할 (text region / table region / image region / details region). region 별 frame 또는 display strategy 선택.
- **Layer B — Frame Slot** : frame *내부* 자리 (= F13 의 pillar_1, F29 의 process_column 등). frame 안에서 content unit 이 들어갈 곳.
Layer A 와 Layer B 는 *별개 entity* 가 아니라 *한 composition pipeline 의 두 sub-phase*. content_object schema / display strategy 어휘 / telemetry interface 는 *공유*.
### 본 spec 의 핵심 원칙
- *render 전* 결정. *render 후 retry* 가 아님
- content_object 의 *type* 이 핵심 — text / table / image / diagram / details
- Layer A 가 *region 분할* 결정 / Layer B 가 *region 안 Frame Slot 매핑* 결정
- 매칭 안 되는 content 는 *details/popup 으로 escalate*. *원문 삭제 / 압축 X*
- frame 의 *Frame Slot* 이 어떤 type 을 받을 수 있는지 *명시적으로 선언*
본 spec 은 *정의만*. 구현 우선순위는 별도 step (사용자 승인 후).
---
## §1. content_object 정규화 schema
> **layer-agnostic** — Layer A / Layer B 둘 다 사용. v0 → v1 refactor 시 *substantive 미변경*.
MDX section 의 `raw_content` (markdown 문자열) 를 *typed content_object list* 로 정규화.
### 1.1 base schema
```yaml
section:
section_id: str
title: str
content_objects:
- id: str # section 내 unique
type: str # text_block / table / image / diagram / details / transform_table
role: str # summary / detail / decorative / reference
size_estimate:
line_count: int # text/details 의 경우
rows: int # table 의 경우
aspect_ratio: float # image/diagram 의 경우
bytes: int # raw payload 크기 (heuristic용)
raw_payload: str # 원본 (자름 / 변형 X)
type_specific: {...} # 아래 type 별 schema
```
### 1.2 type 별 schema
#### `text_block` — 자유 텍스트 / 불릿
```yaml
type: text_block
type_specific:
format: paragraph | bullet_list | nested_list
bullet_count: int # 불릿이면
max_indent_level: int
has_emphasis: bool # **bold** 등 inline emphasis
```
#### `table` — markdown 표
```yaml
type: table
type_specific:
rows: int # header 제외 데이터 row
cols: int
header_present: bool
is_transform: bool # AS-IS / arrow / TO-BE 구조면 true → transform_table 으로 분류
raw_md: str # 원본 markdown
```
#### `transform_table` — AS-IS / TO-BE pair (`table` 의 specialization)
```yaml
type: transform_table
type_specific:
pair_count: int # 행 수 (각 행 = 1 transform pair)
arrow_glyph: str # ➠ 등
rows: [{from: str, arrow: str, to: str}]
```
#### `image` — markdown / HTML 이미지
```yaml
type: image
type_specific:
src: str
alt: str
aspect_ratio: float | null # 알면 (asset metadata 에서)
intrinsic_width_px: int | null
intrinsic_height_px: int | null
```
#### `diagram` — SVG / 도식
```yaml
type: diagram
type_specific:
source_type: svg_inline | svg_file | mermaid | other
src: str | null
```
#### `details` — `<details>/<summary>` 또는 ":::note[...]" 같은 명시 marker
```yaml
type: details
type_specific:
summary: str # 펼치기 전 보일 헤더
body_raw: str # 펼친 후 content (자름 X)
display_hint: button | inline_collapse | popup # MDX 가 hint 줄 수 있음
```
### 1.3 role 의미
- `summary` — section 의 *핵심 메시지*. inline 으로 반드시 표시
- `detail` — 보조 / 부연. 공간 부족 시 details 로 escalate 가능
- `decorative` — 시각 보조 (배경 이미지 등). 공간 부족 시 *생략 가능*
- `reference` — 출처 / footnote / 보충 자료
### 1.4 정규화 parser 위치
신규 module (이름 *implementation step 에서 결정* — defer) :
- `extract_content_objects(section: MdxSection) -> list[ContentObject]`
- markdown AST parser (예: mistune) 활용 또는 regex 기반 v0
- 현재 `align_sections_to_v4_granularity` 다음 단계에 삽입
---
## §2. Zone Internal Region schema (Layer A — 신규)
본 section 은 **v1 신규**. Zone *내부* 영역 (frame **) 의 entity 정의 + 3-way decision tree + region 비율 + region → frame/display interface.
### 2.1 Internal Region entity schema
```yaml
zone:
zone_id: str
layout_position: str # top / bottom / left / right / ...
internal_regions:
- region_id: str # zone 내 unique
role: str # primary / secondary / supporting / reference
content_type: str # text / table / image / diagram / details / mixed
ratio_estimate: float # 0.0 ~ 1.0 (zone 내 비율, 합 = 1.0)
content_unit_ids: [str] # 이 region 에 배치된 content_object id 들 (Layer A → B 의 입력)
frame_match_strategy: # region → frame/display 매칭 결과
kind: str # frame_match | display_only
frame_id: str | null # frame_match 이면 실제 frame
display_strategy: str # inline_full | inline_preview_with_details | details_only | dropped
```
### 2.2 Universal Region Model
```
모든 Zone 은 1 개 이상의 Internal Region 을 가짐.
text-only zone = single-region zone (현 거동의 자연 표현)
mixed-content zone = multi-region zone
각 Internal Region 은 *자기만의* frame match + display strategy 를 가질 수 있음.
```
text-only section 도 *single-region zone* 으로 표현 (= 현 거동 보존). mixed-content (text + table / text + image / 등) 은 *multi-region zone* 으로 확장. region 이 *first-class entity* — special case 가 아님.
### 2.3 3-way decision tree
각 section 에 대해 *Internal Region 분할* 여부 결정 :
```
section 전체 → 1 frame 매칭 가능?
├ YES → whole-section frame match
│ → single-region zone (region 1개, content_type=primary)
└ NO → child-section grouping 가능?
├ YES → group merge → 1 frame 매칭
│ → single-region zone (region 1개, content_type=primary)
└ NO → content-type split
→ text region / table region / image region / details region
→ region 비율 산정 (예: text 80% / table 20%)
→ multi-region zone (region 2~N개)
```
**판단 기준** :
- *whole-section frame match* — section 전체와 frame contract 의 accepted_content_types 가 호환 + cardinality 가 맞는 경우
- *child-section grouping* — sibling section 들이 같은 frame contract (예: F16 의 4-quadrant) 와 묶이는 경우. heading depth + content 구조 + frame cardinality 로 판단
- *content-type split* — section 안에 *호환 안 되는 content type 조합* 이 있을 때 (text + table 처럼)
### 2.4 region 비율 산정
content type 별 *expected size* 기반 :
| content type | size proxy |
|---|---|
| `text_block` | line_count (text_block.size_estimate.line_count) |
| `table` | rows × line_height_factor (rows × 1.2 ~ 1.5) |
| `transform_table` | pair_count × pair_height |
| `image` | aspect_ratio 기반 height (width 고정 시) |
| `diagram` | aspect_ratio 기반 height |
| `details` | summary line_count (펼치기 전) |
*zone 내 합 = 1.0* 으로 normalize. role 가중치 (primary > supporting) 는 v1 에서 균등 — *별 step refinement*.
### 2.5 Internal Region Layout / Topology Vocabulary
region 들의 *공간 배치 패턴* 어휘. multi-region zone 의 *방향 / 배치* 결정. ratio 와 content_type 만으로는 *어떻게 배치되는지* 가 결정 안 되므로 *vocabulary 단계* 가 명시적으로 필요.
#### 명명 style — slide-level vs region-level 의 *의도된 비대칭*
- **Slide-level 8 vocabulary** (Step 7) = *count-based* 명명 (`horizontal-2`, `top-1-bottom-2`). zone 의 *layout-driven* 성격 반영
- **Region-level vocabulary** (본 §) = *descriptor-based* 명명 (`vertical-stack`, `main-support`). region 의 *content-type / role-driven* 성격 반영
- 이 비대칭은 *의도된 것*. region count 가 작고 (1~4) content type / role 이 핵심 결정 기준이라 descriptor 가 더 의미 전달
#### v1 vocabulary (6 entry)
| region_layout_type | 의미 | 사용 조건 |
|---|---|---|
| `region-single` | 1 region 만 (zone 전체 = region 1개) | region count = 1 (single-region zone) |
| `region-vertical-stack` | 위·아래 수직 stack | region count ≥ 2, content type 이 *순차적 흐름* (예: 본문 + supporting). default fallback |
| `region-horizontal-split` | 좌·우 수평 분할 | region count = 2, content type 이 *대등 비교* 또는 *side-by-side 시각* (text + image, text + diagram 등) |
| `region-main-support` | main region + supporting region (asymmetric ratio) | region count = 2, role = [primary, supporting], ratio asymmetric (예: 0.7 / 0.3) |
| `region-preview-details` | inline preview region + details/popup region | details_presence = true, 또는 큰 content (table N ≥ 5, long text 등) |
| `region-grid-2x2` | 2×2 grid (4 region) | region count = 4, content type 이 *대등 4 항목* |
#### deterministic decision rule
`region_layout_type` 은 AI 호출 X. 다음 *결정론적 함수* 로 도출 :
```
입력 :
- region_count : int
- content_type_mix : list[str]
- ratio_estimate : list[float]
- role 분포 : list[str] (primary / supporting / ...)
- details_presence : bool
결정 분기 (순차 적용, 첫 매칭 채택) :
1. region_count == 1
→ region-single
2. details_presence == true 또는 큰 content (table N ≥ 5 / long text 등)
→ region-preview-details
3. region_count == 4 AND content_type_mix 가 *4 종 대등*
→ region-grid-2x2
4. region_count == 2 AND role == [primary, supporting] AND ratio asymmetric (max / min ≥ 2)
→ region-main-support
5. region_count == 2 AND content_type_mix 내 visual element (image / diagram) 포함
→ region-horizontal-split
6. fallback (위 모든 분기 미매칭)
→ region-vertical-stack
```
#### 출력 schema
각 zone 의 `internal_regions` 컨테이너에 region_layout 필드 추가 :
```yaml
zone:
zone_id: str
internal_regions: [...]
region_layout:
region_layout_type: str # 위 6 entry 중 하나
region_order: [str] # region_id 의 배치 순서 (위→아래 / 좌→우 등)
region_placement: str # vertical | horizontal | grid | main-side | stack
```
#### 구현 위치 (예정)
신규 module (이름 *implementation step 에서 결정* — defer) :
- input : `zone.internal_regions` (from §2.1, ratio + content_type 산정 후)
- output : `zone.region_layout` (region_layout_type + order + placement)
- 위치 : §2.4 region 비율 산정 *직후*, §2.6 region → frame / display interface *직전*
- 결정 함수 deterministic — AI 호출 X
#### v1 vocabulary 의 한계 / 향후 확장 (참고)
- 현재 6 entry = v1 starting set. 추후 sample / frame DB 확장 시 vocabulary 추가 가능 (예: `region-vertical-3` / `region-horizontal-3` / `region-main-side-bottom` 등)
- v1 fallback (`region-vertical-stack`) 이 매칭 안 되는 패턴 발견 시 별 step 으로 entry 추가
### 2.6 region → frame / display 매칭 interface
각 region 은 다음 중 하나 :
- **frame_match** — region 의 content_type 에 호환되는 frame 선택. 매칭된 frame 의 contract 가 §3 의 입력. Stage B 진입
- **display_only** — frame 없이 display strategy 로 처리 (image area 직접 / table preview / details button). frame contract 미사용. Stage B 우회
현재 *runtime contract-registered / verified* frame set (F13 / F29 / F16) 은 모두 text region 만 수용. image / table / details region 은 현재 *display_only* path.
### 2.7 구현 위치 (예정)
신규 module (이름 *implementation step 에서 결정* — defer) :
- input : `section.content_objects` (from §1)
- output : `zone.internal_regions` (with ratio + frame_match_strategy) + `zone.region_layout` (from §2.5)
- 현재 composition planner 의 frame 매칭 *직전* 에 삽입
- region 분할 / 비율 산정 / topology vocabulary 선택 / 매칭 기준 — 모두 deterministic rule 기반 (AI 호출 X)
---
## §3. frame contract 확장 — accepted_content_types + Frame Slot (Layer B)
> **v0 의 §2 → v1 의 §3 (renumber)**. *Layer B / Frame Slot* spec.
`templates/phase_z2/catalog/frame_contracts.yaml`*2 개 신규 필드* 추가.
### 3.1 schema
```yaml
<template_id>:
... (기존 필드 그대로 — source_shape / cardinality / payload / visual_hints / ...)
# NEW : 이 frame 이 받을 수 있는 content type 들
accepted_content_types:
- text_block
- transform_table
- ...
not_accepted: # 명시적 비호환 (디버그용)
- image
- diagram
# NEW : frame 내부 Frame Slot 선언
# YAML field name = 'sub_zones' — 코드 / catalog reality 로 유지. 의미 = Frame Slot (Layer B).
sub_zones:
- id: str # Frame Slot 식별자
role: main_text | supporting_visual | label | details_button | ...
accepts: # 이 Frame Slot 이 받는 content_object type
- text_block
- transform_table
cardinality: # Frame Slot 내 capacity
strict: int # 정확히 N개
# or
min: int
max: int
partial_target_path: # frame partial template 에서 이 Frame Slot 의 위치
# 예 : "f29b__cell--left.row-1" — partial 안 marker (attribute name *implementation step 에서 결정*)
```
> **YAML field 이름** : 코드 / catalog reality 로 `sub_zones` 유지. 의미는 *Frame Slot* (= Layer B).
### 3.2 구체 예시 (현재 3 frame)
#### F13 — three_parallel_requirements
```yaml
three_parallel_requirements:
...
accepted_content_types: [text_block]
sub_zones: # = Frame Slots (Layer B)
- id: pillar_1
role: main_text
accepts: [text_block]
cardinality: { strict: 1 }
- id: pillar_2
role: main_text
accepts: [text_block]
cardinality: { strict: 1 }
- id: pillar_3
role: main_text
accepts: [text_block]
cardinality: { strict: 1 }
```
#### F29 — process_product_two_way
```yaml
process_product_two_way:
...
accepted_content_types: [text_block, transform_table]
sub_zones: # = Frame Slots (Layer B)
- id: process_column
role: main_text
accepts: [text_block, transform_table]
cardinality: { strict: 3 } # 3 sections per column
- id: product_column
role: main_text
accepts: [text_block] # product 쪽은 transform 안 받음 (현재 frame 의 시각적 구분)
cardinality: { strict: 3 }
```
#### F16 — bim_issues_quadrant_four
```yaml
bim_issues_quadrant_four:
...
accepted_content_types: [text_block]
sub_zones: # = Frame Slots (Layer B)
- id: quadrant_1
role: main_text
accepts: [text_block]
cardinality: { strict: 1 }
- id: quadrant_2
role: main_text
accepts: [text_block]
cardinality: { strict: 1 }
- id: quadrant_3
role: main_text
accepts: [text_block]
cardinality: { strict: 1 }
- id: quadrant_4
role: main_text
accepts: [text_block]
cardinality: { strict: 1 }
```
### 3.3 partial template 의 Frame Slot 마커
frame partial 의 HTML 에 Frame Slot 식별 marker 추가 — render 후 Selenium 이 Frame Slot 단위 측정 가능, A1~A4 의 정밀도 향상. *marker attribute name (예: `data-subzone` / `data-frame-slot` / 기타) 은 implementation step 에서 결정 — defer*.
---
## §4. placement algorithm — 2-stage (Layer A → Layer B)
> **v0 의 §3 → v1 의 §4 (renumber + 2-stage 재작성)**. *layer 순차 dependency*.
### 4.1 input / output
```
input :
section: { section_id, content_objects: [...] }
zone: { zone_id, layout_position }
available_frames: [...] # V4 top-k from Step 5
output :
internal_regions: [
{
region_id, role, content_type, ratio_estimate,
content_unit_ids: [...],
frame_match_strategy: { kind, frame_id, display_strategy },
# Stage B 결과 (frame_match region 만)
slot_assignments: [
{ content_object_id, frame_slot_id, display_strategy }
],
overflow_buffer: [...],
rejection: [...]
},
...
]
```
### 4.2 Stage A — content → Internal Region (Layer A)
> region 분할 결정 + content_object → region 배치.
```
1. 3-way decision (§2.3) 적용
- whole-section frame fit 가능 → single-region (Stage B 로 1 region)
- child-grouping 가능 → group merge → single-region
- content-type split 필요 → multi-region
2. multi-region 인 경우 :
- content_object.type 별로 region 분류 (text region / table region / image region / ...)
- 같은 region 안의 content_object 끼리 묶음
- region 별 ratio 산정 (§2.4)
3. region 별 frame_match_strategy 결정 :
- region.content_type 이 frame.accepted_content_types 에 매칭 가능 → frame_match
- 매칭 frame 없음 → display_only (image / table / details path)
4. 결과 : zone.internal_regions = [region_1, region_2, ...]
각 region 은 content_unit_ids + frame_match_strategy 를 가짐
```
### 4.3 Stage B — Internal Region content → Frame Slot (Layer B)
> region 의 content 를 frame 의 Frame Slot 에 배치. *frame_match_strategy.kind == "frame_match"* 인 region 에만 적용.
각 frame_match region 에 대해 :
```
1. content_object 정렬
- role 기준 우선순위 : summary > reference > detail > decorative
- 같은 role 내 raw_payload 등장 순서
2. content_object.type 이 frame.accepted_content_types 에 없는 것
→ rejection 으로 분리. 본 frame 부적합 신호 (Stage A 의 frame_match_strategy 재검토 신호)
3. 남은 content_object 를 Frame Slot 들에 배치
- 각 Frame Slot 을 순회 (frame contract 의 declaration 순서)
- Frame Slot.accepts 에 매칭되는 content_object 들에서
cardinality.strict 또는 max 수만큼 할당
- role 우선순위 높은 것부터
4. 배치 안 된 content_object
- role = decorative → 무조건 drop (생략)
- role = detail → overflow_buffer 로 (details/popup 후보)
- role = summary / reference → rejection (frame 부적합)
5. 결과 :
- slot_assignments : 정확히 무엇이 어디로
- overflow_buffer : details/popup 으로 escalate 할 candidate
- rejection : 본 frame 으로는 표현 불가 — frame_reselect 신호
```
### 4.4 매칭 충돌 / tie-break
Frame Slot 단위로 :
- 동일 Frame Slot 에 다수 content_object 후보 시 :
- role 우선순위 (summary > reference > detail)
- 동률 시 size_estimate 작은 것 우선 (fit 가능성 ↑)
- 동일 content_object 가 여러 Frame Slot 에 매칭 가능 시 :
- role 매칭 우선 (Frame Slot.role == content_object.role)
- 그래도 동률이면 contract declaration 순서 (앞쪽 Frame Slot 우선)
### 4.5 display_only region 의 처리
`frame_match_strategy.kind == "display_only"` 인 region 은 Stage B 우회. 대신 :
- image region → image area 직접 배치 (frame 없이, region 안에 직접 inline)
- table region → table preview (rows ≤ N inline) + 자세히보기 (rows > N popup)
- details region → details button + popup
- diagram region → diagram inline
display strategy 어휘는 §5 와 동일 — region-level 적용.
### 4.6 구현 위치 (예정)
신규 module (이름 *implementation step 에서 결정* — defer) :
- `plan_placement(section, zone, available_frames) -> Placement`
- composition planner 의 frame 매칭 *직후*, slot_payload 생성 *직전*
- Stage A → Stage B 순차 실행
- 결과를 slot_payload 생성 단계에 전달
---
## §5. 표시 전략 — inline preview vs details / popup escalation
> **v0 의 §4 → v1 의 §5 (renumber)**. **layer-agnostic** — region-level (Stage A) + slot-level (Stage B) 둘 다 적용. *어휘 미변경*.
### 5.1 결정 기준 (per content_object type)
| type | inline 가능 조건 | preview + details 전환 | popup-only 전환 |
|---|---|---|---|
| `text_block` | line_count ≤ Frame Slot capacity | line_count > capacity AND role=detail | role=detail AND line_count >> capacity (예: 20+) |
| `table` (rows N) | N ≤ 4 | 5 ≤ N ≤ 7 (preview 첫 N rows + details) | N ≥ 8 (popup-only) |
| `transform_table` | rows ≤ frame 의 transform Frame Slot capacity (보통 3) | rows > capacity, 일부 inline | rows >> capacity |
| `image` | aspect_ratio fit 가능 | 일부 frame 에서 inline + details 의 thumbnail | 거의 없음 (image 는 보통 inline 또는 drop) |
| `diagram` | Frame Slot 호환 | preview thumbnail + popup full | popup-only |
| `details` (already-marked) | inline 만 안 함 (정의상) | summary inline + body popup | summary + body popup |
### 5.2 *원문 손실 금지* 룰
- 표시 전략 결정은 *어디 보여줄지*. *content 자르지 / 압축 / 요약 X*
- inline preview 도 *raw_payload 의 일부* 만 빌려옴. 나머지는 details 로
- AI 호출 X — 모든 결정은 deterministic rule 기반
### 5.3 적용 layer
display strategy 어휘 (`inline_full` / `inline_preview_with_details` / `details_only` / `dropped`) 는 *동일* :
- **region-level** (Stage A 의 display_only region) — image area / table preview / details button 등
- **slot-level** (Stage B 의 frame_match region 안 Frame Slot 별 content) — Frame Slot 안 content 가 fit 안 되면 escalate
### 5.4 details / popup runtime
- frame partial 또는 region container 에 `<details>/<summary>` 또는 별 button + popup layer
- 단순 v0 : `<details>` 내장 — 클릭으로 펼침
- 향후 v1 : 별도 popup overlay (CLAUDE.md 의 자세히보기 원칙)
### 5.5 구현 위치 (예정)
placement planner (§4.6) 의 후속 단계 — 각 assignment / region 에 `display_strategy` 부착 :
- `inline_full` — content 전체 inline
- `inline_preview_with_details` — 일부 inline, 나머지 details
- `details_only` — summary 만 inline, content 는 popup
- `dropped` — decorative 가 공간 부족으로 생략
---
## §6. A1~A4 telemetry 와의 interface
> **v0 의 §5 → v1 의 §6 (renumber)**. *layer-agnostic*. *구조 미변경* — `sub_zone` 단어 mechanical rename + region-level metadata 추가.
본 composition layer 와 기존 telemetry layer (A1~A4) 가 *어떻게 흐르는지*.
### 6.1 forward flow (composition → render → telemetry)
```
section
↓ extract_content_objects
content_objects
↓ placement_planner (Stage A → Stage B)
placement {
internal_regions: [
{
region_id, content_type, ratio_estimate,
slot_assignments: [{content_object_id, frame_slot_id, display_strategy}],
overflow_buffer: [...],
rejection: [...],
}
]
}
↓ slot_payload 생성 (region + Frame Slot 단위로 grouping)
slot_payload (with region + Frame Slot metadata)
↓ render (frame partial — Frame Slot aware + region container aware)
HTML
↓ Selenium check
overflow signals
↓ A1 fit_classifier
categories
↓ A2 router
proposed_actions
↓ A3 retry / A4 failure_classifier / next_action
final_status
```
### 6.2 telemetry 에 전달되는 새 metadata
각 zone 의 debug entry 에 추가 :
```yaml
zone:
... (기존)
internal_regions: # Layer A
- region_id
content_type
ratio_estimate
frame_match_strategy
placement:
slot_assignments: [...] # 이 zone 에 어떤 content_object 가 어디 Frame Slot 으로
overflow_buffer: [...] # details 로 간 것
rejection: [...] # frame 부적합 후보
region_metrics: # Selenium 이 region 별로 측정 (Layer A)
- region_id
ch / sh / excess_y # region 단위 overflow
frame_slot_metrics: # Selenium 이 Frame Slot 별로 측정 (Layer B, frame_match region 만)
- frame_slot_id
content_object_id
ch / sh / excess_y # Frame Slot 단위 overflow
```
### 6.3 backward flow (telemetry → composition)
A4 의 `next_proposed_action``frame_internal_fit_candidate` 또는 `frame_reselect` 일 때 :
- composition layer 가 *재호출* 됨 (단, retry budget 별도)
- 다른 frame 또는 다른 placement 시도
본 v1 에서는 *backward flow 자동화 X* (구현 단계). placement 가 정확히 되어 있으면 telemetry 거의 trigger X.
### 6.4 fit_classifier 의 *content_type aware* 진화
현재 fit_classifier 는 *className → semantic_content_type* 매핑. 본 spec 적용 후 :
- Selenium 이 region marker / Frame Slot marker / content_object_id marker 를 읽음 (marker attribute name *implementation step 에서 결정* — defer)
- classifier 는 *content_object 의 type* 을 직접 알 수 있음
- 분류 정밀도 향상 (예: F29 의 frame_match region 안 Frame Slot 의 transform-block 이 transform_table content_object 임을 *직접* 알 수 있음 — 현재는 inner_content_signals 로 추론)
---
## §7. current code gap — 재사용 / 신규 분리
> **v0 의 §6 → v1 의 §7 (renumber)**. 신규 module 이름 *defer*.
### 7.1 이미 있는 것 (재사용)
- MDX parser : section 단위 (## / ### drilling)
- align_sections_to_v4_granularity
- composition planner (parent_merged_inferred 포함)
- frame_contracts.yaml + builder/parser registry
- mapper (catalog-driven slot_payload 생성)
- Jinja2 render
- 8-preset layout vocabulary
- A1~A4 telemetry chain
### 7.2 신규 필요
| 항목 | 위치 | 비고 |
|---|---|---|
| **content_object 정규화** | 신규 module (이름 *defer*) | markdown AST 또는 regex 기반 v0 |
| **Internal Region planner (Layer A)** | 신규 module (이름 *defer*) | 3-way decision + region 비율 + frame_match_strategy 결정 |
| **frame_contracts.yaml**`accepted_content_types` + `sub_zones` 필드 (= Frame Slot 선언) | catalog (기존 yaml 확장) | 3 frame (F13/F29/F16) 우선 |
| **placement_planner (Layer A → Layer B)** | 신규 module (이름 *defer*) | Stage A: content → Internal Region / Stage B: region content → Frame Slot |
| **display_strategy** 결정기 | placement_planner 내부 | inline_full / inline_preview_with_details / details_only / dropped |
| **frame partial 에 Frame Slot 마커** | `templates/phase_z2/families/*.html` | marker attribute name *defer* |
| **region container 마커** | `templates/phase_z2/slide_base.html` 또는 partial | region 단위 측정 marker, name *defer* |
| **details/popup runtime** | partial template 또는 base slide | `<details>` 우선, 추후 popup overlay |
| **fit_classifier 의 region / Frame Slot 인식** | `src/phase_z2_classifier.py` 확장 | inner_content_signals → region / Frame Slot 직접 read |
| **mapper 의 region / Frame Slot-aware slot_payload** | `src/phase_z2_mapper.py` 확장 | builder 들이 region + Frame Slot 그룹핑 인식 |
### 7.3 정의 vs 구현 분리
본 spec 은 *정의만*. 구현 axis 는 별도 step :
- B1. content_extractor (MDX → content_object 정규화)
- B2. internal_region_planner (Layer A — 3-way decision + 비율 + frame_match_strategy)
- B3. frame_contracts 의 accepted_content_types + sub_zones (= Frame Slot) 선언 (3 frame)
- B4. placement_planner (Layer A → Layer B 통합)
- B5. partial / region container marker 추가 + telemetry 연동 (이름 결정 포함)
- B6. details/popup runtime
각 axis 는 *별도 step*. 한 axis 씩 사용자 승인 후 진행.
> **module / marker / attribute 이름** : 본 spec 에서 *defer*. implementation step 에서 결정.
---
## §8. 본 spec 의 활용
> **v0 의 §7 → v1 의 §8 (renumber)**.
### 8.1 composition layer 의 룰북
향후 frame 추가 / content_object 변경 / Layer A 재분할 / Frame Slot 매핑 변경 시 본 spec 의 schema 를 따름. *임의 매핑 / hack 차단*.
### 8.2 telemetry 와의 cross-check
A1~A4 의 분류 결과 (`structural_minor_overflow` 등) 가 본 spec 의 placement 결과와 *일치하는가* 확인 가능. 불일치 = composition planning 의 *예상치 못한 케이스* — 진단 자료.
### 8.3 미사용 sample (MDX 01 / 02) 진단
본 spec 적용 후 MDX 01/02 를 돌리면 :
- 각 section 의 content_object 정규화 결과 visible
- 각 zone 의 Internal Region 분할 결과 visible (single vs multi)
- 어떤 content type 이 frame contract 에 없는지 (frame 추가 필요 신호)
- placement 의 rejection 비율 (frame coverage gap)
- overflow_buffer 의 details 후보 (popup runtime 필요 신호)
- display_only region 비율 (현재 frame DB 의 Layer A 미커버 영역)
이 정보가 *generalization validation* 의 진짜 신호.
---
## §9. MDX 03 의 case 를 본 spec 으로 검증 (illustrative)
> **v0 의 §8 → v1 의 §9 (renumber)**. mechanical rename + 2-stage 표현.
> MDX 03 = sample. *fix 대상 X*. 본 spec 룰의 *예시 적용*.
### 9.1 03-1 의 content_object 정규화 (예상)
```yaml
section_id: "03-1"
title: "1. DX 시행을 위한 필수 요건"
content_objects:
- id: "03-1.text-1"
type: text_block
role: summary
type_specific: { format: nested_list, bullet_count: 3 (top), nested_count: 7 }
size_estimate: { line_count: ~12 }
```
→ 1 content_object (text_block, role=summary).
### 9.2 03-1 의 Stage A → Stage B (F13 contract 적용)
**Stage A** :
- 3-way decision : section 전체가 F13 (3 pillars) 의 child grouping 으로 매칭 → *whole-section frame match*
- single-region zone, content_type=text, ratio=1.0
- frame_match_strategy = { kind: "frame_match", frame_id: "F13" }
**Stage B** :
- F13 sub_zones (= Frame Slots) : [pillar_1, pillar_2, pillar_3] (각 cardinality strict 1, accepts text_block)
- text_block 1 개 → 3 Frame Slot 에 어떻게 배치?
- 현재 mapper (`pillar_item` parser) 가 *implicit* 으로 top_bullet 3 개를 3 pillar 에 분배
- 본 spec 적용 시 : text_block 의 nested 구조를 *3 sub_text_block* 으로 sub-decompose 하거나, Frame Slot cardinality 를 *aggregate (3)* 으로 해석할지 결정 필요
- v1 단순화 : text_block 의 top-bullet 단위가 *implicit 한 sub-content_object* — 향후 explicit 화
### 9.3 03-2 의 case (transform_table 포함)
```yaml
section_id: "03-2"
content_objects:
- id: "03-2.transform-1"
type: transform_table
role: summary
type_specific: { pair_count: 3 }
- id: "03-2.text-1"
type: text_block
role: detail
type_specific: { bullet_count: 1 }
- id: "03-2.text-2"
type: text_block
role: detail
type_specific: { bullet_count: 1 }
- id: "03-2.text-3"
type: text_block
role: detail
type_specific: { bullet_count: 3 (large) }
- ... (product 쪽도 4 개)
```
**Stage A** :
- 3-way decision : section 전체가 F29 (process/product 2-column structure) 와 매칭 → *whole-section frame match*
- single-region zone, content_type=text+transform_table, ratio=1.0
- frame_match_strategy = { kind: "frame_match", frame_id: "F29" }
**Stage B** :
- F29 sub_zones (= Frame Slots) : [process_column (accepts: text_block + transform_table, cardinality 3), product_column (accepts: text_block, cardinality 3)]
- process_column → transform_table + 2 text_block (3 개)
- product_column → 3 text_block
- 모두 inline_full 로 표시
이건 *현재 mapper (column_with_transform / column_plain) 가 implicit 으로 하는 것* — 본 spec 이 *explicit 하게 표현*.
### 9.4 03-2 의 cell row 1 (transform_table) 의 10 px overflow 재해석
placement 가 explicit 하게 되어도 transform_table 이 row 1 cell 에 *콘텐츠 height 131 vs 가용 121* 인 건 변하지 않음.
**그러나** :
- placement 가 *transform_table 의 size_estimate* 를 미리 알면
- frame contract 의 Frame Slot 이 *expected_height* 를 declare 하면
- planning 단계에서 *"transform_table 이 row 1 Frame Slot 의 expected_height 초과한다"* 를 사전 감지 가능
- 그 시점에서 display_strategy = `inline_preview_with_details` 로 자동 전환 (3 transforms 중 2 inline + "1 더 보기")
- 또는 placement 가 *frame 부적합* 으로 판정 → frame_reselect 신호
*본 spec 의 §4 placement algorithm 에 size_estimate 기반 fit pre-check* 가 들어가면 — A1~A4 telemetry 가 *trigger 안 되는 정상 path* 가 됨.
이게 본 spec 이 가리키는 *진짜 fit policy 의 자리*.
---
## 10. 다음 step (사용자 결정)
본 spec v1 정의 후 구현 axis 후보 :
- B1. content_extractor (MDX → content_object 정규화)
- B2. internal_region_planner (Layer A — 3-way decision + 비율 + frame_match_strategy)
- B3. frame_contracts 에 accepted_content_types + sub_zones (= Frame Slot) 선언 (3 frame)
- B4. placement_planner (Layer A → Layer B 통합)
- B5. partial / region container marker 추가 + telemetry 연동 (이름 결정 포함)
- B6. details/popup runtime
각 axis 는 *별도 step*. 사용자가 우선순위 결정.
본 spec 자체는 *implementation 0 단계의 정의*. 다음 step 은 사용자가 잠근 후 진행.