"""MDX04 partial preview — diagnostic only, NOT a Phase Z final. 목적: F16 (`bim_issues_quadrant_four`) 가 04-2.1 / 04-2.2 의 4 항목 구조와 시각적으로 정합하는지 사용자가 눈으로 확인. 방식: - V4 runtime 우회 (정식 Phase Z 아님) - F16 figma 원본 HTML 을 iframe 으로 임베드 (디자인 형태 그대로) - 04-2.1 / 04-2.2 의 MDX 4 항목을 옆에 시각화 (구조 비교) - 04-1 = frame library gap (5-card 구조, 매칭 frame 부재) placeholder - diagnostic banner + V4 metadata + debug.json 출력: data/runs/mdx04_partial_preview/index.html data/runs/mdx04_partial_preview/debug.json data/runs/mdx04_partial_preview/f16_original/ (figma 원본 + assets) """ import json import re import sys from datetime import datetime from html import escape from pathlib import Path import yaml ROOT = Path(__file__).resolve().parents[2] MDX_PATH = ROOT / "samples" / "mdx_batch" / "04.mdx" V4_RESULT = ROOT / "tests" / "matching" / "v4_full32_result.yaml" RUN_DIR = ROOT / "data" / "runs" / "mdx04_partial_preview" # ─── MDX 04 의 04-2.1 / 04-2.2 섹션 추출 (### bullet) ────────────── RE_SUBSECTION_HEAD = re.compile(r'^###\s+(\d+\.\d+)\s+(.+)$', re.MULTILINE) RE_TOP_BULLET = re.compile(r'^-\s+\*\*([^*]+)\*\*\s*$') def extract_subsection(text, num_label): """### {num_label} ... 부터 다음 ### 또는 --- 직전까지 추출.""" lines = text.split('\n') start = None for i, ln in enumerate(lines): m = RE_SUBSECTION_HEAD.match(ln.strip()) if m and m.group(1) == num_label: start = i break if start is None: return None, [] end = len(lines) for j in range(start + 1, len(lines)): s = lines[j].strip() if RE_SUBSECTION_HEAD.match(s) or s == '---': end = j break section_title = lines[start].lstrip('# ').strip() body_lines = lines[start + 1:end] # 4 항목 추출 (top bullet + nested bullets) items = [] cur = None for ln in body_lines: stripped = ln.strip() m = RE_TOP_BULLET.match(stripped) if m: if cur is not None: items.append(cur) cur = {'headline': m.group(1).strip(), 'subs': []} continue m2 = re.match(r'^-\s+(.+)$', stripped) if m2 and cur is not None and not stripped.startswith('- **'): cur['subs'].append(m2.group(1).strip()) if cur is not None: items.append(cur) return section_title, items def extract_section_04_1(text): """04-1 = ## 1. DX에 대한 인식.

카드 5 개 + 각 카드 안 인용 + bullet 3 개.""" lines = text.split('\n') start = None for i, ln in enumerate(lines): if ln.strip() == '## 1. DX에 대한 인식': start = i break if start is None: return None, [] end = len(lines) for j in range(start + 1, len(lines)): s = lines[j].strip() if s.startswith('## ') and s != '## 1. DX에 대한 인식': end = j break body = '\n'.join(lines[start:end]) #

라벨 + 다음

인용 +

', body): cards.append({'label': m.group(1).strip()}) return lines[start].lstrip('# ').strip(), cards # ─── V4 metadata lookup ────────────────────────────────────────── def get_f16_judgment(v4, section_id): sec = v4['mdx_sections'].get(section_id) if not sec: return None for e in sec['judgments_full32']: if e['frame_number'] == 16: return e return None def get_top1(v4, section_id): sec = v4['mdx_sections'].get(section_id) if not sec: return None j = sec.get('judgments_full32', []) return j[0] if j else None # ─── HTML 렌더링 ───────────────────────────────────────────────── def render_items_html(items): parts = ['
'] for i, it in enumerate(items, 1): parts.append('
') parts.append(f'
{i}. {escape(it["headline"])}
') if it['subs']: parts.append('') parts.append('
') parts.append('
') return '\n'.join(parts) def render_cards_html(cards): parts = ['
'] for i, c in enumerate(cards, 1): parts.append(f'
{i}. {escape(c["label"])}
') parts.append('
') return '\n'.join(parts) def render_v4_metadata_html(j, label_note=''): if j is None: return '
V4 entry not found
' axes = j.get('axes', {}) return f'''
V4 rank:{j["v4_full_rank"]} conf:{j["confidence"]:.4f} label:{j["label"]}
axes: anchor={axes.get("anchor", 0):.2f} · cardinality={axes.get("cardinality", 0):.2f} · relation={axes.get("relation", 0):.2f} · slot={axes.get("slot", 0):.2f} · content={axes.get("content", 0):.4f}
{f'
{label_note}
' if label_note else ''}
''' def render_section(section_id, mdx_title, items_html, j_f16, top1, note): """좌: F16 figma 원본 iframe / 우: MDX 텍스트 4 항목 / 하: V4 metadata.""" label_note = note return f'''

{escape(section_id)} · {escape(mdx_title)}

F16 (bim_issues_quadrant_four) candidate · top1 = F{top1["frame_number"]} ({top1["label"]}, conf {top1["confidence"]:.4f})
F16 figma 원본 (디자인 형태)
MDX 04 {escape(section_id)} 본문 (4 항목)
{items_html}
{render_v4_metadata_html(j_f16, label_note)}
''' def render_04_1_placeholder(top1): return f'''

04-1 · DX에 대한 인식

Frame library gap — 5-card 구조, 32 frame DB 에 cardinality.ideal=5 frame 부재 (이번 preview 제외)
왜 제외: 04-1 은 5 개 카드 (기술/효과/인력/경제/실무) — h3_cards=5 인식까지는 정상. 다만 32 frame 중 5-card 대응 frame 이 없어 V4 multi-constraint 통과 가능 frame 자체가 없음 (사용 가능 0/32, 모두 reject). 이건 detect bug 가 아니라 frame library readiness 문제.

V4 top1 = F{top1["frame_number"]} (conf {top1["confidence"]:.4f}, {top1["label"]}) — F16 도 rank 15, conf 0.361, reject.
''' # ─── 메인 ──────────────────────────────────────────────────────── def main(): if not V4_RESULT.exists(): print(f"ERROR: V4 result not found at {V4_RESULT}", file=sys.stderr) sys.exit(1) if not MDX_PATH.exists(): print(f"ERROR: MDX 04 not found at {MDX_PATH}", file=sys.stderr) sys.exit(1) mdx_text = MDX_PATH.read_text(encoding='utf-8') v4 = yaml.safe_load(V4_RESULT.read_text(encoding='utf-8')) # 04-2.1 title_2_1, items_2_1 = extract_subsection(mdx_text, '2.1') j16_2_1 = get_f16_judgment(v4, '04-2.1') top1_2_1 = get_top1(v4, '04-2.1') # 04-2.2 title_2_2, items_2_2 = extract_subsection(mdx_text, '2.2') j16_2_2 = get_f16_judgment(v4, '04-2.2') top1_2_2 = get_top1(v4, '04-2.2') # 04-1 title_1, cards_1 = extract_section_04_1(mdx_text) top1_1 = get_top1(v4, '04-1') # HTML 조립 section_2_1_html = render_section( '04-2.1', title_2_1, render_items_html(items_2_1), j16_2_1, top1_2_1, note='F16 candidate — V4 label=reject (anchor=0). 의미 매칭 회복했으나 anchor terms 부재로 multi-constraint 탈락. preview 목적 = F16 디자인 / 04-2.1 본문 정합성 시각 확인.' ) section_2_2_html = render_section( '04-2.2', title_2_2, render_items_html(items_2_2), j16_2_2, top1_2_2, note='F16 restructure — V4 label=restructure 통과 (사용 가능 라벨). preview 목적 = F16 디자인이 04-2.2 본문에 시각적으로 fit 한지 확인.' ) section_1_html = render_04_1_placeholder(top1_1) timestamp = datetime.now().isoformat(timespec='seconds') page_html = f''' MDX04 Partial Preview · diagnostic only {section_2_1_html} {section_2_2_html} {section_1_html} ''' out_html = RUN_DIR / "index.html" out_html.write_text(page_html, encoding='utf-8') debug = { 'kind': 'mdx04_partial_preview', 'is_phase_z_final': False, 'is_diagnostic': True, 'purpose': 'F16 디자인 / 04-2.* 4 항목 구조 시각 정합성 확인', 'generated_at': timestamp, 'v4_source': str(V4_RESULT.relative_to(ROOT)), 'mdx_source': str(MDX_PATH.relative_to(ROOT)), 'sections': { '04-2.1': { 'mdx_title': title_2_1, 'item_count': len(items_2_1), 'top1': top1_2_1, 'f16_judgment': j16_2_1, 'preview_label': 'F16 candidate (V4 label = reject, conf 0.648, anchor=0)', }, '04-2.2': { 'mdx_title': title_2_2, 'item_count': len(items_2_2), 'top1': top1_2_2, 'f16_judgment': j16_2_2, 'preview_label': 'F16 restructure (V4 label = restructure, 사용 가능 통과)', }, '04-1': { 'mdx_title': title_1, 'card_count': len(cards_1), 'top1': top1_1, 'preview_label': 'EXCLUDED — frame library gap (5-card structure, no matching frame in 32 DB)', }, }, 'caveats': [ '정식 Phase Z final 아님 — V4 runtime / mapper / partial 모두 우회', 'F16 figma 원본 HTML 을 그대로 임베드 — 디자인 형태만 시각화 (텍스트 슬롯 매핑 X)', '04-2.1 의 F16 V4 label = reject (anchor=0) — 의미 매칭 회복했으나 anchor terms 부재', '04-2.2 의 F16 V4 label = restructure — 사용 가능 라벨, 단 정식 partial 미작성', '04-1 = frame library readiness 문제 (detect bug 아님)', ], } out_debug = RUN_DIR / "debug.json" out_debug.write_text(json.dumps(debug, ensure_ascii=False, indent=2), encoding='utf-8') print(f"[mdx04_partial_preview] generated:") print(f" html : {out_html}") print(f" debug : {out_debug}") print(f" figma : {RUN_DIR / 'f16_original' / 'index.html'}") if __name__ == "__main__": main()