``)
+# matches the actual rendered shape, not just the synthetic F29 stub.
+# 2. F29 real-partial injection — proves the u1 stamper additively layers
+# ``data-region-id`` + ``data-content-unit-id`` on the F29 root WITHOUT
+# perturbing the inner ``data-frame-slot-id`` instances (#96 89-d axis,
+# disjoint by attribute name). Same property is asserted for F9.
+#
+# Source-file inspection (read-only) is the lightest credible evidence; no
+# Jinja render or full pipeline run is needed for this contract.
+
+
+_FAMILY_PARTIALS_DIR = "templates/phase_z2/families"
+
+# All 13 family partials. Each tuple = (file_basename, expected_template_id).
+# Sourced from `grep -n data-template-id templates/phase_z2/families/*.html`
+# on branch main (fresh validation 2026-05-26, Stage 1 EXIT REPORT evidence).
+_ALL_13_FAMILY_ANCHORS: tuple[tuple[str, str], ...] = (
+ ("app_sw_package_vs_solution.html", "app_sw_package_vs_solution"),
+ ("bim_current_problems_paired.html", "bim_current_problems_paired"),
+ ("bim_dx_comparison_table.html", "bim_dx_comparison_table"),
+ ("bim_issues_quadrant_four.html", "bim_issues_quadrant_four"),
+ ("construction_bim_three_usage.html", "construction_bim_three_usage"),
+ (
+ "construction_goals_three_circle_intersection.html",
+ "construction_goals_three_circle_intersection",
+ ),
+ ("dx_sw_necessity_three_perspectives.html", "dx_sw_necessity_three_perspectives"),
+ ("info_management_what_how_when.html", "info_management_what_how_when"),
+ ("pre_construction_model_info_stacked.html", "pre_construction_model_info_stacked"),
+ ("process_product_two_way.html", "process_product_two_way"),
+ ("sw_reality_three_emphasis.html", "sw_reality_three_emphasis"),
+ ("three_parallel_requirements.html", "three_parallel_requirements"),
+ ("three_persona_benefits.html", "three_persona_benefits"),
+)
+
+
+def _read_family_partial(basename: str) -> str:
+ from pathlib import Path
+
+ return Path(_FAMILY_PARTIALS_DIR, basename).read_text(encoding="utf-8")
+
+
+def test_u5_family_partials_count_is_13():
+ """u5 fixture guard — exactly 13 family partials carry the
+ ``data-template-id`` root anchor on branch main. Catches accidental
+ inventory drift before the per-partial tests run.
+ """
+ from pathlib import Path
+
+ files = sorted(Path(_FAMILY_PARTIALS_DIR).glob("*.html"))
+ assert len(files) == 13, (
+ f"family partial count drift: expected 13, found {len(files)}: "
+ f"{[f.name for f in files]}"
+ )
+ assert len(_ALL_13_FAMILY_ANCHORS) == 13
+
+
+def test_u5_stamper_injects_into_every_family_partial_root():
+ """u5 core — the u1 stamper must successfully inject
+ ``data-region-id`` + ``data-content-unit-id`` into the root
+ ``
`` of EACH of the 13 family
+ partials. Proves the regex anchor matches the real rendered shape
+ (not just the synthetic F29 stub used in u1 tests).
+ """
+ markers = [
+ {"region_id": "zone--top__region_0", "content_unit_id": "cu_test"},
+ ]
+ for basename, template_id in _ALL_13_FAMILY_ANCHORS:
+ partial_src = _read_family_partial(basename)
+ # Sanity: the anchor exists in the source.
+ anchor = f'data-template-id="{template_id}"'
+ assert anchor in partial_src, (
+ f"{basename}: data-template-id={template_id!r} anchor missing in source"
+ )
+ # Stamp.
+ out = stamp_zone_html(partial_src, markers)
+ # Both markers must land.
+ assert f'{REGION_ID_ATTR}="zone--top__region_0"' in out, (
+ f"{basename}: region_id marker not injected"
+ )
+ assert f'{CONTENT_UNIT_ID_ATTR}="cu_test"' in out, (
+ f"{basename}: content_unit_id marker not injected"
+ )
+ # Anchor preserved verbatim (additive only — root attrs untouched).
+ assert anchor in out, (
+ f"{basename}: data-template-id anchor lost after stamping"
+ )
+ # Exactly ONE stamp on the root (u1 contract: only first root div).
+ assert out.count(f'{REGION_ID_ATTR}="zone--top__region_0"') == 1, (
+ f"{basename}: region_id stamped more than once"
+ )
+
+
+def test_u5_stamper_idempotent_on_every_family_partial():
+ """u5 idempotence — re-stamping any of the 13 family partials with
+ different markers must NOT overwrite (first stamp wins). Mirrors the
+ u1 synthetic-shape idempotence guarantee against real partials.
+ """
+ first = [{"region_id": "r_first", "content_unit_id": "c_first"}]
+ second = [{"region_id": "r_OVERWRITE", "content_unit_id": "c_OVERWRITE"}]
+ for basename, _template_id in _ALL_13_FAMILY_ANCHORS:
+ partial_src = _read_family_partial(basename)
+ once = stamp_zone_html(partial_src, first)
+ twice = stamp_zone_html(once, second)
+ assert once == twice, f"{basename}: re-stamp mutated output"
+ assert "r_OVERWRITE" not in twice, (
+ f"{basename}: re-stamp overwrote region_id"
+ )
+ assert "c_OVERWRITE" not in twice, (
+ f"{basename}: re-stamp overwrote content_unit_id"
+ )
+
+
+def test_u5_stamper_passthrough_on_every_family_partial_with_empty_markers():
+ """u5 passthrough — every family partial with ``markers=[]`` must
+ return byte-equivalent source (deterministic no-op on the u2 fallback
+ surface). Confirms no non-live branch can leak markers via the chain.
+ """
+ for basename, _template_id in _ALL_13_FAMILY_ANCHORS:
+ partial_src = _read_family_partial(basename)
+ out = stamp_zone_html(partial_src, [])
+ assert out == partial_src, (
+ f"{basename}: passthrough on empty markers mutated source"
+ )
+ assert REGION_ID_ATTR not in out, (
+ f"{basename}: REGION_ID_ATTR leaked into passthrough output"
+ )
+ assert CONTENT_UNIT_ID_ATTR not in out, (
+ f"{basename}: CONTENT_UNIT_ID_ATTR leaked into passthrough output"
+ )
+
+
+def test_u5_f29_real_partial_injection_preserves_frame_slot_axis():
+ """u5 F29 real-partial — stamping the actual
+ ``process_product_two_way.html`` (F29) root with u1 markers must NOT
+ perturb the inner ``data-frame-slot-id`` instances (#96 89-d axis,
+ disjoint by attribute name). Six F29 inner slots
+ (process_column × 3 + product_column × 3) must remain identical
+ pre / post stamp.
+ """
+ f29 = _read_family_partial("process_product_two_way.html")
+
+ # Baseline counts on the unmodified partial (fresh-validation evidence).
+ baseline_process_col = f29.count('data-frame-slot-id="process_column"')
+ baseline_product_col = f29.count('data-frame-slot-id="product_column"')
+ assert baseline_process_col == 3, (
+ f"F29 baseline drift: expected 3 process_column, found {baseline_process_col}"
+ )
+ assert baseline_product_col == 3, (
+ f"F29 baseline drift: expected 3 product_column, found {baseline_product_col}"
+ )
+
+ out = stamp_zone_html(
+ f29,
+ [{"region_id": "zone--bottom__region_0", "content_unit_id": "cu_f29_root"}],
+ )
+
+ # u1 markers injected on the root.
+ assert f'{REGION_ID_ATTR}="zone--bottom__region_0"' in out
+ assert f'{CONTENT_UNIT_ID_ATTR}="cu_f29_root"' in out
+ # Root anchor still present verbatim.
+ assert 'data-template-id="process_product_two_way"' in out
+ # #96 axis (data-frame-slot-id) untouched — counts unchanged.
+ assert out.count('data-frame-slot-id="process_column"') == baseline_process_col
+ assert out.count('data-frame-slot-id="product_column"') == baseline_product_col
+ # u1 attribute name disjoint from #96 attribute name (defensive).
+ assert REGION_ID_ATTR != "data-frame-slot-id"
+ assert CONTENT_UNIT_ID_ATTR != "data-frame-slot-id"
+
+
+def test_u5_f9_real_partial_injection_preserves_frame_slot_axis():
+ """u5 F9 real-partial — same axis-isolation guarantee for the second
+ pre-existing ``data-frame-slot-id`` carrier
+ (``pre_construction_model_info_stacked.html``). Inner pill_dynamic
+ slot must remain identical pre / post stamp.
+ """
+ f9 = _read_family_partial("pre_construction_model_info_stacked.html")
+
+ baseline_pill = f9.count('data-frame-slot-id="pill_dynamic"')
+ assert baseline_pill >= 1, (
+ f"F9 baseline drift: expected ≥1 pill_dynamic, found {baseline_pill}"
+ )
+
+ out = stamp_zone_html(
+ f9,
+ [{"region_id": "zone--top__region_0", "content_unit_id": "cu_f9_root"}],
+ )
+
+ assert f'{REGION_ID_ATTR}="zone--top__region_0"' in out
+ assert f'{CONTENT_UNIT_ID_ATTR}="cu_f9_root"' in out
+ assert 'data-template-id="pre_construction_model_info_stacked"' in out
+ # #96 axis untouched.
+ assert out.count('data-frame-slot-id="pill_dynamic"') == baseline_pill
+
+
+# ─── u6 — integration / parity through render_slide ──────────────────────
+#
+# u6 contract (Stage 2 plan): "Test live stamping, P4b no-crash, MDX 01
+# strip-attr parity, and trace-to-DOM parity."
+#
+# 1. Live stamping — synthetic PlacementPlan + slot_assignments fed through
+# _derive_placement_markers (u3) → zones_data placement_markers → the
+# u2 render_slide chain → final HTML carries data-region-id +
+# data-content-unit-id on the family-partial root.
+# 2. P4b no-crash — a zone shape matching the u4 non-live P4b verbatim
+# recovery surface (placement_markers=[]) renders without crash and
+# without marker leak. Closes Codex #16 P4b crash-risk on the live
+# pipeline (in addition to the call-site ``or []`` fallback).
+# 3. Strip-attr parity — rendering the same zone WITH markers vs WITHOUT
+# markers, then stripping the two new IMP-94 attrs from the marked
+# output, must produce byte-equivalent HTML. Locks the issue body's
+# validation contract: "mdx 01-05 의 final.html SHA = byte-equivalent
+# except for new data-* attrs" at the render_slide layer (no MDX 01
+# pipeline run needed — synthetic zone exercises the same code path).
+# 4. Trace-to-DOM parity — placement_trace (asdict of PlacementPlan) ↔ DOM
+# ``[data-region-id]`` set, scoped to the u1 single-root-stamp contract:
+# the root region_id is exactly trace.slot_assignments[0].region_id
+# (only the first marker is consumed per zone in u1).
+
+
+_RENDER_TEMPLATE_ID = "bim_current_problems_paired"
+
+
+def _u6_layout_css() -> dict:
+ """Minimal valid layout_css for a single-zone slide.
+
+ Mirrors tests/test_phase_z2_text_path_stamper.py shape so u6 tests do
+ not rely on real Phase Z layout planning.
+ """
+ return {"areas": '"primary"', "cols": "1fr", "rows": "1fr"}
+
+
+def _u6_paired_slot_payload() -> dict:
+ """Synthetic slot_payload for the bim_current_problems_paired family.
+
+ Only row_1 is populated; rows 2-4 stay empty (mirrors u9 fixture).
+ """
+ payload: dict = {
+ "title": "u6 synthetic title",
+ "row_1_left_label": "u6 left pill",
+ "row_1_left_body": [{"text": "u6 left A", "indent": 0}],
+ "row_1_right_label": "u6 right pill",
+ "row_1_right_body": [{"text": "u6 right A", "indent": 0}],
+ }
+ for r in (2, 3, 4):
+ payload[f"row_{r}_left_label"] = f"u6 left {r}"
+ payload[f"row_{r}_left_body"] = []
+ payload[f"row_{r}_right_label"] = f"u6 right {r}"
+ payload[f"row_{r}_right_body"] = []
+ return payload
+
+
+def _u6_render_zone(zone: dict) -> str:
+ """Run render_slide on a single zone — embedded mode (no print-mode CSS)."""
+ from src.phase_z2_pipeline import render_slide
+
+ return render_slide(
+ slide_title="u6_title",
+ slide_footer=None,
+ zones_data=[zone],
+ layout_preset="single",
+ layout_css=_u6_layout_css(),
+ gap_px=14,
+ embedded_mode="embedded",
+ )
+
+
+def _u6_synthetic_plan():
+ """Build a 2-assignment PlacementPlan for live stamping / trace parity."""
+ from phase_z2_placement_planner import PlacementPlan, SlotAssignment
+
+ return PlacementPlan(
+ section_id="zone--primary",
+ selected_frame_id="1171281194",
+ selected_template_id=_RENDER_TEMPLATE_ID,
+ slot_assignments=[
+ SlotAssignment(
+ region_id="zone--primary__region_0",
+ content_unit_id="cu_u6_root",
+ frame_slot_id="row_1_left_body",
+ partial_target_path=".f17b__row1_left",
+ display_strategy="inline_full",
+ ),
+ SlotAssignment(
+ region_id="zone--primary__region_1",
+ content_unit_id="cu_u6_excess",
+ frame_slot_id="row_1_right_body",
+ partial_target_path=".f17b__row1_right",
+ display_strategy="inline_full",
+ ),
+ ],
+ )
+
+
+def test_u6_live_stamping_through_render_slide():
+ """u6 axis 1 — synthetic PlacementPlan flows through u3 projection →
+ u2 chain → u1 stamper. Final HTML carries the first marker's
+ data-region-id + data-content-unit-id on the family-partial root.
+ Excess markers (slot_assignments[1+]) are silently dropped by u1
+ (one root per zone).
+ """
+ from src.phase_z2_pipeline import _derive_placement_markers
+
+ plan = _u6_synthetic_plan()
+ markers = _derive_placement_markers(plan)
+ # u3 contract: 2 marker dicts (one per SlotAssignment).
+ assert len(markers) == 2
+ assert markers[0]["region_id"] == "zone--primary__region_0"
+ assert markers[0]["content_unit_id"] == "cu_u6_root"
+
+ zone = {
+ "position": "primary",
+ "template_id": _RENDER_TEMPLATE_ID,
+ "slot_payload": _u6_paired_slot_payload(),
+ "placement_markers": markers,
+ }
+ html = _u6_render_zone(zone)
+
+ # First marker stamped on root.
+ assert f'{REGION_ID_ATTR}="zone--primary__region_0"' in html
+ assert f'{CONTENT_UNIT_ID_ATTR}="cu_u6_root"' in html
+ # Excess marker NOT stamped (u1 consumes only markers[0]).
+ assert "cu_u6_excess" not in html
+ assert "zone--primary__region_1" not in html
+ # Family-partial root anchor preserved.
+ assert f'data-template-id="{_RENDER_TEMPLATE_ID}"' in html
+ # Exactly one root-level data-region-id stamped (one zone, one root).
+ assert html.count(f'{REGION_ID_ATTR}="zone--primary__region_0"') == 1
+
+
+def test_u6_p4b_path_no_crash_with_empty_placement_markers():
+ """u6 axis 2 — P4b verbatim recovery shape (placement_markers=[])
+ renders without crash and without marker leak. Mirrors the u4 site-2
+ ``emergency_p4b_verbatim_code`` zone surface end-to-end through
+ render_slide. Closes Codex #16 P4b crash-risk at the live render
+ layer (in addition to the call-site ``or []`` fallback).
+ """
+ zone = {
+ "position": "primary",
+ "template_id": _RENDER_TEMPLATE_ID,
+ "slot_payload": _u6_paired_slot_payload(),
+ "placement_markers": [],
+ }
+ # Must NOT raise.
+ html = _u6_render_zone(zone)
+ # No marker attributes leaked into the rendered slide.
+ assert REGION_ID_ATTR not in html, (
+ "empty placement_markers must not leak data-region-id into final.html"
+ )
+ assert CONTENT_UNIT_ID_ATTR not in html, (
+ "empty placement_markers must not leak data-content-unit-id into final.html"
+ )
+ # Sanity: family-partial root still present (no regression to render path).
+ assert f'data-template-id="{_RENDER_TEMPLATE_ID}"' in html
+
+
+def test_u6_strip_attr_parity_baseline_vs_marked():
+ """u6 axis 3 — strip-attr parity. Render the same zone WITH markers
+ and WITHOUT markers; stripping the two new IMP-94 attrs from the
+ marked output must reproduce the unmarked output byte-for-byte.
+
+ Locks the issue body validation contract: ``mdx 01-05 의 final.html
+ SHA = byte-equivalent except for new data-* attrs``. Synthetic zone
+ exercises the exact same render_slide code path as a live MDX 01 run,
+ so MDX 01 pipeline execution is not required for this property.
+ """
+ payload = _u6_paired_slot_payload()
+ zone_unmarked = {
+ "position": "primary",
+ "template_id": _RENDER_TEMPLATE_ID,
+ "slot_payload": payload,
+ "placement_markers": [],
+ }
+ zone_marked = {
+ "position": "primary",
+ "template_id": _RENDER_TEMPLATE_ID,
+ "slot_payload": _u6_paired_slot_payload(),
+ "placement_markers": [
+ {
+ "region_id": "zone--primary__region_0",
+ "content_unit_id": "cu_u6_strip",
+ }
+ ],
+ }
+ html_unmarked = _u6_render_zone(zone_unmarked)
+ html_marked = _u6_render_zone(zone_marked)
+
+ # Sanity: marked path actually emitted both attrs once.
+ assert html_marked.count(f'{REGION_ID_ATTR}="zone--primary__region_0"') == 1
+ assert html_marked.count(f'{CONTENT_UNIT_ID_ATTR}="cu_u6_strip"') == 1
+
+ # Strip exactly the two new attrs (preceded by a single space) from the
+ # marked output. The u1 stamper injects ``
`` so the leading space +
+ # attr token shape is deterministic.
+ stripped = html_marked.replace(
+ f' {REGION_ID_ATTR}="zone--primary__region_0"', "", 1
+ ).replace(
+ f' {CONTENT_UNIT_ID_ATTR}="cu_u6_strip"', "", 1
+ )
+ assert stripped == html_unmarked, (
+ "strip-attr parity violation: marked output minus the two new IMP-94 "
+ "attrs is NOT byte-equivalent to the unmarked baseline"
+ )
+
+
+def test_u6_trace_to_dom_parity():
+ """u6 axis 4 — placement_trace ↔ DOM region-id parity. ``asdict`` of
+ the live PlacementPlan (the value stored in ``debug_zones[i].placement_trace``
+ at `src/phase_z2_pipeline.py:6640-6645`) carries the same slot_assignments
+ that the u3 projection feeds to the stamper. The first slot_assignment's
+ region_id must appear in the rendered DOM exactly once, and no other
+ slot_assignment region_id may leak (u1 single-root contract).
+ """
+ import re
+ from dataclasses import asdict
+
+ from src.phase_z2_pipeline import _derive_placement_markers
+
+ plan = _u6_synthetic_plan()
+ trace = asdict(plan)
+ trace_region_ids = [sa["region_id"] for sa in trace["slot_assignments"]]
+ assert trace_region_ids == [
+ "zone--primary__region_0",
+ "zone--primary__region_1",
+ ]
+
+ zone = {
+ "position": "primary",
+ "template_id": _RENDER_TEMPLATE_ID,
+ "slot_payload": _u6_paired_slot_payload(),
+ "placement_markers": _derive_placement_markers(plan),
+ }
+ html = _u6_render_zone(zone)
+
+ # Parse all data-region-id values from the DOM.
+ dom_region_ids = re.findall(r'data-region-id="([^"]+)"', html)
+ # u1 contract: exactly one stamp per zone — first slot_assignment.
+ assert dom_region_ids == ["zone--primary__region_0"], (
+ f"DOM region-id set drift vs u1 single-root contract: {dom_region_ids}"
+ )
+ # Parity direction: DOM set ⊆ trace slot_assignments region-id set.
+ assert set(dom_region_ids).issubset(set(trace_region_ids)), (
+ "DOM region-id not present in placement_trace slot_assignments"
+ )
+ # Excess slot_assignment region_id MUST NOT leak (single-root contract).
+ assert "zone--primary__region_1" not in html
+
+
+# ─── u6 axes 3' + 4' — REAL MDX 01 pipeline subprocess parity ──────────
+#
+# Round #6 rewind (Stage 3 code-edit): Codex flagged the original u6 axis-3
+# (strip-attr parity) and axis-4 (trace-to-DOM parity) as a scope gap — they
+# exercised a synthetic single-zone render rather than the binding contract's
+# ``MDX 01 strip-attr parity vs current rendered output``. The synthetic
+# variants above remain (they pin the render_slide unit invariants), but the
+# axes below close the contract gap by running the actual ``samples/mdx_batch/
+# 01.mdx`` end-to-end through ``python -m src.phase_z2_pipeline`` (same
+# subprocess shape as IMP-91 u2 ``multi_mdx_runs``) and asserting the strip-
+# parity + trace-to-DOM invariants on the live ``data/runs//phase_z2/
+# final.html`` + ``debug.json``.
+#
+# Subprocess pattern mirrors ``tests/integration/test_multi_mdx_regression.py``
+# L44-71. Module-scoped cache so both axes share one MDX 01 invocation.
+#
+# Marked ``@pytest.mark.integration`` (defined in pyproject.toml) so the
+# heavy Selenium-invoking run is opt-in skippable via ``-m "not integration"``
+# but included in the default ``pytest -q tests`` sweep.
+
+
+_REPO_ROOT = Path(__file__).resolve().parent.parent
+_SAMPLES_BATCH_DIR = _REPO_ROOT / "samples" / "mdx_batch"
+_DATA_RUNS_DIR = _REPO_ROOT / "data" / "runs"
+_MDX01_FIXTURE_PATH = _SAMPLES_BATCH_DIR / "01.mdx"
+
+# Strip patterns — mirror the u1 stamper injection shape exactly
+# (leading single space + attr token). The stamper at
+# ``src/region_marker_stamper.py:131-135`` emits::
+#
+# f' {REGION_ID_ATTR}="{region_id}" {CONTENT_UNIT_ID_ATTR}="{content_unit_id}"'
+#
+# so the strip regex anchors on that ``(space + attr + ="value")`` shape.
+_STRIP_REGION_RE = re.compile(r' data-region-id="[^"]*"')
+_STRIP_CONTENT_UNIT_RE = re.compile(r' data-content-unit-id="[^"]*"')
+
+
+@pytest.fixture(scope="module")
+def mdx01_actual_pipeline_run() -> dict:
+ """Run ``samples/mdx_batch/01.mdx`` through the actual Phase Z pipeline.
+
+ Module-scoped so the two MDX-01 axes share a single subprocess
+ invocation (~30-90s). Returns a dict with the rendered final.html,
+ debug.json payload, run_dir Path, and the subprocess returncode for
+ diagnostic surfacing.
+
+ Mirrors the IMP-91 u2 ``multi_mdx_runs`` subprocess shape
+ (``tests/integration/test_multi_mdx_regression.py:44-71``) so the
+ invocation contract stays single-source-of-truth across the
+ acceptance suite. Fresh run per test session — no frozen artifact
+ dependency ([[feedback_validation_first_for_closed_issues]]).
+ """
+ if not _MDX01_FIXTURE_PATH.is_file():
+ pytest.skip(
+ f"MDX 01 fixture missing at {_MDX01_FIXTURE_PATH!s}; "
+ f"cannot run real-pipeline parity axes."
+ )
+ run_id = f"imp94_u6_mdx01_{uuid.uuid4().hex[:8]}"
+ cp = subprocess.run(
+ [
+ sys.executable,
+ "-m",
+ "src.phase_z2_pipeline",
+ str(_MDX01_FIXTURE_PATH),
+ run_id,
+ ],
+ capture_output=True,
+ text=True,
+ timeout=360,
+ cwd=str(_REPO_ROOT),
+ )
+ run_dir = _DATA_RUNS_DIR / run_id / "phase_z2"
+ final_html_path = run_dir / "final.html"
+ debug_json_path = run_dir / "debug.json"
+ assert final_html_path.is_file(), (
+ f"MDX 01 pipeline subprocess did not produce {final_html_path!s} "
+ f"(returncode={cp.returncode}); stderr tail: {cp.stderr[-1200:]}"
+ )
+ assert debug_json_path.is_file(), (
+ f"MDX 01 pipeline subprocess did not produce {debug_json_path!s} "
+ f"(returncode={cp.returncode}); stderr tail: {cp.stderr[-1200:]}"
+ )
+ return {
+ "run_id": run_id,
+ "run_dir": run_dir,
+ "returncode": cp.returncode,
+ "final_html": final_html_path.read_text(encoding="utf-8"),
+ "debug": json.loads(debug_json_path.read_text(encoding="utf-8")),
+ }
+
+
+@pytest.mark.integration
+def test_u6_mdx01_strip_attr_parity_real_pipeline(mdx01_actual_pipeline_run):
+ """u6 axis 3' (REAL MDX 01) — Round #6 rewind fix.
+
+ Binding contract (Stage 2 plan + issue body guardrail) ::
+
+ mdx 01-05 의 final.html SHA = byte-equivalent except for new
+ ``data-*`` attrs
+
+ Verified on the LIVE MDX 01 pipeline output (no synthetic shim):
+
+ (a) The current rendered final.html carries ≥ 1 ``data-region-id``
+ AND ≥ 1 ``data-content-unit-id`` — proves the u2 chain
+ executed end-to-end through the production render_slide.
+ (b) The two IMP-94 attrs appear in matched pairs (one per
+ stamped family-partial root, u1 single-root contract).
+ (c) Stripping every ``data-region-id="..."`` and
+ ``data-content-unit-id="..."`` token (with its leading space)
+ from the marked output produces a baseline with:
+ - zero IMP-94 markers,
+ - exact original counts of every other ``data-*`` attribute
+ that pre-existed before IMP-94 (``data-template-id``,
+ ``data-frame-id``, ``data-frame-slot-id`` — the #96 89-d
+ axis must remain disjoint),
+ - byte length = ``len(original) − Σ stamped attr bytes``
+ (mathematical byte-equivalence to the pre-stamper state).
+
+ (c) is the strongest expression of "byte-equivalent except for new
+ data-* attrs" available without a separate pre-IMP-94 fixture: the
+ stripped output's length exactly matches the original minus the
+ sum of stamped attr substring lengths, and every other attribute
+ is preserved verbatim with identical counts. Together these prove
+ the u2 stamper chain is purely additive on the real pipeline
+ output, matching the contract surface called out in the rewind
+ failure report.
+ """
+ html = mdx01_actual_pipeline_run["final_html"]
+
+ # (a) IMP-94 attrs present on live MDX 01 output.
+ region_attrs = _STRIP_REGION_RE.findall(html)
+ content_attrs = _STRIP_CONTENT_UNIT_RE.findall(html)
+ assert len(region_attrs) >= 1, (
+ f"MDX 01 final.html ({mdx01_actual_pipeline_run['run_id']}) carries no "
+ f"data-region-id — u2 stamper chain failed to execute end-to-end."
+ )
+ assert len(content_attrs) >= 1, (
+ f"MDX 01 final.html ({mdx01_actual_pipeline_run['run_id']}) carries no "
+ f"data-content-unit-id — u2 stamper chain failed end-to-end."
+ )
+
+ # (b) Paired stamps — one region_id + one content_unit_id per root.
+ assert len(region_attrs) == len(content_attrs), (
+ f"MDX 01 stamp pairing violation: {len(region_attrs)} region_id vs "
+ f"{len(content_attrs)} content_unit_id"
+ )
+
+ # Baseline counts of other attrs on the unstripped HTML.
+ baseline_template_id_count = html.count('data-template-id="')
+ baseline_frame_id_count = html.count('data-frame-id="')
+ baseline_frame_slot_id_count = html.count('data-frame-slot-id="')
+ # u1 single-root-per-zone contract: stamps ≤ family-partial roots.
+ assert len(region_attrs) <= baseline_template_id_count, (
+ f"MDX 01 stamp count {len(region_attrs)} exceeds family-partial root "
+ f"count {baseline_template_id_count} — single-root contract violated."
+ )
+
+ # (c) Strip exactly the IMP-94 attrs (leading space + attr token).
+ stripped = _STRIP_REGION_RE.sub("", html)
+ stripped = _STRIP_CONTENT_UNIT_RE.sub("", stripped)
+
+ # (c-1) No IMP-94 markers remain.
+ assert REGION_ID_ATTR not in stripped, (
+ "data-region-id leaked through strip regex"
+ )
+ assert CONTENT_UNIT_ID_ATTR not in stripped, (
+ "data-content-unit-id leaked through strip regex"
+ )
+
+ # (c-2) Other attrs preserved verbatim (additive-only invariant).
+ assert stripped.count('data-template-id="') == baseline_template_id_count, (
+ "data-template-id count drift after strip — strip regex over-matched"
+ )
+ assert stripped.count('data-frame-id="') == baseline_frame_id_count, (
+ "data-frame-id count drift after strip — strip regex over-matched"
+ )
+ assert stripped.count('data-frame-slot-id="') == baseline_frame_slot_id_count, (
+ "data-frame-slot-id count drift after strip — #96 axis must be disjoint"
+ )
+
+ # (c-3) Byte-length math: byte-equivalent to the pre-stamper hypothesis.
+ stamp_bytes = sum(len(s) for s in region_attrs) + sum(len(s) for s in content_attrs)
+ assert len(html) - stamp_bytes == len(stripped), (
+ f"byte-length parity violation: len(original)={len(html)}, "
+ f"stamp_bytes={stamp_bytes}, expected stripped len={len(html) - stamp_bytes}, "
+ f"actual stripped len={len(stripped)}"
+ )
+
+
+@pytest.mark.integration
+def test_u6_mdx01_trace_to_dom_parity_real_pipeline(mdx01_actual_pipeline_run):
+ """u6 axis 4' (REAL MDX 01) — Round #6 rewind fix.
+
+ Binding contract (issue body validation guardrail) ::
+
+ placement_trace ↔ DOM 의 ``[data-region-id]`` set 정합 검증
+
+ Verified on the LIVE MDX 01 pipeline output:
+
+ For each zone in ``debug.json["zones"]`` carrying a
+ ``placement_trace.slot_assignments`` list, the FIRST
+ ``slot_assignment.region_id`` MUST appear in the rendered
+ final.html exactly once (u1 single-root-per-zone contract via the
+ u3 projection that consumes only ``markers[0]``). All other
+ ``slot_assignment[i>0].region_id`` values MUST NOT appear in the
+ DOM — the u1 stamper consumes only the first marker per zone
+ (excess markers are reserved for the future per-slot stamper
+ tracked under #96 / 89-d).
+
+ Closes the rewind scope gap: the synthetic-plan version of this
+ axis above (``test_u6_trace_to_dom_parity``) does not exercise the
+ live debug.json ↔ final.html surface — only the in-process render_slide
+ call. This real-pipeline axis pins the parity invariant against
+ the actual ``data/runs//phase_z2/debug.json`` shape produced
+ by the u3 + u4 wiring through ``zones_data.append`` (live + non-live
+ branches).
+ """
+ html = mdx01_actual_pipeline_run["final_html"]
+ debug = mdx01_actual_pipeline_run["debug"]
+ zones = debug.get("zones", []) or []
+ assert len(zones) >= 1, (
+ f"MDX 01 debug.json ({mdx01_actual_pipeline_run['run_id']}) carries no "
+ f"zones — pipeline produced an empty zones list."
+ )
+
+ dom_region_ids = set(re.findall(r'data-region-id="([^"]+)"', html))
+
+ # Build expected DOM = first slot_assignment.region_id per zone with trace.
+ expected_first_slot_region_ids: set[str] = set()
+ excess_slot_region_ids: set[str] = set()
+ saw_any_trace = False
+ for zone in zones:
+ trace = zone.get("placement_trace")
+ if not isinstance(trace, dict):
+ continue
+ slot_assignments = trace.get("slot_assignments") or []
+ if not slot_assignments:
+ continue
+ saw_any_trace = True
+ for idx, sa in enumerate(slot_assignments):
+ if not isinstance(sa, dict):
+ continue
+ rid = sa.get("region_id") or ""
+ if not rid:
+ continue
+ if idx == 0:
+ expected_first_slot_region_ids.add(rid)
+ else:
+ excess_slot_region_ids.add(rid)
+
+ # If no zone carries a populated placement_trace, the live B4 path
+ # (u3 projection) did not execute for any zone. The DOM should then
+ # carry no IMP-94 markers — u4 non-live defaults guarantee
+ # ``placement_markers=[]`` so the u1 stamper is a no-op everywhere.
+ if not saw_any_trace:
+ assert dom_region_ids == set(), (
+ f"MDX 01 has no placement_trace zones but DOM still carries "
+ f"data-region-id markers: {sorted(dom_region_ids)} — u4 non-live "
+ f"default contract violated."
+ )
+ return
+
+ # Parity: DOM region_id set equals the first-slot region_id set.
+ assert dom_region_ids == expected_first_slot_region_ids, (
+ f"MDX 01 trace ↔ DOM parity drift:\n"
+ f" DOM region-ids: {sorted(dom_region_ids)}\n"
+ f" expected (first slot_assignment per zone): "
+ f"{sorted(expected_first_slot_region_ids)}\n"
+ f" excess slot region-ids (must NOT leak): "
+ f"{sorted(excess_slot_region_ids)}"
+ )
+
+ # Single-root contract: no excess slot_assignment region_id leaks to DOM.
+ for rid in excess_slot_region_ids:
+ assert rid not in html, (
+ f"excess slot_assignment region_id {rid!r} leaked into MDX 01 "
+ f"final.html — u1 single-root contract (markers[0] only) violated."
+ )