- consolidate evidence-based principles for Step 5/6/7 and B-axis matching layer - record CompositionUnit, candidate-based composition, and 4-tier terminology - defer bridge architecture and slot_payload evolution as scope-locked open items
12 KiB
Phase Z 매칭 아키텍처 — 원칙 anchor
22-step pipeline 의 매칭 layer (Step 5/6/7 + B-axis Step 9/10/11) 를 다루는 원칙 anchor doc.
본 문서는 새 디자인 생성 이 아니라 기존 코드 / 과거 docs / 관찰 사례 / session 인사이트 를 근거로 한 forward improvement consolidation.
관련 anchors :
docs/architecture/PHASE-Z-PIPELINE-OVERVIEW.md(D1, 22-step pipeline)docs/architecture/PHASE-Z-CONTENT-OBJECT-SUBZONE-SPEC.md(D2, Layer A/B SPEC)docs/architecture/PHASE-Z-CONTENT-OBJECT-SUBZONE-PLAN.md(D3, schema completion plan)docs/architecture/PHASE-Z-PIPELINE-STATUS-BOARD.md(D4, 현재 status)
§0. 본 문서의 작성 방법론 (P0)
evidence-based forward improvement — 매번 4 source 를 거쳐 도출 :
- 과거 docs (D1 / D2 / D3 / D4) — 원래 의도
- 현재 코드 (
src/phase_z2_*.py/templates/phase_z2/catalog/frame_contracts.yaml) — 실제 동작 - 관찰된 혼선 / 실패 사례 — 경험적 evidence
- session 인사이트 — 최근 정리된 mental model
이걸 관찰 → 혼선 → 도출 원칙 → 개선 방향 form 으로 표면화. 과거 회귀 가 아니라 과거를 근거로 한 개선. 감으로 결정 / 가장 쉬운 선택 금지.
§1. 현재 코드 상태 (관찰)
1.1 매칭 단위 — CompositionUnit
매칭의 atomic unit 은 section 이 아니라 CompositionUnit. src/phase_z2_composition.py 의 collect_candidates 가 3 종 candidate type 을 동시 생성 :
single—src/phase_z2_composition.py:230-243(1 section 단독)parent_merged—src/phase_z2_composition.py:260-273(parent 자체가 V4 매칭, children 묶음)parent_merged_inferred—src/phase_z2_composition.py:352-368(children rep_match 기반 inferred merge)
각 candidate 는 source_section_ids[] + merge_type (line 232 / 262 / 354) 로 어떤 section 들을 어떻게 묶었는지 표현.
1.2 frame 선택 — render path authority = V4 rank-1
현재 render path 의 frame_template_id 는 composition planner 의 V4 rank-1 기반 :
lookup_v4_match()insrc/phase_z2_pipeline.py(D1:212 명시) — rank-1 만 반환- composition planner 가 V4 결과로 candidate 생성
B4 (src/phase_z2_placement_planner.py:90-107 의 _select_frame) 도 frame selection 을 수행 하지만 trace-only — render path authority 는 V4 rank-1.
V4 = top-k 선언만 됐고 rank-1 사용 만 활성. D4 의 ⚠ partial.
1.3 binding — phase_z2_mapper.map_with_contract (Layer 1)
src/phase_z2_mapper.py:584-609 의 map_with_contract(section, contract) 는 contract 에서 오직 두 field 만 read :
contract["source_shape"](split 규칙)contract["payload"]["builder"](named PAYLOAD_BUILDERS dispatch)
accepted_content_types / sub_zones 등 Layer 2 field 는 접근 X. 즉 mapper 는 Layer 1 binder 로 격리.
1.4 placement — phase_z2_placement_planner (Layer 2 reader, trace-only)
src/phase_z2_placement_planner.py :
_select_frame(90-107) —accepted_content_types ⊇ content_type_setcover + declaration order first_assign_region_to_sub_zone(113-151) —sub_zones의 narrowest-accepts firstplan_placement(157+) — Stage A (B2 internal regions) + Stage B (region ↔ sub_zone) 통합
B4 는 Layer 2 reader 이지만 src/phase_z2_pipeline.py:1060-1145 에서 trace-only 로만 호출. render path (slot_payload 생성) 미연결. D4:78-83 의 render path placement_trace 미사용 과 일치.
1.5 capacity / fit — classifier (post-render telemetry)
src/phase_z2_classifier.py 의 classify_visual_runtime_check — render 후 visual measurement 기반 fit 판정. Architectural reframe (D2:16-33 의 PLANNING / RENDER / POST-RENDER 3-layer) : A1~A4 = post-render telemetry layer. 덜 중요 가 아니라 위치가 다름. 매칭 결정 자체에는 직접 입력 X (현재).
§2. 확인된 혼선 (resolution)
2.1 Sub-zone 용어 4-방향 충돌 → 4-tier 분리 (P5'')
session 안 누적 혼선 :
- D1 의 "Zone (top / bottom_l / bottom_r 등)"
- composition.py 의 child unit 분리 (single / parent_merged 등)
- yaml 의
sub_zonesfield - 사용자 mental model 의 "### split"
→ 4 tier 별 명칭 lock :
| Tier | 명칭 | 위치 | 정체 |
|---|---|---|---|
| 1 | Layout | slide-level | slide-body 안 zone topology (8-preset vocabulary) |
| 2 | Zone | layout 안 | 콘텐츠 구역 (top / bottom_l / bottom_r 등). 1 frame 매칭 단위 |
| 3 | Split Zone (Child Unit) | zone 안 | composition planner 가 만든 child unit (parent_merged 의 분리 단위) |
| 4 | Frame Slot | frame 안 | yaml 의 sub_zones = Layer B placement target. region 매칭 대상 |
이후 본 문서 / 후속 doc 은 위 명칭 어휘 만 사용. yaml field sub_zones 는 기존 이름 유지하되 의미 = Frame Slot 으로 lock (mechanical rename 은 D3 plan).
2.2 composition tie-break ≠ Frame Slot tie-break
scope-lock 단계에서 D2:524-533 의 tie-break 을 composition tie-break evidence 로 잘못 분류. 실제로는 :
- D2:524-533 = Frame Slot 단위 tie-break (Stage B 안, frame 이미 선택된 후)
- composition tie-break =
composition.py:433-441(sort + greedy + coverage)
두 layer 가 다름. P7 evidence 는 composition layer 만, Frame Slot layer 는 별개 location.
2.3 "Layer 2 미사용" framing 오류
scope-lock 초안의 "mapper 가 Layer 2 를 consume 안 함" framing 은 암묵적으로 mapper 가 Layer 2 reader 가 돼야 한다 의 fix direction 함의. 실제 architectural intent :
- mapper = Layer 1 binder (의도된 격리)
- B4 = Layer 2 reader (의도된 분리)
문제 는 Layer 2 unconsumed 가 아니라 B4 의 Layer 2 read 가 trace-only 라 render path 까지 안 닿음. bridge 가 답이지 mapper 확장 이 아님.
2.4 Step 4/5/9 분리 미완
현재 구현은 D1 이 의도한 Step 4 → Step 5 → Step 9 분리가 완성되지 않음 :
- Step 4 (composition planning input — section_layout_signature / content_object 구조) = partial/dormant (D4:73 의 ⚠ partial transition)
- Step 5 evidence 와 Step 9 final selection = conflate (D1:255)
D1:185 가 명시적으로 Step 4 가 frame matching 보다 먼저 와야 함 을 지적.
§3. 도출된 원칙
P1 : 매칭 단위 = CompositionUnit (section 아님)
- 근거 :
composition.py:230-243(single) /:260-273(parent_merged) /:352-368(parent_merged_inferred). 각 line:232/:262/:354가merge_typesignal - 함의 : MDX section 을 1:1 zone 매칭 단위로 가정하는 코드 / doc 는 모두 추상화 누락. CompositionUnit 으로 통일.
P3 : frame 선택 / content binding layer 분리
- 근거 : 현재 코드의 de facto 분리 (mapper §1.3 + placement_planner §1.4)
- 원칙 : frame 선택 (어떤 frame 을 쓸지) 과 content binding (선택된 frame 에 콘텐츠를 어떻게 채울지) 는 별개 axis. 함께 결정하면 search space 가 곱셈으로 폭발 + 실패 격리 불가.
- 함의 : 어떤 axis 가 frame 을 결정 하는지 + 어떤 axis 가 binding 하는지 명시적 분리 — 한 module 이 둘 다 하면 회귀 위험.
P5'' : 4-tier terminology lock
§2.1 표 그대로. 후속 doc / commit message / 코드 docstring 모두 4-tier 어휘만 사용.
P7 : candidate-based composition (sequential A/B 아님)
- 근거 :
- 원리 :
docs/architecture/PHASE-Z-PIPELINE-OVERVIEW.md:216-218("child 따로 / sibling 묶기 / parent 단위" + scoring inputs) - 구현 :
composition.py:230-243/:260-273/:352-368(3 candidate type 동시 생성) - 선택 :
composition.py:433-441((score desc, source_section_ids count desc)+ greedy covered skip)
- 원리 :
- 원칙 : composition 은 "## → fail → ### split → re-merge" sequential 이 아니라 3 candidate type 을 동시 생성 → score + coverage tie-break 의 parallel evaluation.
- D2:524-533 은 Frame Slot tie-break (Stage B) 로 본 P7 evidence 와 별도 layer.
P8 : staged migration — frame 선택 authority + binding 진화 분리
P8-a (near-term)
- 목표 : frame selection authority 정리 (V4 rank-1 단독 → B4 informant 또는 B4-mediated 의 어떤 형태)
- 유지 : mapper binding channel (PAYLOAD_BUILDERS) — 현재 working channel 안전
- open : B4 결과를 render path frame selection 에 어떻게 반영 할지의 bridge 형태 → §5 의 Q-V4B4 / Q-LB 에서 별도 scope-lock
P8-b (longer-term, 지금 결정 X)
- region / Frame Slot-aware slot_payload 진화
- PLACEMENT_PAYLOAD_BUILDERS 등 별도 namespace 가능성
- DC2 Open question (Q-LB) 해결 후 결정. 본 문서 시점에서는 deferred.
§4. 코드 변경 결정 지점
DC1 : Step 4/5/9 분리 미완
- 현 상태 : D1 의도한 Step 4 → Step 5 → Step 9 분리가 완성되지 않음. Step 4 (composition planning input — section_layout_signature / content_object 구조) 는 partial/dormant (D4:73 의 ⚠ partial), Step 5 evidence 와 Step 9 final selection 은 conflate (D1:255).
- 결정 지점 :
- Step 4 가 frame matching 전 에 composition candidate 평가 input 으로 흘러야 (D1:185 명시적 지적)
- Step 5 evidence layer 와 Step 9 final selection layer 의 명시적 분리 필요
- 영향 module :
src/phase_z2_pipeline.pyorchestrator +src/phase_z2_composition.py(input 확장)
DC2-a : phase_z2_mapper = Layer 1 working channel
- 현 상태 :
src/phase_z2_mapper.py:584-609가source_shape+payload.builder만 read. 현재 render path 의 유일 working channel. - 결정 지점 : Layer 1 격리 유지. mapper 에 Layer 2 read 를 추가하는 방향은 반대 — 실제 의도는 별 axis (DC2-b + Open question Q-LB).
DC2-b : phase_z2_placement_planner = Layer 2 reader, render path 미연결
- 현 상태 :
src/phase_z2_placement_planner.py:90-107(accepted_content_types) +:113-151(sub_zones) — Layer 2 read 활성. 그러나src/phase_z2_pipeline.py:1060-1145에서 trace-only. final render 미연결. - 결정 지점 : Layer 2 read 결과를 render path 에 어떻게 합류시킬지 의 bridge 결정 — §5 의 Q-LB 에서 별도 scope-lock.
DC3 : Internal Region runtime — trace-only partial
- 현 상태 : SPEC v1 (D2) 가 Internal Region (Layer A) / Frame Slot (Layer B) 의 layered placement 정의. Layer A runtime 은 trace-only partial —
src/phase_z2_placement_planner.py:157+의plan_placement가 Stage A 호출 (:192-197의plan_internal_regionscall) 수행하지만, 결과가 render path 미합류. 부재 가 아니라 render path authority 미연결. D4:73 의 ⚠ partial 분류와 일치. - 결정 지점 : Layer A planning telemetry 활성 단계 → render path 합류 단계의 separate axis. P8-b 와 연관 (slot_payload 진화 이전 단계).
§5. open scope-lock questions
본 axis 범위 밖 의 deferred 결정. 후속 axis 에서 각각 별도로 lock.
Q-FW : 4 filter score weight / threshold
- F1 frame internal fit / F2 child frame divergence / F3 content type-structure match / F4 content loss risk 의 score weight 와 auto vs review threshold 결정 필요.
Q-CO : candidate priority tie-break
- composition.py:433 의
(score, source_section_ids count)외에 추가 axis (cardinality_fit / hierarchy_coherence / density) 도입 시점.
Q-RT : review / adapter_needed threshold
- W1/W2/W3 신호 + auto_selectable=False candidate 의 review surface 정책.
Q-V4B4 : V4 → B4 frame selection authority transition timing
- P8-a 의 언제 — V4 단독 / B4 informant / B4-mediated 의 단계.
Q-LB : Layer 1 ↔ Layer 2 bridge architecture
- DC2-b 의 어떻게 — mapper direct consume / B4-mediated integration / 별도 translator. Q-V4B4 와 연관되나 별개 layer (frame 선택 timing vs Layer 2 → render path bridge 구조).
Q-CE : catalog extension surface
- frame_contracts.yaml 에 신규 frame 추가 시 어떤 layer 부터 채워야 self-consistent 인지의 declarative form.
Q-DT : decision trace 표준 form
- composition / placement / fit_classification 의 trace 통합 schema. 현재 각각 다른 dict 구조.