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
This commit is contained in:
849
docs/architecture/PHASE-Z-CONTENT-OBJECT-SUBZONE-SPEC.md
Normal file
849
docs/architecture/PHASE-Z-CONTENT-OBJECT-SUBZONE-SPEC.md
Normal file
@@ -0,0 +1,849 @@
|
||||
# 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 은 사용자가 잠근 후 진행.
|
||||
Reference in New Issue
Block a user