From a06dd3d4b07767f752d6f40ed690e07164836e0c Mon Sep 17 00:00:00 2001 From: kyeongmin Date: Thu, 21 May 2026 19:39:16 +0900 Subject: [PATCH] feat(#42): IMP-04b catalog extension to 32 frames (u1~u24) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends frame_contracts.yaml from 11 to 32 contracts to match V4 evidence (tests/matching/v4_full32_result.yaml unique template_ids), closing the IMP-04b gap surfaced in IMP-04 (#4) Track A milestone. Scope (Stage 2 24-unit plan): - u3/u4: WIP partial absorb — app_sw_package_vs_solution (F23), pre_construction_model_info_stacked (F9). Both promoted from _WIP_FILES.md to frame_contracts.yaml. WIP allowlist now empty. - u5~u11: Track A 7 frames (index.html present, contract missing). - u12~u23: Track B 12 frames (visual_pending: true; family partial authoring deferred — contract-first per Stage 2 plan). - u24: BT closure gate. Adds test_imp04b_closure_gate_v4_coverage_and_wip_empty (catalog ↔ V4 set-equal + WIP==0) and test_vp_exempt_keys_are_contracted_and_disk_absent (vp ∩ disk == ∅). Relaxes test_contracts_set_equals_disk_families_minus_wip to (disk - wip) ∪ vp. 32 derived from V4 evidence YAML (no hardcoding). Closure facts (locked): contracts = 32, v4_unique = 32, missing = [], extra = [], wip_count = 0, vp_count = 19, vp ∩ disk = []. Guardrails honored: - No calculate_fit migration. - No AI/Kei API call in per-frame work. - No 1-2 sample hardcoding (Codex #7 generalization guardrail). - No production refactor for tests (IMP-32 owns helper extract). - figma_to_html / V4 / Phase Z 3-layer separation preserved. - 1 commit = 1 IMP-04b decision unit (bundled u1~u24 per Stage 2 plan; CAT+WIP atomicity for u3/u4 preserved). Tests: tests/test_family_contract_baseline.py 4/4 PASS. Cross-ref: IMP-04 (#4), IMP-29 (#38), IMP-30 (#39), IMP-31 (#40), IMP-32 (#41), IMP-33 (#61), IMP-47A (#75). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../phase_z2/catalog/frame_contracts.yaml | 1071 +++++++++++++++++ templates/phase_z2/families/_WIP_FILES.md | 5 +- tests/test_family_contract_baseline.py | 95 +- 3 files changed, 1164 insertions(+), 7 deletions(-) diff --git a/templates/phase_z2/catalog/frame_contracts.yaml b/templates/phase_z2/catalog/frame_contracts.yaml index f86d64b..1c101d4 100644 --- a/templates/phase_z2/catalog/frame_contracts.yaml +++ b/templates/phase_z2/catalog/frame_contracts.yaml @@ -727,3 +727,1074 @@ bim_current_problems_paired: body_key_pattern: "row_{r}_{side}_body" rows: 4 sides: [left, right] + + +app_sw_package_vs_solution: + # Reason : V4 01-2 rank 4 RS=1 (0.6813); #42 IMP-04b u3 WIP absorb; F29 payload shape reuse. + template_id: app_sw_package_vs_solution + frame_id: 1171281203 + family: table + + source_shape: h3_subsections # F29 와 동일 — 2 h3 subsection = 2 column. + cardinality: + strict: 2 # 2 column (Package / Solution) — NOT row count. + overflow_policy: abort_or_review + + role_order: + - col_a + - col_b + + visual_hints: + min_height_px: 350 # title 30 + header 35 + 5 row×~50 + padding 30 + buffer 5. + + accepted_content_types: + - text_block + + sub_zones: + - id: col_a + role: main_text + accepts: [text_block] + cardinality: { min: 1, max: 8 } # F18 rows {min:1,max:12} pattern → column-internal row 1..8. + partial_target_path: ".f23b__col-a-body" + - id: col_b + role: main_text + accepts: [text_block] + cardinality: { min: 1, max: 8 } + partial_target_path: ".f23b__col-b-body" + + payload: + title: + source: section.title + builder: process_product_pair # 재사용 (F29). title_to/body_to = WIP partial key 명. + builder_options: + pad_sections_to: 5 # canonical 5 row (고객/정의/특징/장점/단점). + columns: + - title_to: col_a_label + body_to: col_a_body + body_parser: column_plain + - title_to: col_b_label + body_to: col_b_body + body_parser: column_plain + + +pre_construction_model_info_stacked: + # Reason : V4 04-1 holdout rank 1 RS=1 (0.7114); #42 IMP-04b u4 WIP absorb; F16/F11 builder reuse. + template_id: pre_construction_model_info_stacked + frame_id: 1171281180 + family: list + + source_shape: top_bullets + cardinality: + min: 4 # analysis.md min 4 / Figma 5-color cycle design floor. + max: 5 # partial `_slot_count or 5` hardcap (5-color cycle, line 175). + overflow_policy: abort_or_review + + visual_hints: + min_height_px: 350 # title 30 + 5 pill × ~55 + grid gap 40 + padding 30 + buffer 5. + + accepted_content_types: + - text_block + + sub_zones: + - id: pills + role: main_text + accepts: [text_block] + cardinality: { min: 4, max: 5 } # F18 single-container precedent (homogeneous list). + partial_target_path: ".f9b__pill-rows" + + payload: + title: + source: section.title + builder: quadrant_flat_slots # 재사용 (F16/F11/F20/F8/F28 pattern). + builder_options: + item_parser: quadrant_item + pad_to: 5 + truncate_at: 5 + label_key_pattern: "pill_{n}_label" + body_key_pattern: "pill_{n}_body" + empty_label: "" + empty_body: [] + + +engn_sw_three_types: + # Reason : V4 LE=1 (01-2 rank 3, 0.7571); #42 IMP-04b u5 TrackA VP deep-review repr — F18 2-col 변형 (3 data col × N data rows); visual_pending = family partial 미작성 (figma_to_html source index.html 만 존재); builder/parser compare_table_3col + compare_row_3col_item = new mapper wire 별 IMP. + template_id: engn_sw_three_types + frame_id: 1171281204 + family: table + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 3 + overflow_policy: abort_or_review + + role_order: [col_a, col_b, col_c] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: col_a_header + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f24b__header > .f24b__header-cell:nth-child(2)" + - id: col_b_header + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f24b__header > .f24b__header-cell:nth-child(3)" + - id: col_c_header + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f24b__header > .f24b__header-cell:nth-child(4)" + - id: rows + role: data + accepts: [text_block] + cardinality: { min: 1, max: 8 } + partial_target_path: ".f24b__rows" + payload: + title: { source: section.title } + builder: compare_table_3col + builder_options: + item_parser: compare_row_3col_item + max_rows: 8 + + +bim_adoption_central_split: + # Reason : V4 RS=2 best (01-2 rank 5, 0.6455; 03-2 rank 5, 0.6278); #42 IMP-04b u6 TrackA VP checklist — F27 central-split-synthesis 변형 (header + 2 arms 수렴 → center_result + conclusion, analysis.md slots 5 required 4); visual_pending = family partial 미작성 (figma_to_html source index.html 만 존재); builder/parser central_split_synthesis + arm_label_body_item = new mapper wire 별 IMP. + template_id: bim_adoption_central_split + frame_id: 1171281208 + family: compare + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 2 # 2 arms (좌/우) — header + center_result + conclusion = fixed singletons (non-data axis). + overflow_policy: abort_or_review + + role_order: [left_arm, right_arm] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: header + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f27b__header" + - id: left_arm + role: main_text + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f27b__arm--left" + - id: right_arm + role: main_text + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f27b__arm--right" + - id: center_result + role: emphasis + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f27b__center" + - id: conclusion + role: conclusion + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f27b__conclusion" + payload: + title: { source: section.title } + builder: central_split_synthesis + builder_options: + item_parser: arm_label_body_item + + +bigroom_system_components: + # Reason : V4 RJ=10 (best 01-2 rank 8, 0.5352; 03-2 rank 7, 0.5054; all sections label reject — anchor 0.25 약함, structure axes cardinality/relation/slot 모두 1.0); #42 IMP-04b u7 TrackA VP checklist — F-1171281178 (frame_number 7) 2col-paired-list (compare family, H/W + S/W 2축 분해 + 각 측 변수 항목 list); analysis.md slots 5 required 5 (title + col_a_label + col_a_items + col_b_label + col_b_items); visual_pending = family partial 미작성 (figma_to_html source index.html 만 존재); builder/parser paired_list_two_col + column_label_body_item = new mapper wire 별 IMP. + template_id: bigroom_system_components + frame_id: 1171281178 + family: compare + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 2 # 2 columns (H/W + S/W) — column-internal item count 은 별축 (각 column 의 항목 수는 col_a_items / col_b_items sub_zone cardinality 로 표현). + overflow_policy: abort_or_review + + role_order: [col_a, col_b] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f7b__title" + - id: col_a_label + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f7b__col-a-label" + - id: col_a_items + role: data + accepts: [text_block] + cardinality: { min: 1, max: 8 } + partial_target_path: ".f7b__col-a-items" + - id: col_b_label + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f7b__col-b-label" + - id: col_b_items + role: data + accepts: [text_block] + cardinality: { min: 1, max: 8 } + partial_target_path: ".f7b__col-b-items" + payload: + title: { source: section.title } + builder: paired_list_two_col + builder_options: + item_parser: column_label_body_item + max_items_per_column: 8 + + +model_specialized_engn_sw: + # Reason : V4 RJ=9/RS=1 (best non-holdout 02-2.2 rank 9, 0.5301; 03-1 rank 9, 0.4986; 03-2 rank 9, 0.3778; 01-2 rank 12, 0.4065 — all 4 main sections label reject; holdout best 01-1 rank 3, 0.749 restructure); #42 IMP-04b u8 TrackA VP checklist — F22 split-panel-numbered (diagram family, 좌:category_panel + 우:numbered item_1..item_3 body); analysis.md slots 5 required 5 (title + category_panel + item_1_body + item_2_body + item_3_body), cardinality ideal 3 / min 3 / max 4 (strict:3 = ideal/min anchor, max=4 future flex per analysis.md); visual_pending = family partial 미작성 (figma_to_html source index.html 만 존재); builder/parser split_panel_numbered + numbered_item_body = new mapper wire 별 IMP. + template_id: model_specialized_engn_sw + frame_id: 1171281202 + family: diagram + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 3 # 3 numbered items (item_1..item_3) — title + category_panel = fixed singletons (non-data axis). analysis.md max=4 = future flex, current strict matches ideal=3 / min=3 + explicit 3-slot naming. + overflow_policy: abort_or_review + + role_order: [item_1, item_2, item_3] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f22b__title" + - id: category_panel + role: main_text + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f22b__category-panel" + - id: item_1_body + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f22b__item--1" + - id: item_2_body + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f22b__item--2" + - id: item_3_body + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f22b__item--3" + payload: + title: { source: section.title } + builder: split_panel_numbered + builder_options: + item_parser: numbered_item_body + + +sw_development_cycle_six_nodes: + # Reason : V4 RJ=10 (all 10 sections label reject; best non-holdout 02-2.2 rank 22, 0.2261; main 01-2 rank 26, 0.1115; 03-1 rank 25, 0.1694; 03-2 rank 26, 0.1127; holdout best 01-1 rank 17, 0.424; cardinality axis 0.0 across the board — frame strict 6 mismatches all MDX section item counts); #42 IMP-04b u9 TrackA VP checklist — F-1171281172 (frame_number 1) circular-nodes-6 (diagram family, 6 병렬 노드 순환/방사형 배치); analysis.md slots 7 required 7 (title + node_1..node_6), cardinality ideal 6 / min 6 / max 6 (strict:6); visual_pending = family partial 미작성 (figma_to_html source index.html 만 존재); builder/parser circular_nodes_six + node_label_item = new mapper wire 별 IMP. + template_id: sw_development_cycle_six_nodes + frame_id: 1171281172 + family: diagram + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 6 # 6 nodes (node_1..node_6) — title = fixed singleton (non-data axis). analysis.md ideal/min/max = 6 (strict 6-node 순환도, 가변 항목 없음). + overflow_policy: abort_or_review + + role_order: [node_1, node_2, node_3, node_4, node_5, node_6] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f1b__title" + - id: node_1 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f1b__node--1" + - id: node_2 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f1b__node--2" + - id: node_3 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f1b__node--3" + - id: node_4 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f1b__node--4" + - id: node_5 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f1b__node--5" + - id: node_6 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f1b__node--6" + payload: + title: { source: section.title } + builder: circular_nodes_six + builder_options: + item_parser: node_label_item + + +overseas_bim_numbered_list: + # Reason : V4 RJ=10 (all 10 sections label reject; best non-holdout 02-2.2 rank 18, 0.3053; main 01-2 rank 27, 0.079; 03-1 rank 17, 0.3008; 03-2 rank 23, 0.1701; holdout best 01-1 rank 15, 0.5125; cardinality axis 0.0 across main 4 — frame strict 4 mismatches MDX section item counts); #42 IMP-04b u10 TrackA VP checklist — F-1171281174 (frame_number 3) numbered-list-4 (list family, 4 병렬 번호 항목 — 국외 BIM 수행 현황); analysis.md slots 5 required 5 (title + item_1..item_4), cardinality ideal 4 / min 4 / max 4 (strict:4); visual_pending = family partial 미작성 (figma_to_html source index.html 만 존재); builder/parser numbered_list_four + numbered_label_body_item = new mapper wire 별 IMP. + template_id: overseas_bim_numbered_list + frame_id: 1171281174 + family: list + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 4 # 4 numbered items (item_1..item_4) — title = fixed singleton (non-data axis). analysis.md ideal/min/max = 4 (strict 4-item numbered list, 가변 항목 없음). + overflow_policy: abort_or_review + + role_order: [item_1, item_2, item_3, item_4] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f3b__title" + - id: item_1 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f3b__item--1" + - id: item_2 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f3b__item--2" + - id: item_3 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f3b__item--3" + - id: item_4 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f3b__item--4" + payload: + title: { source: section.title } + builder: numbered_list_four + builder_options: + item_parser: numbered_label_body_item + + +solution_engn_split_diagram: + # Reason : V4 RJ=7/RS=3 (best non-holdout 02-2.2 rank 4, 0.7394 restructure; main 01-2 rank 20, 0.2097 reject; 03-1 rank 4, 0.6631 reject; 03-2 rank 18, 0.2452 reject; holdout best 02-1 rank 5, 0.724 restructure; cardinality axis non-zero on 7/10 sections — 0.8 on main 02-2.2/03-1, 0.0 on main 01-2/03-2); #42 IMP-04b u11 TrackA VP checklist — F-1171281201 (frame_number 21) split-panel-diagram (diagram family, 좌 split panel + 우 항목 + flow_labels — solution engineering S/W 개념도); analysis.md slots 4 required 4 (title + left_panel + right_panel + flow_labels), cardinality ideal 4 / min 3 / max 5 (strict:3 = data axis count = role_order length, excluding title header; analysis ideal 4 includes title slot); visual_pending = family partial 미작성 (figma_to_html source index.html 만 존재); builder/parser split_panel_diagram + panel_section_item = new mapper wire 별 IMP. + template_id: solution_engn_split_diagram + frame_id: 1171281201 + family: diagram + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 3 # 3 data slots (left_panel, right_panel, flow_labels) — title = fixed singleton header (non-data axis). analysis.md ideal 4 includes title; data-axis strict = 3 matches role_order length and analysis min=3. + overflow_policy: abort_or_review + + role_order: [left_panel, right_panel, flow_labels] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f21b__title" + - id: left_panel + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f21b__panel--left" + - id: right_panel + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f21b__panel--right" + - id: flow_labels + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f21b__flow-labels" + payload: + title: { source: section.title } + builder: split_panel_diagram + builder_options: + item_parser: panel_section_item + + +engn_sw_development_domain_knowledge: + # Reason : V4 RJ=10 (all 10 sections label reject; best non-holdout 03-1 rank 21, 0.1891; main 01-2 rank 21, 0.1755; 02-2.2 rank 25, 0.1831; 03-2 rank 28, 0.0635; holdout best 04-2.2 rank 8, 0.6537; cardinality axis 0.0 across main 4 — frame strict 4 mismatches MDX section item counts); #42 IMP-04b u12 TrackB VP contract-first — F-1171281173 (frame_number 2) bullet-cards-4-plus-center (cards family, 4 pillar labels + center_message + description — Engineering S/W 개발 Domain knowledge 기반); analysis.md slots 7 required 6 (title + pillar_1..pillar_4 + center_message + description; description optional); cardinality ideal 4 / min 4 / max 4 (strict:4 = pillar data axis); visual_pending = figma_to_html source index.html 미작성 (TrackB contract-first per Stage 2 plan) + Phase Z family partial 미작성; builder/parser pillar_cards_four_plus_center + pillar_label_item = new mapper wire 별 IMP. + template_id: engn_sw_development_domain_knowledge + frame_id: 1171281173 + family: cards + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 4 # 4 pillar labels (pillar_1..pillar_4) — data axis. analysis.md ideal/min/max = 4 (strict 4-pillar 디자인, 가변 항목 없음); title / center_message / description = non-data-axis singleton slots. + overflow_policy: abort_or_review + + role_order: [pillar_1, pillar_2, pillar_3, pillar_4] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f2b__title" + - id: pillar_1 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f2b__pillar--1" + - id: pillar_2 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f2b__pillar--2" + - id: pillar_3 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f2b__pillar--3" + - id: pillar_4 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f2b__pillar--4" + - id: center_message + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f2b__center-message" + - id: description + role: footer + accepts: [text_block] + cardinality: { min: 0, max: 1 } # analysis.md "slots 7 required 6" — description = 1 optional slot (footer 하단 설명 텍스트). + partial_target_path: ".f2b__description" + payload: + title: { source: section.title } + builder: pillar_cards_four_plus_center + builder_options: + item_parser: pillar_label_item + + +domestic_bim_actor_relations: + # Reason : V4 RJ=9/RS=1 (best non-holdout 01-2 rank 10, 0.5096 reject; main 02-2.2 rank 19, 0.2512 reject; 03-1 rank 30, 0.0253 reject (penalty 0.2); 03-2 rank 16, 0.279 reject; holdout best 04-2.1 rank 9, 0.6016 restructure (cardinality 1.0); 04-2.2 rank 10, 0.5787 reject (cardinality 1.0); cardinality axis 0.0 across main 4 — frame strict 4 mismatches MDX section item counts on main sections); #42 IMP-04b u13 TrackB VP contract-first — F-1171281175 (frame_number 4) quadrilateral-relations (diagram family, 4 BIM 수행 주체 actors + 중앙 relations annotation — 국내 BIM 수행 관계도); analysis.md slots 6 required 5 (title + actor_1..actor_4 + relations; relations optional annotation overlay per analysis 라벨과 연결선); cardinality ideal 4 / min 4 / max 4 (strict:4 = 4-actor data axis, 가변 항목 없음); visual_pending = figma_to_html source index.html 미작성 (TrackB contract-first per Stage 2 plan) + Phase Z family partial 미작성; builder/parser quadrilateral_actor_relations + actor_label_item = new mapper wire 별 IMP. + template_id: domestic_bim_actor_relations + frame_id: 1171281175 + family: diagram + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 4 # 4 actor labels (actor_1..actor_4) — data axis. analysis.md ideal/min/max = 4 (strict 4-actor 다이어그램, 가변 항목 없음); title = fixed singleton header, relations = optional annotation overlay (non-data axis). + overflow_policy: abort_or_review + + role_order: [actor_1, actor_2, actor_3, actor_4] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f4b__title" + - id: actor_1 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f4b__actor--1" + - id: actor_2 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f4b__actor--2" + - id: actor_3 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f4b__actor--3" + - id: actor_4 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f4b__actor--4" + - id: relations + role: footer + accepts: [text_block] + cardinality: { min: 0, max: 1 } # analysis.md "slots 6 required 5" — relations = 1 optional slot (지침·규정·기술형식 연결선 annotation overlay). + partial_target_path: ".f4b__relations" + payload: + title: { source: section.title } + builder: quadrilateral_actor_relations + builder_options: + item_parser: actor_label_item + + +compensation_complaint_side_card: + # Reason : V4 RJ=10 (all 10 sections label reject; best non-holdout 02-2.2 rank 11, 0.5231; main 01-2 rank 29, 0.0707; 03-1 rank 15, 0.4277; 03-2 rank 25, 0.121; holdout best by confidence 01-1 rank 9, 0.6574 reject (cardinality 1.0); holdout best by rank 04-1 rank 4, 0.5946 reject; cardinality axis 0.5 / 1.0 / 1.0 / 0.5 across main 4 — partial cardinality match on 02-2.2 / 03-1, half-match on 01-2 / 03-2); #42 IMP-04b u14 TrackB VP contract-first — F-1171281176 (frame_number 5) side-card-with-list (cards family, 좌측 사이드 다크 배경 타이틀 + 우측 3-item 불릿 리스트 — 보상현황·민원 관리 시각화 활용 토구 나열); analysis.md slots 4 required 4 (title + item_1..item_3); cardinality ideal 3 / min 3 / max 5 (strict:3 = data axis = declared item slot count, peer parity with u11 ideal/min/max divergence convention); visual_pending = figma_to_html source index.html 미작성 (TrackB contract-first per Stage 2 plan) + Phase Z family partial 미작성; builder/parser side_card_with_list_three + side_card_list_item = new mapper wire 별 IMP. + template_id: compensation_complaint_side_card + frame_id: 1171281176 + family: cards + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 3 # 3 list items (item_1..item_3) — data axis. analysis.md ideal 3 / min 3 (strict 3 = declared data slot count and min cardinality, peer parity with u11 strict:3 ideal/min/max divergence convention); title = fixed singleton header (non-data axis). max 5 = design 가용 범위 (declared slots only 3; overflow_policy 처리). + overflow_policy: abort_or_review + + role_order: [item_1, item_2, item_3] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f5b__title" + - id: item_1 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f5b__item--1" + - id: item_2 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f5b__item--2" + - id: item_3 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f5b__item--3" + payload: + title: { source: section.title } + builder: side_card_with_list_three + builder_options: + item_parser: side_card_list_item + + +compensation_complaint_map: + # Reason : V4 RJ=10 (all 10 sections label reject; best non-holdout 01-2 rank 16, 0.3429; main 02-2.2 rank 30, 0.0749 (base 0.2749, penalty 0.2); main 03-1 rank 31, 0.0 (base 0.1937, penalty 0.2); main 03-2 rank 22, 0.1867; holdout best by confidence 02-2.1 rank 1, 0.544 reject (cardinality 1.0); holdout 01-1 rank 28, 0.2216 reject; cardinality axis 0.0 / 0.0 / 0.0 / 0.0 across main 4 — frame strict 1 (single banner + single full-page map) mismatches MDX section multi-item bullet structure); #42 IMP-04b u15 TrackB VP contract-first — F-1171281177 (frame_number 6) full-page-map-banner (map family, 상단 배너 타이틀 + 풀페이지 위치 기반 시공 리스크 맵 시각화 — 보상현황·민원 관리 사업 맵 영역 표시); analysis.md slots 2 required 2 (banner_title + map_area); cardinality ideal 1 / min 1 / max 1 (strict:1 = data axis = single map_area, peer parity with strict-singleton frames); visual_pending = figma_to_html source index.html 미작성 (TrackB contract-first per Stage 2 plan) + Phase Z family partial 미작성; builder/parser full_page_map_banner + map_area_item = new mapper wire 별 IMP. + template_id: compensation_complaint_map + frame_id: 1171281177 + family: map + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 1 # 1 map area (map_area) — data axis. analysis.md ideal 1 / min 1 / max 1 (strict:1 = data axis = declared map data slot count; non-data axes excluded); banner_title = fixed singleton header (non-data axis). + overflow_policy: abort_or_review + + role_order: [map_area] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: banner_title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f6b__banner-title" + - id: map_area + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f6b__map-area" + payload: + title: { source: section.title } + builder: full_page_map_banner + builder_options: + item_parser: map_area_item + + +field_effectiveness_five_elements: + # Reason : V4 RJ=10 (all 10 sections label reject; best non-holdout 02-2.2 rank 26, 0.18 (base 0.38, penalty 0.2); main 01-2 rank 30, 0.0707 (base 0.2707, penalty 0.2); main 03-1 rank 22, 0.1881 (base 0.3881, penalty 0.2); main 03-2 rank 31, 0.0454 (base 0.2454, penalty 0.2); holdout best by rank 04-1 rank 2, 0.6453 reject (cardinality 1.0); holdout 01-1 rank 22, 0.3844 reject; cardinality axis 0.0 / 0.0 / 0.0 / 0.0 across main 4 — frame strict 5 (5 radial element) mismatches MDX answer-section item counts (answer_map 01-2 → F18, 02-2.2 → F14, 03-1 → F13, 03-2 → F29; F10 not an answer-section target)); #42 IMP-04b u16 TrackB VP contract-first — F-1171281181 (frame_number 10) radial-diagram-5 (diagram family, 5 병렬 요소 방사형 배치 — 현장 실효성 증대 위한 기본사항 element_1..element_5 with center-emphasis element_3 시각 보조 / 디지털 정보·S/W·PC·인터페이스 5요소 인프라); analysis.md slots 6 required 6 (title + element_1..element_5); cardinality ideal 5 / min 5 / max 5 (strict:5 = data axis = declared element slot count, peer parity with u9 strict:6 / u10 strict:4 / u12 strict:4 / u13 strict:4 where ideal=min=max convention); visual_pending = figma_to_html source index.html 미작성 (TrackB contract-first per Stage 2 plan) + Phase Z family partial 미작성; builder/parser radial_diagram_five + radial_element_item = new mapper wire 별 IMP. + template_id: field_effectiveness_five_elements + frame_id: 1171281181 + family: diagram + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 5 # 5 radial elements (element_1..element_5) — data axis. analysis.md ideal 5 / min 5 / max 5 (strict:5 = declared data slot count, peer parity with u9/u10/u12/u13 ideal=min=max convention); title = fixed singleton header (non-data axis). element_3 center-emphasis = 시각적 강조이며 구조 슬롯 평탄(데이터 슬롯 수는 5 유지). + overflow_policy: abort_or_review + + role_order: [element_1, element_2, element_3, element_4, element_5] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f10b__title" + - id: element_1 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f10b__element--1" + - id: element_2 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f10b__element--2" + - id: element_3 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f10b__element--3" + - id: element_4 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f10b__element--4" + - id: element_5 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f10b__element--5" + payload: + title: { source: section.title } + builder: radial_diagram_five + builder_options: + item_parser: radial_element_item + + +policy_goals_plus_execution_requirements: + # Reason : V4 RJ=10 (all 10 sections label reject; best non-holdout by base 03-1 rank 18, 0.2359 (base 0.4359, penalty 0.2); main 01-2 rank 17, 0.2649 (base 0.2649, penalty 0.0); main 02-2.2 rank 23, 0.2056 (base 0.4056, penalty 0.2); main 03-2 rank 17, 0.2469 (base 0.2469, penalty 0.0); holdout best by rank 04-2.1 rank 5, 0.6429 reject (cardinality 1.0); holdout 04-2.2 rank 9, 0.6427 reject (cardinality 1.0); cardinality axis 0.0 / 0.0 / 0.0 / 0.0 across main 4 — frame strict 4 (4 정책목표) mismatches MDX answer-section item counts (answer_map 01-2 → F18, 02-2.2 → F14, 03-1 → F13, 03-2 → F29; F15 not an answer-section target)); #42 IMP-04b u17 TrackB VP contract-first — F-1171281192 (frame_number 15) policy-4card-plus-list (cards family, 4 정책목표 카드 상단 + 1 실행요건 블록 하단 — 국가 신 성장 동력 재도약 / 공기·공사비 30% 절감 / 안전사고 40% 감소 / 고부가가치 산업전환 4 goal + 실행요건 footer composite list); analysis.md slots 10 required 9 (title + goal_1_label + goal_1_body + goal_2_label + goal_2_body + goal_3_label + goal_3_body + goal_4_label + goal_4_body + requirements_block); cardinality ideal 4 / min 4 / max 4 (strict:4 = 4 정책목표 data axis, peer parity with u9 strict:6 / u10 strict:4 / u12 strict:4 / u13 strict:4 / u16 strict:5 where ideal=min=max convention); sub_zones composite goal_N convention per F16 precedent (`bim_issues_quadrant_four` quadrant_1..4 = composite, label/body 는 payload key not separate Frame Slot) — analysis.md goal_N_label / goal_N_body granularity = builder/parser internal (별 IMP), Frame Slot 단위 = composite goal_N; requirements_block = optional footer composite (10 - 1 optional per analysis.md "required 9" → 5 실행요건 카드 5장 nested list); visual_pending = figma_to_html source index.html 미작성 (TrackB contract-first per Stage 2 plan) + Phase Z family partial 미작성; builder/parser policy_4card_plus_list + goal_label_body_item = new mapper wire 별 IMP. + template_id: policy_goals_plus_execution_requirements + frame_id: 1171281192 + family: cards + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 4 # 4 정책목표 (goal_1..goal_4) — data axis. analysis.md ideal 4 / min 4 / max 4 (strict:4 = declared data slot count, peer parity with u9/u10/u12/u13/u16 ideal=min=max convention); title = fixed singleton header (non-data axis); requirements_block = optional footer composite (analysis.md "slots 10 required 9" → requirements_block 1 optional). goal_N_label + goal_N_body 평탄 = builder/parser 내부 (composite goal_N = Frame Slot 단위, per F16 precedent). + overflow_policy: abort_or_review + + role_order: [goal_1, goal_2, goal_3, goal_4] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f15b__title" + - id: goal_1 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f15b__goal--1" + - id: goal_2 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f15b__goal--2" + - id: goal_3 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f15b__goal--3" + - id: goal_4 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f15b__goal--4" + - id: requirements_block + role: footer + accepts: [text_block] + cardinality: { min: 0, max: 1 } # analysis.md "slots 10 required 9" — requirements_block = 1 optional slot (footer 실행요건 nested list composite). + partial_target_path: ".f15b__requirements" + payload: + title: { source: section.title } + builder: policy_4card_plus_list + builder_options: + item_parser: goal_label_body_item + + +design_method_distortion_three_col: + # Reason : V4 RJ=10 (all 10 sections label reject; best non-holdout by base 03-1 rank 9, 0.5975 (base 0.5975, penalty 0.0, cardinality 1.0); main 01-2 rank 12, 0.4422 (base 0.4422, penalty 0.0, cardinality 0.0); main 02-2.2 rank 14, 0.4117 (base 0.6117, penalty 0.2, cardinality 1.0); main 03-2 rank 11, 0.3754 (base 0.3754, penalty 0.0, cardinality 0.0); holdout best by rank 02-1 rank 12, 0.5897 reject (cardinality 1.0); holdout best by confidence 02-1 rank 12, 0.5897 reject; cardinality axis 0.0 / 1.0 / 1.0 / 0.0 across main 4 — frame strict 3 (3 aspect 카드) mismatches MDX answer-section item counts (answer_map 01-2 → F18, 02-2.2 → F14, 03-1 → F13, 03-2 → F29; F19 not an answer-section target)); #42 IMP-04b u18 TrackB VP contract-first — F-1171281197 (frame_number 19) cards-3-compare (compare family, 설계방식 왜곡 3열 대조 — 건설산업 설계 특성 / 단순 작업 대체 / 저부가가치 산업전략 3 aspect label+body 병치 카드); analysis.md slots 7 required 7 (title + aspect_1_label + aspect_1_body + aspect_2_label + aspect_2_body + aspect_3_label + aspect_3_body); cardinality ideal 3 / min 3 / max 3 (strict:3 = 3 aspect data axis, peer parity with u9 strict:6 / u10 strict:4 / u12 strict:4 / u13 strict:4 / u16 strict:5 / u17 strict:4 where ideal=min=max convention); sub_zones composite aspect_N convention per F16 precedent (`bim_issues_quadrant_four` quadrant_1..4 = composite, label/body 는 payload key not separate Frame Slot) — analysis.md aspect_N_label / aspect_N_body granularity = builder/parser internal (별 IMP), Frame Slot 단위 = composite aspect_N; visual_pending = figma_to_html source index.html 미작성 (TrackB contract-first per Stage 2 plan) + Phase Z family partial 미작성; builder/parser three_col_compare + aspect_label_body_item = new mapper wire 별 IMP. + template_id: design_method_distortion_three_col + frame_id: 1171281197 + family: compare + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 3 # 3 aspect (aspect_1..aspect_3) — data axis. analysis.md ideal 3 / min 3 / max 3 (strict:3 = declared data slot count, peer parity with u9/u10/u12/u13/u16/u17 ideal=min=max convention); title = fixed singleton header (non-data axis). aspect_N_label + aspect_N_body 평탄 = builder/parser 내부 (composite aspect_N = Frame Slot 단위, per F16 precedent). + overflow_policy: abort_or_review + + role_order: [aspect_1, aspect_2, aspect_3] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f19b__title" + - id: aspect_1 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f19b__aspect--1" + - id: aspect_2 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f19b__aspect--2" + - id: aspect_3 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f19b__aspect--3" + payload: + title: { source: section.title } + builder: three_col_compare + builder_options: + item_parser: aspect_label_body_item + + +commercial_sw_four_categories: + # Reason : V4 RJ=10 (all 10 sections label reject; best non-holdout by base 01-2 rank 11, 0.46 (base 0.46, penalty 0.0, cardinality 0.0); main 02-2.2 rank 13, 0.4376 (base 0.4376, penalty 0.0, cardinality 0.0); main 03-1 rank 16, 0.4246 (base 0.4246, penalty 0.0, cardinality 0.0); main 03-2 rank 12, 0.3651 (base 0.3651, penalty 0.0, cardinality 0.0); holdout best by confidence 04-2.2 rank 4, 0.6994 reject (cardinality 1.0); holdout best by rank 04-2.1 rank 4, 0.6907 reject (cardinality 1.0); cardinality axis 0.0 / 0.0 / 0.0 / 0.0 across main 4 — frame strict 4 (4 상용 S/W 카테고리) mismatches MDX answer-section item counts (answer_map 01-2 → F18, 02-2.2 → F14, 03-1 → F13, 03-2 → F29; F25 not an answer-section target)); #42 IMP-04b u19 TrackB VP contract-first — F-1171281205 (frame_number 25) left-categories-right-logos (cards family, 상용 Engn. S/W 4 카테고리 분류 — 조사/계획/분석 / 해석 / 단가/공정 / Graphic 4 category label 좌측 4열 + logos_panel 우측 시각 영역 2-boxes 구조); analysis.md slots 6 required 5 (title + category_1_label + category_2_label + category_3_label + category_4_label + logos_panel); cardinality ideal 4 / min 4 / max 4 (strict:4 = 4 category data axis, peer parity with u10 strict:4 / u12 strict:4 / u13 strict:4 / u17 strict:4 where ideal=min=max convention); sub_zones composite category_N convention per F16 precedent (`bim_issues_quadrant_four` quadrant_1..4 = composite); logos_panel = optional visual region (analysis.md "slots 6 required 5" → logos_panel 1 optional, S/W 로고 이미지 패널 figma_to_html assets-promotion target 별 IMP); visual_pending = figma_to_html source index.html 미작성 (TrackB contract-first per Stage 2 plan) + Phase Z family partial 미작성; builder/parser left_categories_right_logos + category_label_item = new mapper wire 별 IMP. + template_id: commercial_sw_four_categories + frame_id: 1171281205 + family: cards + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 4 # 4 category (category_1..category_4) — data axis. analysis.md ideal 4 / min 4 / max 4 (strict:4 = declared data slot count, peer parity with u10/u12/u13/u17 ideal=min=max convention); title = fixed singleton header (non-data axis); logos_panel = optional visual region (analysis.md "slots 6 required 5" → logos_panel 1 optional, S/W 로고 이미지 패널). + overflow_policy: abort_or_review + + role_order: [category_1, category_2, category_3, category_4] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f25b__title" + - id: category_1 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f25b__category--1" + - id: category_2 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f25b__category--2" + - id: category_3 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f25b__category--3" + - id: category_4 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f25b__category--4" + - id: logos_panel + role: visual + accepts: [text_block] + cardinality: { min: 0, max: 1 } # analysis.md "slots 6 required 5" — logos_panel = 1 optional slot (visual S/W 로고 패널, asset-promotion 별 IMP). + partial_target_path: ".f25b__logos" + payload: + title: { source: section.title } + builder: left_categories_right_logos + builder_options: + item_parser: category_label_item + + +sw_dependency_four_problems: + # Reason : V4 8 sections label reject + 2 holdout positives (04-2.1 restructure 0.8018 cardinality 1.0, 04-2.2 light_edit 0.8074 cardinality 1.0); best non-holdout by base 02-2.2 rank 9, 0.5327 (base 0.6327, penalty 0.1, cardinality 0.5); main 01-2 rank 9, 0.5107 (base 0.6107, penalty 0.1, cardinality 0.5); main 03-1 rank 12, 0.4453 (base 0.5453, penalty 0.1, cardinality 0.5); main 03-2 rank 13, 0.3196 (base 0.4196, penalty 0.1, cardinality 0.5); holdout best by base 04-2.2 rank 2, 0.8074 light_edit (cardinality 1.0); holdout 04-2.1 rank 1, 0.8018 restructure (cardinality 1.0); cardinality axis 0.5 / 0.5 / 0.5 / 0.5 across main 4 — frame strict 4 (4 상용 S/W 의존 문제) mismatches MDX answer-section item counts (answer_map 01-2 → F18, 02-2.2 → F14, 03-1 → F13, 03-2 → F29; F26 not an answer-section target but holdout 04-2.1/04-2.2 reach restructure/light_edit thresholds); #42 IMP-04b u20 TrackB VP contract-first — F-1171281206 (frame_number 26) cards-4-grid (cards family, 상용 S/W 의존 4대 문제 병렬 카드 — 독과점 생태계 형성 / 기술예속 심화 / S/W 사용료 과다 / 존폐 위기 4 problem label+body 카드 그리드); analysis.md slots 9 required 9 (title + problem_1_label + problem_1_body + problem_2_label + problem_2_body + problem_3_label + problem_3_body + problem_4_label + problem_4_body); cardinality ideal 4 / min 4 / max 4 (strict:4 = 4 problem data axis, peer parity with u10 strict:4 / u12 strict:4 / u13 strict:4 / u17 strict:4 / u19 strict:4 where ideal=min=max convention); sub_zones composite problem_N convention per F16 precedent (`bim_issues_quadrant_four` quadrant_1..4 = composite, label/body 는 payload key not separate Frame Slot) — analysis.md problem_N_label / problem_N_body granularity = builder/parser internal (별 IMP), Frame Slot 단위 = composite problem_N; visual_pending = figma_to_html source index.html 미작성 (TrackB contract-first per Stage 2 plan) + Phase Z family partial 미작성; builder/parser cards_4_grid + problem_label_body_item = new mapper wire 별 IMP. + template_id: sw_dependency_four_problems + frame_id: 1171281206 + family: cards + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 4 # 4 problem (problem_1..problem_4) — data axis. analysis.md ideal 4 / min 4 / max 4 (strict:4 = declared data slot count, peer parity with u10/u12/u13/u17/u19 ideal=min=max convention); title = fixed singleton header (non-data axis). problem_N_label + problem_N_body 평탄 = builder/parser 내부 (composite problem_N = Frame Slot 단위, per F16 precedent). + overflow_policy: abort_or_review + + role_order: [problem_1, problem_2, problem_3, problem_4] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f26b__title" + - id: problem_1 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f26b__problem--1" + - id: problem_2 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f26b__problem--2" + - id: problem_3 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f26b__problem--3" + - id: problem_4 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f26b__problem--4" + payload: + title: { source: section.title } + builder: cards_4_grid + builder_options: + item_parser: problem_label_body_item + + +policy_achievement_five_goals: + # Reason : V4 RJ=10 (all 10 sections label reject; best non-holdout by base 02-2.2 rank 24, 0.1896 (base 0.3896, penalty 0.2, cardinality 0.0); main 01-2 rank 31, 0.0683 (base 0.2683, penalty 0.2, cardinality 0.0); main 03-1 rank 23, 0.1813 (base 0.3813, penalty 0.2, cardinality 0.0); main 03-2 rank 30, 0.0515 (base 0.2515, penalty 0.2, cardinality 0.0); holdout best by confidence 04-1 rank 3, 0.6412 reject (cardinality 1.0); holdout best by rank 04-1 rank 3, 0.6412 reject; cardinality axis 0.0 / 0.0 / 0.0 / 0.0 across main 4 — frame strict 5 (5 핵심 목표 Easy/Convenient/Quality/Cost/Speed) mismatches MDX answer-section item counts (answer_map 01-2 → F18, 02-2.2 → F14, 03-1 → F13, 03-2 → F29; F32 not an answer-section target)); #42 IMP-04b u21 TrackB VP contract-first — F-1171281213 (frame_number 32) central-5-goals (composite family, 정책 달성 Engn. Solution / DfMA 프레임워크 + 5 핵심 목표 중앙 방사형 — Easy Like Breath / Convenient Intuition / Ultra High Quality / Super Low Cost / As Fast As Lightning 5 goal label+body 카드, 좌측 Engn. Solution / 우측 DfMA subtitle + description 박스 header composite); analysis.md slots 9 required 8 (title + subtitle_left + subtitle_right + description + goal_1 + goal_2 + goal_3 + goal_4 + goal_5; 1 optional = description per "required 8"); cardinality ideal 5 / min 5 / max 5 (strict:5 = 5 goal data axis, peer parity with u9 strict:6 / u10 strict:4 / u12 strict:4 / u13 strict:4 / u16 strict:5 / u17 strict:4 / u19 strict:4 / u20 strict:4 where ideal=min=max convention); sub_zones composite goal_N convention per F16 precedent (`bim_issues_quadrant_four` quadrant_1..4 = composite, label/body 는 payload key not separate Frame Slot) — Frame Slot 단위 = composite goal_N; subtitle_left / subtitle_right = header composite (Engn. Solution / DfMA 프레임워크 라벨), description = optional header composite (Engn. Solution + DfMA 설명 박스, analysis.md "slots 9 required 8" → 1 optional); visual_pending = figma_to_html source index.html 미작성 (TrackB contract-first per Stage 2 plan) + Phase Z family partial 미작성; builder/parser central_five_goals + goal_label_body_item = new mapper wire 별 IMP. + template_id: policy_achievement_five_goals + frame_id: 1171281213 + family: composite + visual_pending: true + + source_shape: top_bullets + cardinality: + strict: 5 # 5 goal (goal_1..goal_5) — data axis. analysis.md ideal 5 / min 5 / max 5 (strict:5 = declared data slot count, peer parity with u9/u10/u12/u13/u16/u17/u19/u20 ideal=min=max convention); title + subtitle_left + subtitle_right = fixed singleton header (non-data axis); description = optional header composite (analysis.md "slots 9 required 8" → description 1 optional, Engn. Solution + DfMA 설명 박스). goal_N_label + goal_N_body 평탄 = builder/parser 내부 (composite goal_N = Frame Slot 단위, per F16 precedent). + overflow_policy: abort_or_review + + role_order: [goal_1, goal_2, goal_3, goal_4, goal_5] + visual_hints: + min_height_px: 350 + accepted_content_types: [text_block] + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f32b__title" + - id: subtitle_left + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f32b__subtitle--left" + - id: subtitle_right + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f32b__subtitle--right" + - id: description + role: header + accepts: [text_block] + cardinality: { min: 0, max: 1 } # analysis.md "slots 9 required 8" — description = 1 optional slot (Engn. Solution + DfMA 설명 박스 header composite). + partial_target_path: ".f32b__description" + - id: goal_1 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f32b__goal--1" + - id: goal_2 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f32b__goal--2" + - id: goal_3 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f32b__goal--3" + - id: goal_4 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f32b__goal--4" + - id: goal_5 + role: data + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f32b__goal--5" + payload: + title: { source: section.title } + builder: central_five_goals + builder_options: + item_parser: goal_label_body_item + + +industry_current_status_three_col: + # Reason : V4 RJ=10 (all 10 sections label reject; best non-holdout 03-1 rank 6, 0.6048 (base 0.6048, penalty 0.0, cardinality 1.0); main 01-2 rank 7, 0.5763 (base 0.5763, penalty 0.0, cardinality 0.5); main 02-2.2 rank 16, 0.3497 (base 0.5497, penalty 0.2, cardinality 1.0); main 03-2 rank 6, 0.5324 (base 0.5324, penalty 0.0, cardinality 0.5); holdout best by confidence 01-1 rank 13, 0.5407 reject (cardinality 1.0); holdout best by rank 01-1 rank 13, 0.5407 reject; cardinality axis 0.5 / 1.0 / 1.0 / 0.5 across main 4 — frame table-3col (3 column 제조업/건축/토목 × N category rows) mismatches MDX answer-section item counts (answer_map 01-2 → F18, 02-2.2 → F14, 03-1 → F13, 03-2 → F29; F30 not an answer-section target)); #42 IMP-04b u22 TrackB VP contract-first — F-1171281211 (frame_number 30) table-3col (table family, 산업별 현황 3개 산업 × N 관점 다면 비교 — 제조업/건축/토목 3 column header + N category row (개요/핵심 Keyword/S/W 사용/발전방향/S/W별 시장규모 source 5 rows) + 토목 강조 테두리 visual cue + 우측 S/W 로고 panel ArchiCAD/SketchUp/Nemetschek 보조 이미지); analysis.md slots 5 required 5 (title + col_a_label + col_b_label + col_c_label + rows; 0 optional); cardinality ideal 3 / min 3 / max 3 (strict:3 = 3 column data axis, peer parity with bim_dx_comparison_table strict:2 = 2 column convention — strict = column count NOT row count); sub_zones col_a/col_b/col_c_header + rows[] convention per bim_dx_comparison_table precedent (header = singleton column label, rows = variable data axis); visual_pending = figma_to_html source index.html 미작성 (TrackB contract-first per Stage 2 plan) + Phase Z family partial 미작성; builder/parser compare_table_3col + compare_row_3col_item = new mapper wire 별 IMP (peer parity with bim_dx_comparison_table compare_table_2col + compare_row_2col_item). + template_id: industry_current_status_three_col + frame_id: 1171281211 + family: table + visual_pending: true + + source_shape: top_bullets + # NOTE : top-level `cardinality.strict: 3` = *column count* (col_a / col_b / col_c, fixed 3 산업). + # data row count 는 별 — `sub_zones.rows.cardinality` 의 `{min:1, max:12}`. Peer parity with + # bim_dx_comparison_table strict:2 (2 column compare) — 3 column extension. + cardinality: + strict: 3 # 3 columns (col_a / col_b / col_c, 제조업/건축/토목) — NOT row count. analysis.md ideal 3 / min 3 / max 3 (3 산업 고정 axis). + overflow_policy: abort_or_review + + role_order: [col_a, col_b, col_c] + visual_hints: + min_height_px: 350 # Peer parity with bim_dx_comparison_table (350 = N rows × ~40 + header/title/padding/buffer). Phase Z compact (title 30 + header 30 + 5 data rows × 50 + padding 30 + buffer 10) = 350 baseline; visual-pending 미렌더 estimate (smoke 검증 별 IMP). + + accepted_content_types: [text_block] + + # Frame Slot 선언 : header (title + col_a/col_b/col_c) + rows[] data axis. + # F30 source 강조 테두리 (토목 열 빨간색) + S/W 로고 panel = visual styling / optional asset + # (Frame Slot 분리 X, builder visual styling internal 처리 또는 visual-pending family partial 별 IMP). + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f30b__title" + - id: col_a_header + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f30b__header > .f30b__header-cell:nth-child(2)" + - id: col_b_header + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f30b__header > .f30b__header-cell:nth-child(3)" + - id: col_c_header + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f30b__header > .f30b__header-cell:nth-child(4)" + - id: rows + role: data + accepts: [text_block] + cardinality: { min: 1, max: 12 } # N category rows (typical 4-6; source 5 = 개요/핵심 Keyword/S/W 사용/발전방향/S/W별 시장규모). + partial_target_path: ".f30b__rows" + + payload: + title: { source: section.title } + builder: compare_table_3col # NEW builder placeholder — visual-pending; new mapper wire 별 IMP. Peer parity with bim_dx_comparison_table compare_table_2col. + builder_options: + item_parser: compare_row_3col_item # NEW parser placeholder — top_bullet → {label, col_a, col_b, col_c}. Peer parity with compare_row_2col_item. + col_a_label_default: "제조업" # F30 source column 1 label (analysis.md three_industries anchor set). + col_b_label_default: "건축" # F30 source column 2 label. + col_c_label_default: "토목" # F30 source column 3 label (강조 테두리 빨간색 cue — visual styling). + max_rows: 12 # typical 4-6, overflow 보호 (peer parity with compare_table_2col max_rows 12). + + +industry_characteristics_three_col: + # Reason : V4 RJ=10 (all 10 sections label reject; best non-holdout 03-1 rank 8, 0.6037 (base 0.6037, penalty 0.0, cardinality 1.0); main 01-2 rank 6, 0.5781 (base 0.5781, penalty 0.0, cardinality 0.5); main 02-2.2 rank 17, 0.3398 (base 0.5398, penalty 0.2, cardinality 1.0); main 03-2 rank 8, 0.4757 (base 0.4757, penalty 0.0, cardinality 0.5); holdout best by confidence 01-1 rank 11, 0.5467 reject (cardinality 1.0); holdout best by rank 01-1 rank 11, 0.5467 reject; cardinality axis 0.5 / 1.0 / 1.0 / 0.5 across main 4 — frame table-3col (3 column 제조업/건축/토목 × N category rows, compressed view of F30) mismatches MDX answer-section item counts (answer_map 01-2 → F18, 02-2.2 → F14, 03-1 → F13, 03-2 → F29; F31 not an answer-section target)); #42 IMP-04b u23 TrackB VP contract-first — F-1171281212 (frame_number 31) table-3col (table family, 산업별 특성과 발전방향 — F30 압축 뷰, 3개 산업 × N 관점 다면 비교 — 제조업/건축/토목 3 column header + N category row (개요/특징/S/W의 사용 source 3 rows; analysis.md 특성·S/W 적용·발전방향 3가지 관점) + 토목 강조 테두리 빨간색 visual cue); analysis.md slots 5 required 5 (title + col_a_label + col_b_label + col_c_label + rows; 0 optional); cardinality ideal 3 / min 3 / max 3 (strict:3 = 3 column data axis, peer parity with bim_dx_comparison_table strict:2 = 2 column convention + industry_current_status_three_col strict:3 = 3 column convention — strict = column count NOT row count); sub_zones col_a/col_b/col_c_header + rows[] convention per bim_dx_comparison_table + industry_current_status_three_col precedent (header = singleton column label, rows = variable data axis); visual_pending = figma_to_html source index.html 미작성 (TrackB contract-first per Stage 2 plan) + Phase Z family partial 미작성; builder/parser compare_table_3col + compare_row_3col_item = new mapper wire 별 IMP (peer parity with industry_current_status_three_col compare_table_3col + compare_row_3col_item — shared builder/parser across both 3-col table frames F30/F31). + template_id: industry_characteristics_three_col + frame_id: 1171281212 + family: table + visual_pending: true + + source_shape: top_bullets + # NOTE : top-level `cardinality.strict: 3` = *column count* (col_a / col_b / col_c, fixed 3 산업). + # data row count 는 별 — `sub_zones.rows.cardinality` 의 `{min:1, max:12}`. Peer parity with + # bim_dx_comparison_table strict:2 (2 column compare) + industry_current_status_three_col strict:3. + cardinality: + strict: 3 # 3 columns (col_a / col_b / col_c, 제조업/건축/토목) — NOT row count. analysis.md ideal 3 / min 3 / max 3 (3 산업 고정 axis). + overflow_policy: abort_or_review + + role_order: [col_a, col_b, col_c] + visual_hints: + min_height_px: 350 # Peer parity with bim_dx_comparison_table + industry_current_status_three_col (350 = N rows × ~40 + header/title/padding/buffer). Phase Z compact (title 30 + header 30 + 3 data rows × 70 + padding 30 + buffer 10) = 350 baseline (F31 rows fewer than F30 but per-row body 더 김); visual-pending 미렌더 estimate (smoke 검증 별 IMP). + + accepted_content_types: [text_block] + + # Frame Slot 선언 : header (title + col_a/col_b/col_c) + rows[] data axis. + # F31 source 강조 테두리 (토목 열 빨간색) = visual styling / optional asset + # (Frame Slot 분리 X, builder visual styling internal 처리 또는 visual-pending family partial 별 IMP). + sub_zones: + - id: title + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f31b__title" + - id: col_a_header + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f31b__header > .f31b__header-cell:nth-child(2)" + - id: col_b_header + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f31b__header > .f31b__header-cell:nth-child(3)" + - id: col_c_header + role: header + accepts: [text_block] + cardinality: { strict: 1 } + partial_target_path: ".f31b__header > .f31b__header-cell:nth-child(4)" + - id: rows + role: data + accepts: [text_block] + cardinality: { min: 1, max: 12 } # N category rows (typical 3-4; source 3 = 개요/특징/S/W의 사용). F31 compressed view of F30 — fewer rows but per-row body 더 김. + partial_target_path: ".f31b__rows" + + payload: + title: { source: section.title } + builder: compare_table_3col # Shared builder with industry_current_status_three_col (F30) — visual-pending; new mapper wire 별 IMP. Peer parity with bim_dx_comparison_table compare_table_2col. + builder_options: + item_parser: compare_row_3col_item # Shared parser with industry_current_status_three_col (F30) — top_bullet → {label, col_a, col_b, col_c}. Peer parity with compare_row_2col_item. + col_a_label_default: "제조업" # F31 source column 1 label (analysis.md three_industries anchor set; shared with F30). + col_b_label_default: "건축" # F31 source column 2 label (shared with F30). + col_c_label_default: "토목" # F31 source column 3 label (강조 테두리 빨간색 cue — visual styling; shared with F30). + max_rows: 12 # typical 3-4 (F31 compressed view), overflow 보호 (peer parity with compare_table_2col + F30 compare_table_3col max_rows 12). diff --git a/templates/phase_z2/families/_WIP_FILES.md b/templates/phase_z2/families/_WIP_FILES.md index 00abfca..092bbb4 100644 --- a/templates/phase_z2/families/_WIP_FILES.md +++ b/templates/phase_z2/families/_WIP_FILES.md @@ -18,8 +18,9 @@ Active contracted family count = **11**. Tracked basenames ↔ `frame_contracts. | File | Figma frame | Status | |---|---|---| -| `app_sw_package_vs_solution.html` | frame 23 (`1171281203`) | WIP — not in `frame_contracts.yaml`, not in runtime matcher set. | -| `pre_construction_model_info_stacked.html` | frame 9 (`1171281180`) | WIP — not in `frame_contracts.yaml`, not in runtime matcher set. | + +> **#42 IMP-04b u3 (2026-05-21)** — frame 23 (`1171281203`) partial absorbed → `frame_contracts.yaml::app_sw_package_vs_solution`. Counts in `## Baseline lock (2026-05-19)` are historical (post-u3: 11 tracked + 1 WIP / 12 contract). +> **#42 IMP-04b u4 (2026-05-21)** — frame 9 (`1171281180`) partial absorbed → `frame_contracts.yaml::pre_construction_model_info_stacked`. WIP family table now empty (post-u4: 13 tracked / 13 contract). These files are partials authored during Phase Z-2 MVP-1.5b exploration. They are **not** part of the contracted Phase Z runtime catalog and must not be enumerated by frame selection, matcher, or any Stage 3 pipeline surface. diff --git a/tests/test_family_contract_baseline.py b/tests/test_family_contract_baseline.py index f1cdf9e..d303ea1 100644 --- a/tests/test_family_contract_baseline.py +++ b/tests/test_family_contract_baseline.py @@ -6,11 +6,19 @@ documented in `templates/phase_z2/families/_WIP_FILES.md`. Any drift after this point (new family file on disk without a contract entry, or a contract entry pointing to a missing file) fails fast in CI. +IMP-04b (#42) extends the rule with a second exemption axis: catalog entries +flagged `visual_pending: true` are contracted but have no family partial on +disk yet (Track A/B VP frames). The invariant becomes: + + contracts == (disk - wip) ∪ vp + +where `vp ∩ disk == ∅` (VP entry with disk file = promotion overdue). + References: - docs/architecture/INTEGRATION-AUDIT-01-REPORT.md §10.2 F-2 - docs/architecture/IMP-18-SVG-GAP-REPORT.md L28/L30/L51 - templates/phase_z2/families/_WIP_FILES.md (WIP allowlist source) - - Gitea #52 (this reconciliation), #42 (promote/remove gate) + - Gitea #52 (this reconciliation), #42 (promote/remove gate + VP axis) Pattern mirrors `tests/test_catalog_invariant.py` — fail fast with explicit diff message if family ↔ contract surfaces drift. @@ -26,6 +34,7 @@ PROJECT_ROOT = Path(__file__).parent.parent FAMILIES_DIR = PROJECT_ROOT / "templates" / "phase_z2" / "families" CATALOG_PATH = PROJECT_ROOT / "templates" / "phase_z2" / "catalog" / "frame_contracts.yaml" WIP_DOC_PATH = FAMILIES_DIR / "_WIP_FILES.md" +V4_EVIDENCE_PATH = PROJECT_ROOT / "tests" / "matching" / "v4_full32_result.yaml" def _load_contract_keys() -> set[str]: @@ -43,14 +52,46 @@ def _load_wip_allowlist() -> set[str]: return {m.group(1) for m in re.finditer(r"`([A-Za-z0-9_\-]+)\.html`", text)} +def _load_v4_evidence_template_ids() -> set[str]: + """Unique `template_id` values across all V4 full32 judgments. + + IMP-04b (#42) closure gate derives its 32-frame target from V4 evidence + rather than a hardcoded count, so future Track C additions extend both + surfaces in lockstep. + """ + with V4_EVIDENCE_PATH.open(encoding="utf-8") as f: + v4 = yaml.safe_load(f) + return { + j["template_id"] + for sec in v4["mdx_sections"].values() + for j in sec.get("judgments_full32", []) + if "template_id" in j + } + + +def _load_vp_exempt_keys() -> set[str]: + """Catalog entries flagged `visual_pending: true` (IMP-04b / #42). + + VP = contracted but no family partial on disk yet (Track A/B VP frames). + Exempt from the disk-family existence check until the partial is authored. + """ + with CATALOG_PATH.open(encoding="utf-8") as f: + catalog = yaml.safe_load(f) + return { + k for k, v in catalog.items() + if isinstance(v, dict) and v.get("visual_pending") is True + } + + def test_contracts_set_equals_disk_families_minus_wip(): - """`frame_contracts.yaml` keys ↔ disk family stems minus WIP allowlist.""" + """`frame_contracts.yaml` keys ↔ (disk family stems − WIP) ∪ VP-exempt.""" contracts = _load_contract_keys() disk = _load_disk_family_stems() wip = _load_wip_allowlist() + vp = _load_vp_exempt_keys() expected = disk - wip missing = expected - contracts - extra = contracts - expected + extra = (contracts - vp) - expected assert not missing, ( f"Family files on disk without frame_contracts.yaml entry " f"(and not in _WIP_FILES.md): {sorted(missing)}. " @@ -58,8 +99,8 @@ def test_contracts_set_equals_disk_families_minus_wip(): "templates/phase_z2/families/_WIP_FILES.md as WIP." ) assert not extra, ( - f"frame_contracts.yaml has entries with no matching family file: " - f"{sorted(extra)}." + f"frame_contracts.yaml has entries with no matching family file " + f"(and not flagged `visual_pending: true`): {sorted(extra)}." ) @@ -78,3 +119,47 @@ def test_wip_allowlist_is_disk_only_and_uncontracted(): f"{sorted(leaked_into_contracts)}. Promote via #42 instead — " "WIP allowlist must be disk-only / uncontracted." ) + + +def test_vp_exempt_keys_are_contracted_and_disk_absent(): + """VP-exempt keys: must be in catalog (by construction) AND must not have + a family partial on disk (VP = pending partial authoring per #42).""" + contracts = _load_contract_keys() + disk = _load_disk_family_stems() + vp = _load_vp_exempt_keys() + leaked_outside_contracts = vp - contracts + has_disk_partial = vp & disk + assert not leaked_outside_contracts, ( + f"VP-exempt keys not in catalog: {sorted(leaked_outside_contracts)}." + ) + assert not has_disk_partial, ( + f"VP-exempt entries have a family partial on disk: " + f"{sorted(has_disk_partial)}. Drop `visual_pending: true` from the " + "catalog entry (authoring complete) instead of carrying the flag." + ) + + +def test_imp04b_closure_gate_v4_coverage_and_wip_empty(): + """IMP-04b (#42) u24 closure gate: catalog set-equals V4 evidence + WIP==0. + + Locks the 32/32 frame coverage by comparing catalog top-level keys to + `tests/matching/v4_full32_result.yaml` unique `template_id` values, and + asserts the WIP allowlist is empty (both partials absorbed in u3/u4). + """ + contracts = _load_contract_keys() + wip = _load_wip_allowlist() + v4_template_ids = _load_v4_evidence_template_ids() + missing = v4_template_ids - contracts + extra = contracts - v4_template_ids + assert not missing, ( + f"IMP-04b closure gate: V4 template_ids not in frame_contracts.yaml: " + f"{sorted(missing)}." + ) + assert not extra, ( + f"IMP-04b closure gate: frame_contracts.yaml has entries outside " + f"V4 evidence: {sorted(extra)}. Extend V4 evidence first or scope " + "the addition under a follow-up IMP." + ) + assert not wip, ( + f"IMP-04b closure gate: WIP allowlist must be empty: {sorted(wip)}." + )