diff --git a/scripts/gen_ac_traceability.py b/scripts/gen_ac_traceability.py index 1f3f19c..40173ab 100644 --- a/scripts/gen_ac_traceability.py +++ b/scripts/gen_ac_traceability.py @@ -31,6 +31,9 @@ OUT_MD = ROOT / ".planning" / "AC-TRACEABILITY.md" _AC_HEADER_RE = re.compile(r"^\s*-\s*\*\*(AC-(?:\d+\.\d+[a-z]?|NEW-\d+))\*\*") _DEFERRED_RE = re.compile(r"deferred-hardware", re.IGNORECASE) +# Phase 2 / AC-doc Status annotation: `pending-phase-3 (SAFE-01)`, `pending-phase-4 (FDR-02)`, etc. +# Tracks ACs whose test coverage is intentionally deferred to a later phase. +_PENDING_RE = re.compile(r"pending-phase-\d+", re.IGNORECASE) def collect_acs_from_doc() -> dict[str, dict]: @@ -51,10 +54,16 @@ def collect_acs_from_doc() -> dict[str, dict]: m = _AC_HEADER_RE.match(line) if m: current_id = m.group(1) - acs[current_id] = {"deferred": False} + acs[current_id] = {"deferred": False, "deferred_reason": None} continue - if current_id is not None and _DEFERRED_RE.search(line): - acs[current_id]["deferred"] = True + if current_id is not None: + if _DEFERRED_RE.search(line): + acs[current_id]["deferred"] = True + acs[current_id]["deferred_reason"] = "hardware" + elif _PENDING_RE.search(line): + acs[current_id]["deferred"] = True + pm = _PENDING_RE.search(line) + acs[current_id]["deferred_reason"] = pm.group(0) return acs @@ -98,7 +107,7 @@ def render_md(doc_acs: dict[str, dict], test_map: dict[str, list[str]]) -> str: "", f"**ACs declared in acceptance_criteria.md:** {len(declared)}", f"**ACs covered by at least one test:** {len(declared_set & tested)}", - f"**ACs deferred to hardware:** {sum(1 for a in declared if doc_acs[a]['deferred'])}", + f"**ACs deferred (hardware or pending-phase):** {sum(1 for a in declared if doc_acs[a]['deferred'])}", "", "## AC -> Test mapping", "", @@ -107,8 +116,10 @@ def render_md(doc_acs: dict[str, dict], test_map: dict[str, list[str]]) -> str: ] for ac_id in declared: tests = test_map.get(ac_id, []) - if doc_acs[ac_id]["deferred"]: - status = "DEFERRED (hardware)" + meta = doc_acs[ac_id] + if meta["deferred"]: + reason = meta.get("deferred_reason") or "hardware" + status = f"DEFERRED ({reason})" elif tests: status = "OK" else: