Add Phase Z placement trace telemetry

- run B1/B2/B4 placement planning in trace-only mode
- record placement_trace per rendered zone in debug output
- preserve existing render output and visual routing behavior
This commit is contained in:
2026-05-04 10:32:26 +09:00
parent 02a6d44944
commit 425a3054c1

View File

@@ -30,7 +30,7 @@ import re
import shutil import shutil
import sys import sys
import time import time
from dataclasses import dataclass from dataclasses import asdict, dataclass
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
@@ -46,6 +46,7 @@ from phase_z2_mapper import (
FitError, FitError,
compute_capacity_fit, compute_capacity_fit,
get_contract, get_contract,
load_frame_contracts,
map_with_contract, map_with_contract,
) )
from phase_z2_classifier import classify_visual_runtime_check from phase_z2_classifier import classify_visual_runtime_check
@@ -57,6 +58,11 @@ from phase_z2_retry import (
) )
from phase_z2_failure_router import enrich_retry_trace_with_failure_classification from phase_z2_failure_router import enrich_retry_trace_with_failure_classification
# trace-only runtime 연결 v0 — B1 → B4 chain.
# final.html / mapper / render path 미영향. debug_zones[i].placement_trace 만 기록.
from phase_z2_content_extractor import extract_content_objects
from phase_z2_placement_planner import plan_placement
# ─── Constants ────────────────────────────────────────────────── # ─── Constants ──────────────────────────────────────────────────
@@ -1051,6 +1057,38 @@ def run_phase_z2_mvp1(mdx_path: Path, run_id: Optional[str] = None) -> Path:
min_height_px = visual_hints.get("min_height_px", DEFAULT_ZONE_MIN_HEIGHT_PX) min_height_px = visual_hints.get("min_height_px", DEFAULT_ZONE_MIN_HEIGHT_PX)
contract_frame_id = contract.get("frame_id") contract_frame_id = contract.get("frame_id")
# ─── trace-only runtime 연결 v0 — B1 → B4 chain (final.html 영향 X) ───
# B1~B4 의 dormant chain 을 *real MDX runtime data* 로 처음 호출.
# 결과 (PlacementPlan) = debug_zones[i].placement_trace 로 *기록만*.
# render path / mapper output / final.html 모두 미변경 — B5 baseline SHA 유지.
# B4 frame selection = catalog declaration order (V4 evidence 미사용 — 별 axis).
content_objects = extract_content_objects(synth_section)
placement_plan = plan_placement(
content_objects=content_objects,
frame_contracts=list(load_frame_contracts().values()),
section_id=synth_section.section_id,
)
mapper_frame_template_id = unit.frame_template_id
matches_mapper = (
placement_plan.selected_template_id == mapper_frame_template_id
)
match_note: Optional[str] = None
if not matches_mapper:
if placement_plan.selected_template_id is None:
match_note = "no_frame_covers_content_types"
else:
match_note = (
f"B4 selected '{placement_plan.selected_template_id}' but "
f"mapper uses '{mapper_frame_template_id}' (composition V4 rank-1)"
)
placement_trace = {
**asdict(placement_plan),
"mapper_frame_template_id": mapper_frame_template_id,
"frame_selection_matches_mapper": matches_mapper,
"frame_selection_match_note": match_note,
}
# ─── end trace-only runtime 연결 v0 ───
# mapper 시도 — 실패 (FitError) 시 zone 을 adapter_needed 로 표시하고 skip # mapper 시도 — 실패 (FitError) 시 zone 을 adapter_needed 로 표시하고 skip
try: try:
slot_payload = map_mdx_to_slots(synth_section, unit.frame_template_id) slot_payload = map_mdx_to_slots(synth_section, unit.frame_template_id)
@@ -1102,6 +1140,8 @@ def run_phase_z2_mvp1(mdx_path: Path, run_id: Optional[str] = None) -> Path:
"content_truncated_count": truncated_count, # None / N (builder 가 N 개 자름) "content_truncated_count": truncated_count, # None / N (builder 가 N 개 자름)
"assets_dir": str(assets_dir.relative_to(run_dir)) if assets_dir else None, "assets_dir": str(assets_dir.relative_to(run_dir)) if assets_dir else None,
"content_weight": content_weight, "content_weight": content_weight,
# trace-only runtime 연결 v0 — B1 → B2 → B4 chain 결과 (render 미영향).
"placement_trace": placement_trace,
}) })
# 6. Build layout CSS — horizontal-2 = dynamic heights (regression preserve), 그 외 = fr default # 6. Build layout CSS — horizontal-2 = dynamic heights (regression preserve), 그 외 = fr default