"""IMP-95 u8 — capture final.html SHA baseline via the FULL Phase Z pipeline under PHASE_Z_B4_V4_EVIDENCE=OFF (default) for mdx 01/02/04/05. Runs ``src.phase_z2_pipeline.run_phase_z2_mvp1`` end-to-end for each mdx file in the Stage 2 u8 scope (01/02/04/05; mdx 03 is excluded per the ``mdx 03 정비`` lock). Each run writes a real ``final.html`` to disk at the production write site ``src/phase_z2_pipeline.py:5994-5996``; bytes are IMP-94-normalized and SHA-256 hashed, then frozen to ``tests/regression/fixtures/imp95_pre_baseline_sha.json``. The accompanying regression test ``tests/regression/test_b4_v4_evidence_off_sha_parity.py`` re-runs the same pipeline shape under flag OFF and asserts SHA equality with the frozen values. Under flag OFF, IMP-95 (u1~u7) is a strict no-op for ``final.html`` bytes — see the trace-only docstring at ``src/phase_z2_pipeline.py:86`` — so the captured baseline IS the immediate pre-IMP-95 reference even when captured with IMP-95 code already in tree. Reason for a separate baseline (vs. reusing 89a_pre_baseline_sha.json) ====================================================================== The 89a baseline was captured at HEAD ``6e9e3ee`` before subsequent working-tree changes accumulated (e.g. the Emergency P3/P4/P4b verbatim slot_payload builders), so the 89a SHAs no longer match the current flag-OFF pipeline output for mdx 02/04/05 — a pre-existing baseline drift orthogonal to IMP-95. A fresh baseline keyed to the current flag-OFF state isolates IMP-95's u8 regression axis from that upstream drift; the 89a baseline remains the load-bearing guard for the 89-a axis. IMP-94 marker normalization =========================== IMP-94 ``data-region-id`` / ``data-content-unit-id`` tokens (stamped at ``src/region_marker_stamper.py:131-135``) are stripped before hashing. The strip is anchored on ``leading space + attr token`` shape and is disjoint from the IMP-96 ``data-frame-slot-id`` axis by attribute name. Run from repo root:: python tests/regression/scripts/capture_imp95_pre_baseline.py Idempotent. Re-run only when an upstream mapper/render/template delta is reviewed and accepted as the new pre-IMP-95 reference. Refuses to run with PHASE_Z_B4_V4_EVIDENCE enabled (the flag-ON state is not the baseline axis). """ from __future__ import annotations import hashlib import json import os import re import sys import tempfile from datetime import datetime, timezone from pathlib import Path _REPO_ROOT = Path(__file__).resolve().parents[3] sys.path.insert(0, str(_REPO_ROOT)) sys.path.insert(0, str(_REPO_ROOT / "src")) import src.phase_z2_pipeline as pz2 # noqa: E402 _SAMPLES_DIR = _REPO_ROOT / "samples" / "mdx_batch" _MDX_BATCH = ("01.mdx", "02.mdx", "04.mdx", "05.mdx") # Stage 2 u8 scope _OUT_PATH = ( _REPO_ROOT / "tests" / "regression" / "fixtures" / "imp95_pre_baseline_sha.json" ) # IMP-94 additive marker strip patterns — must stay in sync with # tests/regression/test_b4_v4_evidence_off_sha_parity.py. _STRIP_REGION_ID_RE = re.compile(rb' data-region-id="[^"]*"') _STRIP_CONTENT_UNIT_ID_RE = re.compile(rb' data-content-unit-id="[^"]*"') def _strip_imp94_markers(raw_bytes: bytes) -> bytes: stripped = _STRIP_REGION_ID_RE.sub(b"", raw_bytes) stripped = _STRIP_CONTENT_UNIT_ID_RE.sub(b"", stripped) return stripped def _capture_one(mdx_file: str, runs_root: Path) -> dict: mdx_path = _SAMPLES_DIR / mdx_file assert mdx_path.exists(), f"sample missing: {mdx_path}" run_id = f"imp95_baseline_{mdx_path.stem}" pipeline_exit_code: int | None = None try: pz2.run_phase_z2_mvp1(mdx_path, run_id=run_id) except SystemExit as exc: pipeline_exit_code = ( int(exc.code) if isinstance(exc.code, int) else 1 ) final_html_path = runs_root / run_id / "phase_z2" / "final.html" assert final_html_path.exists(), ( f"final.html not written by pipeline: {final_html_path} " f"(pipeline_exit_code={pipeline_exit_code})" ) raw_bytes = final_html_path.read_bytes() assert len(raw_bytes) > 0, f"final.html is empty: {final_html_path}" normalized_bytes = _strip_imp94_markers(raw_bytes) return { "mdx_file": mdx_file, "run_id": run_id, "final_html_size_bytes": len(normalized_bytes), "sha256": hashlib.sha256(normalized_bytes).hexdigest(), "pipeline_exit_code": pipeline_exit_code, } def capture() -> dict: assert os.environ.get("PHASE_Z_B4_V4_EVIDENCE", "") == "", ( "PHASE_Z_B4_V4_EVIDENCE must be unset when capturing baseline " "(default-OFF state is the production-equivalent axis for u8). " "Refusing to run with the flag enabled." ) assert os.environ.get("PHASE_Z_B4_MAPPER_SOURCE", "") == "", ( "PHASE_Z_B4_MAPPER_SOURCE must also be unset — the u8 baseline " "is captured under both flags OFF (full pre-IMP-89/95 path)." ) _OUT_PATH.parent.mkdir(parents=True, exist_ok=True) with tempfile.TemporaryDirectory(prefix="imp95_baseline_") as tmp: runs_root = Path(tmp) original_runs_dir = pz2.RUNS_DIR pz2.RUNS_DIR = runs_root try: entries = [_capture_one(mf, runs_root) for mf in _MDX_BATCH] finally: pz2.RUNS_DIR = original_runs_dir return { "schema_version": 1, "axis": ( "IMP-95 u8 — final.html SHA baseline captured via FULL " "run_phase_z2_mvp1 pipeline under PHASE_Z_B4_V4_EVIDENCE=OFF " "and PHASE_Z_B4_MAPPER_SOURCE=OFF (defaults)" ), "description": ( "Frozen SHA-256 of `final.html` bytes (production write site " "src/phase_z2_pipeline.py:5994-5996) for mdx 01/02/04/05 " "under PHASE_Z_B4_V4_EVIDENCE OFF. Under flag OFF, IMP-95 " "(u1~u7) is strictly no-op for final.html (planner branch " "falls through to legacy _select_frame at u3; u4/u5/u6 " "additive telemetry confined to placement_trace per the " "trace-only docstring at src/phase_z2_pipeline.py:86). The " "u8 regression test asserts SHA equality with these frozen " "values, so any future code change that drifts the flag-OFF " "render output produces a mismatch and breaks the test. " "mdx 03 is excluded per Stage 2 u8 scope (mdx 03 정비 LOCK). " "Regenerate only when an upstream delta is reviewed and " "accepted as the new pre-IMP-95 reference." ), "captured_at_utc": ( datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") ), "renderer": { "entrypoint": "src.phase_z2_pipeline.run_phase_z2_mvp1", "write_site": "src/phase_z2_pipeline.py:5994-5996", "artifact_relpath": "//phase_z2/final.html", }, "mdx_batch": list(_MDX_BATCH), "mdx_files": {entry["mdx_file"]: entry for entry in entries}, "total_files": len(entries), } def main() -> None: data = capture() _OUT_PATH.write_text( json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8", ) print( f"wrote {_OUT_PATH} ({data['total_files']} files: " f"{', '.join(data['mdx_files'].keys())})" ) if __name__ == "__main__": main()