RISK-WP-0005 finished: the seven gaps closed
T01 fix tracking now reads the owner's workplan file and found two findings the register should have known about. T02 incident and external report intake, the latter routed since the address is not ours to create. T03 the production transition defined by what is held rather than what was announced. T04 the README stops claiming a surface. T05 escalation carries a delivery state and is raised once when unacknowledged. T06 checked_by and a heartbeat, so a 1q rung cannot silently mean nobody looked. T07 coverage: 7 of 117 repos have ever appeared in a finding. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
449307bea2
commit
a05ca6822b
14 changed files with 195 additions and 26 deletions
BIN
tools/__pycache__/fix_tracker.cpython-312.pyc
Normal file
BIN
tools/__pycache__/fix_tracker.cpython-312.pyc
Normal file
Binary file not shown.
65
tools/coverage.py
Normal file
65
tools/coverage.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/env python3
|
||||
"""What has never been looked at.
|
||||
|
||||
RISK-WP-0005-T07, deliberately minimal. The register knows what was reported.
|
||||
Without this it has no view of what was never assessed, so a system with zero
|
||||
findings is indistinguishable from a system nobody has examined — while
|
||||
RISK-N-0003 records that every repo which *has* examined its own boundary this
|
||||
month found a defect.
|
||||
|
||||
This does not assess anything and does not grade anyone. It counts.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
import register_lib as lib
|
||||
|
||||
HUB = "http://127.0.0.1:8000/repos/"
|
||||
|
||||
|
||||
def repos() -> list[dict] | None:
|
||||
for url in (HUB, "http://127.0.0.1:8000/repos"):
|
||||
try:
|
||||
with urllib.request.urlopen(url, timeout=8) as r:
|
||||
data = json.load(r)
|
||||
return data if isinstance(data, list) else data.get("items", [])
|
||||
except (urllib.error.URLError, TimeoutError, json.JSONDecodeError, OSError):
|
||||
continue
|
||||
return None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
all_repos = repos()
|
||||
seen: dict[str, list[str]] = {}
|
||||
for f in lib.findings():
|
||||
for key in (f.get("system"), f.get("fix_owner"), f.get("reported_by")):
|
||||
if key and isinstance(key, str):
|
||||
seen.setdefault(key, []).append(f["id"])
|
||||
|
||||
print("Coverage — what the register has heard from\n")
|
||||
print(f" systems that have produced or carried a finding: {len(seen)}")
|
||||
for name in sorted(seen):
|
||||
print(f" {name}: {', '.join(sorted(set(seen[name])))}")
|
||||
|
||||
if all_repos is None:
|
||||
print("\n Hub unreachable — the denominator is unknown, which is the whole point of this report.")
|
||||
return 0
|
||||
|
||||
names = {r.get("slug") for r in all_repos if r.get("slug")}
|
||||
unheard = sorted(names - set(seen))
|
||||
print(f"\n registered repos: {len(names)}")
|
||||
print(f" never appeared in any finding: {len(unheard)}")
|
||||
print("\n A repo in that list has either nothing wrong with it or nobody looking.")
|
||||
print(" This register cannot tell which, and does not guess.\n")
|
||||
for name in unheard[:40]:
|
||||
print(f" {name}")
|
||||
if len(unheard) > 40:
|
||||
print(f" … and {len(unheard) - 40} more")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
|
@ -13,6 +13,7 @@ written down, which is the same rule the register applies to everyone else.
|
|||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
|
@ -62,6 +63,7 @@ def main(argv: list[str]) -> int:
|
|||
fail("outcome must be one of: clean, moved, defer")
|
||||
|
||||
subs = {
|
||||
"checked_by": os.environ.get("RISK_CHECKED_BY", os.environ.get("USER", "unknown")),
|
||||
"last_checked": stamp,
|
||||
"next_check": nxt.strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"cadence": new_rung,
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ def main() -> int:
|
|||
for ref in (f.get("related") or []):
|
||||
if ref not in ids:
|
||||
malformed.append(f"{fid} — related names {ref}, which does not exist")
|
||||
if f.get("embargo_condition") and f.get("disclosure") != "embargoed":
|
||||
if f.get("embargo_condition") and f.get("disclosure") != "embargoed": # a lift is recorded as embargo_lifted
|
||||
malformed.append(f"{fid} — carries an embargo_condition but disclosure is '{f.get('disclosure')}'")
|
||||
if f.get("disclosure") == "embargoed" and not f.get("embargo_condition"):
|
||||
malformed.append(f"{fid} — embargoed with no condition; a hold with no lift is a silence")
|
||||
|
|
@ -159,11 +159,20 @@ def main() -> int:
|
|||
]
|
||||
section("Embargoed", embargo, "none")
|
||||
|
||||
esc = [
|
||||
f"{f['id']} — trigger {f.get('escalation_trigger')}, {f.get('escalation_status')}"
|
||||
for f in fs
|
||||
if f.get("escalation") == "required" and f.get("escalation_status") != "answered"
|
||||
]
|
||||
# T05 — an escalation nobody acknowledged is indistinguishable from one
|
||||
# never sent, which is the failure this register fixed for its own inbox
|
||||
# and not, until now, for the path that matters more.
|
||||
esc = []
|
||||
for f in fs:
|
||||
if f.get("escalation") != "required" or f.get("escalation_status") == "answered":
|
||||
continue
|
||||
sent = lib.moment(f.get("escalation_sent"))
|
||||
age = f", sent {(NOW - sent).days}d ago" if sent else ", never marked sent"
|
||||
state = f.get("escalation_status", "unknown")
|
||||
tail = " ← unacknowledged; raise once more, then record the default" if (
|
||||
sent and (NOW - sent).days >= 7 and state not in ("seen", "answered")
|
||||
) else ""
|
||||
esc.append(f"{f['id']} — trigger {f.get('escalation_trigger')}, {state}{age}{tail}")
|
||||
section("Escalations awaiting the operator", esc, "none")
|
||||
|
||||
rescore = [
|
||||
|
|
@ -182,6 +191,17 @@ def main() -> int:
|
|||
reg.append(f"{r.get('id')} — due {when:%Y-%m-%d}: {str(r.get('title','')).strip()}")
|
||||
section("Regulatory records due", reg, "none")
|
||||
|
||||
# T06 — a 1q rung means "stable for a quarter" and "nobody looked for a
|
||||
# quarter", and those read identically. The heartbeat separates them.
|
||||
checks = [m for f in lib.findings() if (m := lib.moment(f.get("last_checked")))]
|
||||
if checks:
|
||||
newest = max(checks)
|
||||
quiet = (NOW - newest).days
|
||||
if quiet >= 2:
|
||||
lines.insert(0, "")
|
||||
lines.insert(0, f" Nothing anywhere in the register has been checked for {quiet} days.")
|
||||
lines.insert(0, "HEARTBEAT — THE LADDER IS NOT BEING CLIMBED:")
|
||||
|
||||
print(f"Register check — {NOW:%Y-%m-%d %H:%MZ}\n{len(fs)} live finding(s)\n")
|
||||
print("\n".join(lines).rstrip())
|
||||
return 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue