Self-documenting Makefile and read-only query CLI
`make` now lists targets grouped by use with examples. New read-only tools/risk.py backs list/all/show/waits/escalations/policies; checked accepts ID/OUTCOME/NOTE (ARGS still works). Adds CLI tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 6903@bnt-lap001 Assistant-Session: 8319e8a8-ffa6-4eb3-b8bf-b29945628f89
This commit is contained in:
parent
228116926b
commit
2d60312bd1
4 changed files with 220 additions and 21 deletions
105
tools/risk.py
Normal file
105
tools/risk.py
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Read-only query CLI over the register: list, show, waits, escalations, policies.
|
||||
|
||||
python3 tools/risk.py list [--all] [--severity high] [--system qonto-assistant]
|
||||
python3 tools/risk.py show RISK-F-0012
|
||||
python3 tools/risk.py waits
|
||||
python3 tools/risk.py escalations
|
||||
python3 tools/risk.py policies
|
||||
|
||||
The verbs are generic on purpose (list/show over records), so that a future
|
||||
common nexus interface can map onto them. Writing stays with record_check.py.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
import register_lib as lib
|
||||
|
||||
SHOW_FIELDS = ("title", "status", "severity", "impact", "likelihood", "disclosure",
|
||||
"system", "fix_owner", "fix_tracking", "closure_condition", "escalation",
|
||||
"escalation_status", "cadence", "next_check", "ruling")
|
||||
|
||||
|
||||
def cmd_list(a) -> int:
|
||||
fs = lib.findings()
|
||||
if not a.all:
|
||||
fs = [f for f in fs if lib.watched(f.get("status"))]
|
||||
if a.severity:
|
||||
fs = [f for f in fs if f.get("severity") == a.severity]
|
||||
if a.system:
|
||||
fs = [f for f in fs if f.get("system") == a.system]
|
||||
fs.sort(key=lambda f: (lib.sev_rank(f.get("severity")), f["id"]))
|
||||
if not fs:
|
||||
print("no matching findings")
|
||||
for f in fs:
|
||||
print(f"{f['id']} {str(f.get('severity')):8} {str(f.get('status')):9} "
|
||||
f"{str(f.get('fix_owner')):20} {f.get('title')}")
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_show(a) -> int:
|
||||
rec = next((r for r in lib.findings() + lib.regulatory() if r.get("id") == a.id), None)
|
||||
if rec is None:
|
||||
print(f"{a.id}: not found")
|
||||
return 1
|
||||
print(f"{a.id} ({rec['_path'].relative_to(lib.REPO)})")
|
||||
for key in SHOW_FIELDS:
|
||||
if rec.get(key) not in (None, ""):
|
||||
print(f" {key:18} {rec[key]}")
|
||||
for w in rec.get("waiting_on") or []:
|
||||
print(f" waiting on {w.get('who')} until {w.get('default_at')}: {w.get('what')}")
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_waits(_a) -> int:
|
||||
rows = []
|
||||
for r in lib.records_with_waits():
|
||||
for w in r["waiting_on"]:
|
||||
rows.append((str(w.get("default_at")), r.get("id"), w.get("who"), w.get("what")))
|
||||
for when, rid, who, what in sorted(rows):
|
||||
print(f"{when} {rid:14} {who:20} {what}")
|
||||
if not rows:
|
||||
print("nobody is being waited on")
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_escalations(_a) -> int:
|
||||
rows = [f for f in lib.findings() if f.get("escalation") not in (None, "none")]
|
||||
for f in rows:
|
||||
print(f"{f['id']} trigger {f.get('escalation_trigger')} {f.get('escalation_status')}"
|
||||
f" sent {f.get('escalation_sent', '-')} {f.get('title')}")
|
||||
if not rows:
|
||||
print("no escalations recorded")
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_policies(_a) -> int:
|
||||
for r in lib.regulatory():
|
||||
print(f"{r.get('id'):14} {str(r.get('status')):22} next {str(r.get('next_check'))[:16]:16} "
|
||||
f"{r.get('title')}")
|
||||
return 0
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
p = argparse.ArgumentParser(prog="risk", description=__doc__.splitlines()[0])
|
||||
sub = p.add_subparsers(dest="cmd", required=True)
|
||||
ls = sub.add_parser("list", help="findings, most severe first (live ones unless --all)")
|
||||
ls.add_argument("--all", action="store_true", help="include fixed and withdrawn")
|
||||
ls.add_argument("--severity", choices=lib.SEVERITIES)
|
||||
ls.add_argument("--system")
|
||||
ls.set_defaults(fn=cmd_list)
|
||||
sh = sub.add_parser("show", help="one finding or regulatory record")
|
||||
sh.add_argument("id")
|
||||
sh.set_defaults(fn=cmd_show)
|
||||
for name, fn, hlp in (("waits", cmd_waits, "everything owed to or by the register, by deadline"),
|
||||
("escalations", cmd_escalations, "escalations and their delivery state"),
|
||||
("policies", cmd_policies, "regulatory records and full policies")):
|
||||
sub.add_parser(name, help=hlp).set_defaults(fn=fn)
|
||||
a = p.parse_args(argv)
|
||||
return a.fn(a)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
Loading…
Add table
Add a link
Reference in a new issue