docs(#95): IMP-95 u11 status-board markers + idempotence/regex tests (docs+test only)
Some checks failed
Multi-MDX Regression (IMP-91) / multi-mdx-regression (push) Failing after 20s

- Add section 9 to PHASE-Z-PIPELINE-STATUS-BOARD.md carving section 3 item (j)
  into 8 IMP-95 sub-axes (j1-j8). j1-j5 = trace-only, j6-j8 = guarded.
- Marker grammar: <!-- IMP-95:<axis> -->VALUE<!-- /IMP-95 --> (distinct from
  IMP-91 grammar so scripts/update_status_board.py MARKER_RE cannot rewrite
  IMP-95 cells).
- Allowed value enum: {pending, trace-only, guarded, active}.
- tests/scripts/test_update_status_board.py: +1 import, +4 module-level
  constants, +3 test functions verifying marker presence/count (8), value
  domain enum, IMP-91 updater isolation against IMP-95 cells, and IMP-95
  regex rewrite idempotence. IMP-91 tests untouched.
- No production-code touched. Default-OFF flag posture preserved; all cells
  trace-only or guarded.
This commit is contained in:
2026-05-27 18:18:53 +09:00
parent 6e9e3ee1fb
commit 97b7833a1b
2 changed files with 74 additions and 0 deletions

View File

@@ -6,6 +6,7 @@ into the GitHub Actions workflow; these tests guard the contract.
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
@@ -14,6 +15,15 @@ sys.path.insert(0, str(REPO_ROOT / "scripts"))
import update_status_board as usb # noqa: E402
IMP95_BOARD_PATH = (
REPO_ROOT / "docs" / "architecture" / "PHASE-Z-PIPELINE-STATUS-BOARD.md"
)
IMP95_MARKER_RE = re.compile(
r"(<!-- IMP-95:(j\d+) -->)(.*?)(<!-- /IMP-95 -->)", re.DOTALL
)
IMP95_EXPECTED_AXES = {"j1", "j2", "j3", "j4", "j5", "j6", "j7", "j8"}
IMP95_ALLOWED_VALUES = {"pending", "trace-only", "guarded", "active"}
SAMPLE_REPORT = {
"tests": [
@@ -60,3 +70,43 @@ def test_update_board_text_is_idempotent() -> None:
once = usb.update_board_text(board, outcomes)
twice = usb.update_board_text(once, outcomes)
assert once == twice == "<!-- IMP-91:F2:05 -->PASS<!-- /IMP-91 -->"
def test_imp95_markers_present_and_well_formed() -> None:
"""IMP-95 sub-axis markers all present in section 9 with allowed values."""
board = IMP95_BOARD_PATH.read_text(encoding="utf-8")
matches = IMP95_MARKER_RE.findall(board)
axes = {axis for _, axis, _, _ in matches}
assert axes == IMP95_EXPECTED_AXES, (
f"IMP-95 axes drift: expected {IMP95_EXPECTED_AXES}, got {axes}"
)
for _, axis, value, _ in matches:
assert value in IMP95_ALLOWED_VALUES, (
f"IMP-95 {axis} value {value!r} outside allowed set {IMP95_ALLOWED_VALUES}"
)
def test_imp95_markers_isolated_from_imp91_updater() -> None:
"""`update_board_text` (IMP-91) MUST NOT rewrite IMP-95 markers — distinct closing tag."""
sample = (
"<!-- IMP-95:j1 -->trace-only<!-- /IMP-95 --> "
"<!-- IMP-91:F0:01 -->?<!-- /IMP-91 -->"
)
outcomes = {("F0", "01"): "PASS"}
rewritten = usb.update_board_text(sample, outcomes)
assert "<!-- IMP-95:j1 -->trace-only<!-- /IMP-95 -->" in rewritten
assert "<!-- IMP-91:F0:01 -->PASS<!-- /IMP-91 -->" in rewritten
def test_imp95_marker_rewrite_is_idempotent() -> None:
"""Regex-driven IMP-95 cell rewrite is byte-identical on second application."""
board = "<!-- IMP-95:j1 -->pending<!-- /IMP-95 -->"
def rewrite(text: str, value: str) -> str:
return IMP95_MARKER_RE.sub(
lambda m: f"{m.group(1)}{value}{m.group(4)}", text
)
once = rewrite(board, "trace-only")
twice = rewrite(once, "trace-only")
assert once == twice == "<!-- IMP-95:j1 -->trace-only<!-- /IMP-95 -->"