#!/usr/bin/env python3 """Generate REGISTER.md from the finding front-matter. RISK-WP-0001-T05. The index is generated because a hand-maintained one drifts and then lies. Edit the findings, re-run `make register`. """ from __future__ import annotations import datetime as dt import sys import register_lib as lib TODAY = dt.date.today() def date(value) -> dt.date | None: if isinstance(value, dt.date): return value if isinstance(value, str) and value not in ("", "unset"): return dt.date.fromisoformat(value) return None def check_cell(f: dict) -> str: when = lib.moment(f.get("next_check")) if when is None: return "—" if lib.now() >= when: return "**due**" return when.strftime("%Y-%m-%d %H:%MZ") def cadence_cell(f: dict) -> str: rung = f.get("cadence", "instant") streak = f.get("clean_streak", 0) return f"{rung} ({streak})" def sev_cell(f: dict) -> str: sev = f.get("severity", "unset") return f"**{sev}**" if sev in ("critical", "high") else sev def esc_cell(f: dict) -> str: esc = f.get("escalation", "unset") if esc in ("none", "unset"): return esc trigger = f.get("escalation_trigger") status = f.get("escalation_status", "") tail = f" (t{trigger}{', ' + status if status else ''})" if trigger else "" return f"**{esc}**{tail}" def render() -> str: fs = lib.findings() ns = lib.notes() open_fs = [f for f in fs if lib.watched(f.get("status"))] out = [ "# Register", "", f"Generated by `tools/register_index.py` from `findings/`. Do not edit by hand. Last built {TODAY}.", "", f"{len(open_fs)} live of {len(fs)} findings; {len(ns)} notes below the floor.", "", "## Findings", "", "| ID | Finding | System | Severity | Disclosure | Escalation | Fix owner | Status | Cadence | Next check |", "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |", ] for f in fs: out.append( "| [{id}]({path}) | {title} | {system} | {sev} | {disc} | {esc} | {owner} | {status} | {cadence} | {check} |".format( id=f["id"], path=f["_path"].relative_to(lib.REPO), title=str(f.get("title", "")).strip('"'), system=f.get("system", "—"), sev=sev_cell(f), disc=f.get("disclosure", "unset"), esc=esc_cell(f), owner=f.get("fix_owner", "—"), status=f.get("status", "—"), cadence=cadence_cell(f), check=check_cell(f), ) ) constraints = [f for f in fs if f.get("constraint")] if constraints: out += ["", "## Constraints", "", "Hazards created by acting in the wrong order. Each binds another finding's remediation.", "", "| From | Binds | Severity | Constraint |", "| --- | --- | --- | --- |"] for f in constraints: out.append("| {id} | {on} | **{sev}** | {text} |".format( id=f["id"], on=f.get("constraint_on", "—"), sev=f.get("constraint_severity", "—"), text=f["constraint"])) waits = [(f, w) for f in fs for w in (f.get("waiting_on") or [])] # fs is every finding, closed included if waits: out += ["", "## Waiting on someone", "", "Every wait resolves on its default date whether or not anyone answers.", "Silence never buys a softer grade — see `docs/method/dependencies.md`.", "", "| Finding | Who | What would change | Default if silent | On |", "| --- | --- | --- | --- | --- |"] for f, w in waits: out.append("| {id} | {who} | {chg} | {dflt} | {at} |".format( id=f["id"], who=w.get("who", "—"), chg=w.get("would_change", "—"), dflt=w.get("default", "—"), at=w.get("default_at", "—"))) embargoed = [f for f in fs if f.get("disclosure") == "embargoed"] if embargoed: out += ["", "## Embargoes", "", "Held from publication with a stated condition. A hold with no moving condition is a stall.", "", "| Finding | Since | Lifts when | Re-decided |", "| --- | --- | --- | --- |"] for f in embargoed: out.append("| {id} | {since} | {cond} | {rev} |".format( id=f["id"], since=f.get("embargo_since", "—"), cond=f.get("embargo_condition", "—"), rev=f.get("embargo_review", "—"))) if ns: out += ["", "## Notes (below the floor)", "", "Seen, deliberately not findings. Not graded, not reviewed, not published.", "", "| ID | Note | Why below the floor |", "| --- | --- | --- |"] for n in ns: out.append("| [{id}]({path}) | {title} | {why} |".format( id=n["id"], path=n["_path"].relative_to(lib.REPO), title=str(n.get("title", "")).strip('"'), why=str(n.get("floor_reason", "—")).strip('"'))) out += ["", "## How to read this", "", "Severity is `docs/method/severity.md`; disclosure `docs/method/disclosure.md`;", "escalation `docs/method/escalation.md`; the check cadence `docs/method/review.md`.", "Cadence is the ladder rung and the count of consecutive clean checks — a finding at `1q (9)`", "has held still for a long time; one at `instant (0)` moved recently. Anything wrong resets it.", "A constraint may be graded higher than the finding that carries it — read both.", ""] return "\n".join(out) if __name__ == "__main__": text = render() target = lib.REPO / "REGISTER.md" if "--check" in sys.argv: current = target.read_text(encoding="utf-8") if target.exists() else "" if current != text: print("REGISTER.md is stale — run `make register`") raise SystemExit(1) print("REGISTER.md is current") else: target.write_text(text, encoding="utf-8") print(f"wrote {target.relative_to(lib.REPO)}")