Files
C.E.L_Slide_test2/scripts/sync_phase_z_run_to_frontend.py
KyeongminandClaude Opus 4.8 b836e79ee1 wip: phase_z2 evidence 파이프라인 + matching 실험(phase2~26) + 프론트 trace 패널 진행분 스냅샷
- src: phase_z2 composition/mapper/pipeline/placement_planner/retry, ai_fallback(prompts/schema/validate), mdx_text_atoms 신규
- Front: PipelineTracePanel 신규, FramePanel/SlideCanvas/Home/designAgentApi 등 갱신 + 테스트 4종 추가
- templates/phase_z2: catalog(component_expansion_registry, node_slot_mapping 신규), frames, families, slide_base 갱신
- tests/matching: phase2~26 매칭 실험 스크립트·리포트·온톨로지 전체 (미커밋 진행분)
- tests: b4_v4 evidence, task5~28.5 시리즈, regression(imp95 baseline) 등 신규 테스트 대량 추가
- docs/reference: MDX 구조 인벤토리, MDX→Frame 구조 계약 문서
- scripts: mdx 계약/parity/coverage/viewport 체크, gitea comment, run sync 유틸
- .gitignore: tmp*.json, chromedriver, .orchestrator, *.pkl, Front_test* 등 임시/스냅샷 제외

미완성 작업의 보존용 스냅샷 커밋 (2026-07-02)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 17:03:42 +09:00

180 lines
5.7 KiB
Python

#!/usr/bin/env python3
"""
Phase Z run 산출물을 frontend prototype 의 client/public/data/ 로 정적 export.
Usage:
python scripts/sync_phase_z_run_to_frontend.py [run_id]
python scripts/sync_phase_z_run_to_frontend.py mdx03_f29_fix_check
python scripts/sync_phase_z_run_to_frontend.py --list # 사용 가능한 runs 목록
성격:
- Static export (Phase 1 — 보고용 read-only viewer prototype).
- 향후 Phase 2 에서 Express endpoint (server/index.ts) 로 대체 가능.
- Frontend 의 designAgentApi.ts 의 loadRun(runId) 가 본 산출물을 fetch.
복사 대상 (Phase Z runtime 결과 → frontend type 매핑에 필요한 최소 set):
root: final.html / preview.png / debug.json
steps: step01_mdx_source.md (MDX 원문)
step01_mdx_upload.json (run metadata)
step02_normalized.json (sections)
step05_v4_evidence.json (V4 후보 list)
step06_composition_plan.json (composition units)
step07_layout.json (layout preset + candidates)
step08_zone_region_ratios.json (zone/region/display)
step09_application_plan.json (적용 계획 — 핵심)
step20_slide_status.json (최종 상태)
"""
from __future__ import annotations
import argparse
import shutil
import sys
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parent.parent
RUNS_DIR = PROJECT_ROOT / "data" / "runs"
FRONTEND_DATA_DIR = Path(
"D:/ad-hoc/kei/design_agent_front/design-agent/client/public/data/runs"
)
ROOT_FILES = ["final.html", "preview.png", "debug.json"]
STEP_FILES = [
"step01_mdx_source.md",
"step01_mdx_upload.json",
"step02_normalized.json",
"step05_v4_evidence.json",
"step06_composition_plan.json",
"step07_layout.json",
"step08_zone_region_ratios.json",
"step09_application_plan.json",
"step20_slide_status.json",
]
def list_runs() -> list[Path]:
"""data/runs/*/phase_z2 가 있는 run dir 목록."""
if not RUNS_DIR.exists():
return []
return sorted(
p for p in RUNS_DIR.iterdir()
if p.is_dir() and (p / "phase_z2").exists()
)
def sync_run(run_id: str, *, verbose: bool = True) -> int:
"""run_id 의 phase_z2 산출물을 frontend 로 복사. 복사된 파일 수 반환."""
src = RUNS_DIR / run_id / "phase_z2"
if not src.exists():
print(f"\n[error] source not found: {src}", file=sys.stderr)
return 0
dst = FRONTEND_DATA_DIR / run_id
dst.mkdir(parents=True, exist_ok=True)
(dst / "steps").mkdir(exist_ok=True)
copied = 0
missing: list[str] = []
for fname in ROOT_FILES:
src_f = src / fname
if src_f.exists():
shutil.copy2(src_f, dst / fname)
copied += 1
else:
missing.append(fname)
for fname in STEP_FILES:
src_f = src / "steps" / fname
if src_f.exists():
shutil.copy2(src_f, dst / "steps" / fname)
copied += 1
else:
missing.append(f"steps/{fname}")
if verbose:
print(f"\n[ok] {run_id}")
print(f" src : {src}")
print(f" dst : {dst}")
print(f" copied : {copied} files")
if missing:
print(f" missing: {len(missing)} files (산출물 없음 — 정상일 수 있음)")
for m in missing:
print(f" - {m}")
return copied
def main() -> None:
parser = argparse.ArgumentParser(
description="Phase Z run → frontend prototype 정적 export"
)
parser.add_argument(
"run_id",
nargs="?",
default=None,
help="복사할 run id. 미지정 시 가장 최근 mdx03_* run 사용.",
)
parser.add_argument(
"--list",
action="store_true",
help="사용 가능한 run 목록만 출력 후 종료.",
)
parser.add_argument(
"--all-mdx03",
action="store_true",
help="mdx03_* 로 시작하는 모든 run 복사.",
)
args = parser.parse_args()
if args.list:
runs = list_runs()
print(f"\nAvailable runs ({len(runs)}):")
for r in runs:
print(f" - {r.name}")
return
# client/public 자체는 존재해야 (frontend repo 가 정상이라는 sanity check)
frontend_public = FRONTEND_DATA_DIR.parent.parent # = client/public
if not frontend_public.exists():
print(
f"\n[error] frontend client/public 폴더 없음: {frontend_public}",
file=sys.stderr,
)
print(
" FRONTEND_DATA_DIR 경로 본 script 안에서 확인 필요.",
file=sys.stderr,
)
sys.exit(1)
# data/runs 가 없으면 자동 생성 (정적 export 첫 실행 시)
FRONTEND_DATA_DIR.mkdir(parents=True, exist_ok=True)
if args.all_mdx03:
runs = [r for r in list_runs() if r.name.startswith("mdx03_")]
print(f"\nSyncing {len(runs)} mdx03_* runs ...")
total = 0
for r in runs:
total += sync_run(r.name, verbose=True)
print(f"\n[done] total: {total} files across {len(runs)} runs")
return
if args.run_id is None:
# default: 가장 최근 mdx03_* run
candidates = [r for r in list_runs() if r.name.startswith("mdx03_")]
if not candidates:
print(
"\n[error] mdx03_* run 없음. 명시적으로 run_id 지정 필요.",
file=sys.stderr,
)
sys.exit(1)
# mtime 기준 최신
latest = max(candidates, key=lambda p: p.stat().st_mtime)
args.run_id = latest.name
print(f"\n[default] 가장 최근 mdx03_* run = {args.run_id}")
sync_run(args.run_id, verbose=True)
if __name__ == "__main__":
main()