diff --git a/Makefile b/Makefile index 4a00324..34473a0 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,11 @@ PY := python3 TOOLS := tools -.PHONY: help register check checked +.PHONY: help register check help: @echo "make register - rebuild REGISTER.md from findings/" @echo "make check - verify the index is current, then report what is going quiet" - @echo "make checked - record a check outcome: make checked ARGS=\"RISK-F-0002 clean\"" register: @$(PY) $(TOOLS)/register_index.py @@ -15,7 +14,3 @@ check: @$(PY) $(TOOLS)/register_index.py --check @echo @$(PY) $(TOOLS)/register_check.py - -checked: - @$(PY) $(TOOLS)/record_check.py $(ARGS) - @$(PY) $(TOOLS)/register_index.py diff --git a/tools/record_check.py b/tools/record_check.py deleted file mode 100644 index a6c92e7..0000000 --- a/tools/record_check.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env python3 -"""Record the outcome of a check and move the finding along the ladder. - - python3 tools/record_check.py RISK-F-0002 clean - python3 tools/record_check.py RISK-F-0002 moved "flex-auth shipped the fix" - python3 tools/record_check.py RISK-F-0002 defer 2026-09-01 "operator: after the migration" - -`clean` climbs one rung, `moved` resets to `instant`, `defer` parks it until a -date by explicit operator decision. Writes the front-matter and appends a dated -line to the finding's Reviews section — the check is not recorded until it is -written down, which is the same rule the register applies to everyone else. -""" -from __future__ import annotations - -import datetime as dt -import re -import sys - -import register_lib as lib - - -def fail(msg: str) -> None: - print(msg) - raise SystemExit(2) - - -def main(argv: list[str]) -> int: - if len(argv) < 2: - fail(__doc__) - fid, outcome, rest = argv[0], argv[1], argv[2:] - matches = [f for f in lib.findings() if f["id"] == fid] - if not matches: - fail(f"no finding with id {fid}") - f = matches[0] - path = f["_path"] - text = path.read_text(encoding="utf-8") - now = lib.now() - stamp = now.strftime("%Y-%m-%dT%H:%M:%SZ") - rung = f.get("cadence", "instant") - - if outcome == "clean": - new_rung = lib.climb(rung) - streak = int(f.get("clean_streak", 0)) + 1 - nxt = now + lib.interval(new_rung) - note = rest[0] if rest else "checked, nothing moved" - line = f"- **{now:%Y-%m-%d}** — clean check: {note}. Cadence {rung} → {new_rung} ({streak} clean in a row); next check {nxt:%Y-%m-%d %H:%MZ}." - defer = "" - elif outcome == "moved": - if not rest: - fail("`moved` needs a reason: what changed") - new_rung, streak, nxt = lib.reset(), 0, now - line = f"- **{now:%Y-%m-%d}** — not clean: {rest[0]} Cadence {rung} → instant; checked again immediately." - defer = "" - elif outcome == "defer": - if len(rest) < 2: - fail("`defer` needs a date and the operator's reason") - until, why = rest[0], rest[1] - new_rung, streak, nxt = rung, int(f.get("clean_streak", 0)), lib.moment(until) - line = f"- **{now:%Y-%m-%d}** — deferred to {until} by explicit operator decision: {why}" - defer = until - else: - fail("outcome must be one of: clean, moved, defer") - - subs = { - "last_checked": stamp, - "next_check": nxt.strftime("%Y-%m-%dT%H:%M:%SZ"), - "cadence": new_rung, - "clean_streak": str(streak), - } - if defer: - subs["deferred_to"] = defer - for key, value in subs.items(): - quoted = f'"{value}"' if key not in ("cadence", "clean_streak") else value - if re.search(rf"(?m)^{key}:", text): - text = re.sub(rf"(?m)^{key}:.*$", f"{key}: {quoted}", text) - else: - text = text.replace("\n---\n", f"\n{key}: {quoted}\n---\n", 1) - - if "\n## Reviews\n" in text: - text = text.rstrip("\n") + "\n" + line + "\n" - else: - text = text.rstrip("\n") + "\n\n## Reviews\n\n" + line + "\n" - path.write_text(text, encoding="utf-8") - print(f"{fid}: {outcome} — cadence {rung} → {new_rung}, next check {subs['next_check']}") - print("REGISTER.md is stale; run `make register`.") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main(sys.argv[1:])) diff --git a/tools/register_check.py b/tools/register_check.py index 202db66..707d068 100644 --- a/tools/register_check.py +++ b/tools/register_check.py @@ -26,16 +26,6 @@ def main() -> int: lines.append(f" {quiet}") lines.append("") - seen: dict[str, str] = {} - dupes = [] - for f in lib.findings(): - prior = seen.get(f["id"]) - if prior: - dupes.append(f"{f['id']} — filed twice: {prior} and {f['_path'].name}; renumber the later commit") - seen[f["id"]] = f["_path"].name - if dupes: - section("Duplicate ids", dupes, "none") - unknown = [ f"{f['id']} — status '{f.get('status')}' is not one of {', '.join(lib.KNOWN_STATUSES)}; watched anyway" for f in fs diff --git a/workplans/RISK-WP-0002-publication-handover.md b/workplans/RISK-WP-0002-publication-handover.md deleted file mode 100644 index 5176ad8..0000000 --- a/workplans/RISK-WP-0002-publication-handover.md +++ /dev/null @@ -1,110 +0,0 @@ ---- -id: RISK-WP-0002 -type: workplan -title: "Hand the publishable findings to policy-nexus, and decide what else is a public document" -domain: infotech -repo: risk-nexus -status: proposed -owner: the-custodian -topic_slug: risk-nexus -created: "2026-08-20" -updated: "2026-08-20" -depends_on_workplans: - - RISK-WP-0001 ---- - -# RISK-WP-0002 — publication handover - -**Draft.** Sized deliberately small: two documents are ready and the rest is a -decision, not a project. - -## Goal - -`RISK-F-0001` and `RISK-F-0008` carry `disclosure: public` and -`publication: pending-handover`. Get them onto `policy.coulomb.social` under -`policy-nexus`'s existing contract, and settle whether this repo's method -documents are public too. - -Done means: both findings have a permanent address, `publication: published`, -and a recorded answer on the method documents. - -## Why now - -Six findings are embargoed with lift conditions, and `RISK-F-0009` has already -demonstrated that a condition can be met and the embargo still hold. When those -conditions start clearing, publication will happen in a trickle rather than a -batch — so the route wants to exist before it is needed, not during. - -`policy-nexus` has been told this is coming (2026-08-20) and asked for nothing. - -## Tasks - -### T01 — Publish the two ready findings - -```task -id: RISK-WP-0002-T01 -status: todo -priority: high -``` - -Follow `policy-nexus`'s publication contract as it stands. Do not invent an -address scheme: `POLICY-NEXUS-WP-0001` settled addressing and permanence, and -this repo is a consumer of that decision. - -Open question for T01 rather than an assumption: **is a finding published whole, -or as a summary?** `RISK-F-0001` contains a full ruling, a re-grade, a review -log and this register's own process defect. Some of that is register-internal -work product. Decide once, here, and apply it to every later publication. - -### T02 — Rule on the method documents - -```task -id: RISK-WP-0002-T02 -status: todo -priority: medium -``` - -`docs/method/severity.md`, `disclosure.md`, `escalation.md`, `review.md`. - -The case for publishing: they say how the estate grades and holds risk, which -is exactly what an outside reader needs to judge whether a published finding -means anything. - -The case against: the escalation rule names the operator's own thresholds, and -the severity scale is a judgement instrument this repo revises freely. A -published instrument invites argument about the instrument. - -Suggested split, to be ruled on rather than assumed: severity and disclosure -public, escalation and review internal. Escalation in particular describes when -the operator is interrupted, which is not the estate's business to advertise. - -### T03 — The standing route - -```task -id: RISK-WP-0002-T03 -status: todo -priority: medium -``` - -Write down what happens when an embargo lifts: who hands over, in what shape, -and how `publication: published` gets recorded back on the finding. - -Small. It is a paragraph in `docs/method/disclosure.md` plus whatever -`policy-nexus` needs on their side, not a mechanism. - -## Non-goals - -- No publication surface here. `policy-nexus` hosts; this repo hands over. -- No timed release, no coordinated disclosure, no notification tiers. Those stay - deferred (`docs/method/disclosure.md`) until there are real users. -- No re-grading of anything to make it publishable. - -## Risks - -**A finding is published with an internal ruling attached.** Mitigation: T01 -decides whole-versus-summary before anything ships. - -**The handover becomes a project.** Mitigation: three tasks, one of which is a -paragraph. If it grows, that is a signal the publication contract does not fit -findings, and that is a conversation with `policy-nexus` rather than more tasks -here. diff --git a/workplans/RISK-WP-0003-regulatory-intake.md b/workplans/RISK-WP-0003-regulatory-intake.md deleted file mode 100644 index 5acad3e..0000000 --- a/workplans/RISK-WP-0003-regulatory-intake.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -id: RISK-WP-0003 -type: workplan -title: "Make regulatory intake a working remit rather than one record" -domain: infotech -repo: risk-nexus -status: proposed -owner: the-custodian -topic_slug: risk-nexus -created: "2026-08-20" -updated: "2026-08-20" -depends_on_workplans: - - RISK-WP-0001 ---- - -# RISK-WP-0003 — regulatory intake - -**Draft.** The half of this repo's remit that `RISK-WP-0001` deliberately did -not touch. - -## Goal - -`INTENT.md` says regulation was previously "consulted and discarded" — the same -question asked twice and the answer silently expiring. `RISK-REG-0001` is one -record against that. Make it a remit: a format that expires, a way for repos to -ask, and the open items that record is carrying. - -Done means: the retention question is answered as far as it can be without -buying advice, the trigger list for buying advice is ruled, and a repo with a -regulatory question knows where to put it. - -## The open items this inherits - -From `RISK-REG-0001` and `RISK-F-0008`, both already written down: - -1. **A defensible retention period per category.** The determination names this - as the weakest point in the estate's whole position: supervisory practice - accepts audit logging under legitimate interest and then asks how long, and - "we keep audit because it is audit" is the form that fails. -2. **`audit-core`'s co-residency horizon.** At `P1` the real erasure horizon is - the maximum across every co-resident on `platform-pg`, not the declared - value. An infrastructure fact is doing load-bearing work in a legal position, - which is an uncomfortable place for it to be. Blocked on `audit-core`. -3. **The trigger list.** First real person's data, first counterparty contract - requiring a stated position, first Art 17 request. Proposed 2026-08-19, not - ruled. - -## Tasks - -### T01 — Rule the trigger list - -```task -id: RISK-WP-0003-T01 -status: todo -priority: high -``` - -Custodian decision. Cheap, and it is what stops the estate either buying advice -it does not need or discovering it needed it. Until it is ruled, `RISK-F-0008` -stays escalated as `partially-answered`. - -### T02 — Retention periods per category - -```task -id: RISK-WP-0003-T02 -status: todo -priority: high -``` - -State a period and a reason per category in `RISK-REG-0001`, or state plainly -that the estate cannot yet and why. The second is an acceptable outcome and a -better record than a number nobody can defend. - -Depends on `audit-core` answering the co-residency horizon, which has been -asked for. If they cannot, that dependency is itself the answer to record. - -### T03 — Intake route for regulatory questions - -```task -id: RISK-WP-0003-T03 -status: todo -priority: medium -``` - -`audit-core` routed theirs by messaging this repo and asking for an owner, -which worked. Write that down as the route rather than leaving it as one repo's -good instinct: what a regulatory question needs when it arrives, what it gets -back, and what this repo will not answer (legal advice, and what the owning -repo must therefore do). - -Extend `findings/README.md` or give `docs/regulatory/README.md` the reporter's -half. Do not invent an intake system. - -### T04 — Expiry - -```task -id: RISK-WP-0003-T04 -status: todo -priority: medium -``` - -Regulatory records expire; that is why the remit moved here. Put them on the -same cadence ladder as findings (`docs/method/review.md`) rather than inventing -a second review mechanism — a record that has held still for a quarter is -making the same statement a finding at `1q` makes. - -`make check` should report a regulatory record due for a check exactly as it -reports a finding. - -## Non-goals - -- **No legal advice.** `INTENT.md`, and the records say so in their own text. -- **No survey of every regime that might apply.** Regulation is scoped to rules - bearing on data the estate holds, markets it sells into, or obligations it has - taken on. A general compliance programme is not this. -- **No answering what a repo must therefore do.** That is the owning repo's. - -## Risks - -**The remit becomes a compliance function.** Mitigation: records answer -questions that were actually asked, by a repo, with a date. - -**A record states a legal conclusion with false confidence.** Mitigation: every -record names where it is weak, and `external_review: none` is a required field -rather than an omission. diff --git a/workplans/RISK-WP-0004-run-the-register.md b/workplans/RISK-WP-0004-run-the-register.md deleted file mode 100644 index 698aa30..0000000 --- a/workplans/RISK-WP-0004-run-the-register.md +++ /dev/null @@ -1,169 +0,0 @@ ---- -id: RISK-WP-0004 -type: workplan -title: "Run the register: make the cadence actually happen, and close the gaps the first week found" -domain: infotech -repo: risk-nexus -status: proposed -owner: the-custodian -topic_slug: risk-nexus -created: "2026-08-20" -updated: "2026-08-20" -depends_on_workplans: - - RISK-WP-0001 ---- - -# RISK-WP-0004 — run the register - -**Draft.** `RISK-WP-0001` built the instruments. This one is about the register -being *operated*, which is a different thing and the one that fails quietly. - -## Goal - -Nine findings, two notes, a regulatory record and an adaptive cadence exist. -**Nothing currently performs a check.** `make check` says what is due; a person -or an agent has to read the finding, ask the five questions, and record the -outcome. If that does not happen, every finding sits at `instant` forever and -the ladder becomes decoration. - -Done means: checks happen on their own schedule without the operator -remembering, the register's own known gaps are closed, and the failure modes -found in the first week cannot recur silently. - -## What the first week actually found - -Every task below traces to something that happened, not something imagined. - -| What happened | Task | -| --- | --- | -| Graded `RISK-F-0001` `critical` while its fix notice sat unread in the inbox | T02 | -| `RISK-F-0003` returned as `mitigated`, a word the tooling did not know, and vanished from the nag | fixed ad hoc; T03 generalises it | -| `ops-warden` and this register both used `RISK-F-0004` | fixed ad hoc; duplicate check now in `make check` | -| Two gradings rest on file comparison because an operator token is expired | T05 | -| `RISK-F-0007` accepted with an on-request path nobody has exercised | T04 | - -## Tasks - -### T01 — Something performs the checks - -```task -id: RISK-WP-0004-T01 -status: todo -priority: high -``` - -`tools/record_check.py` moves a finding along the ladder and writes the dated -line. What is missing is the thing that *calls* it on schedule. - -Options, in ascending order of how much this repo should want them: a scheduled -agent session that runs `make check`, works the due list, and records outcomes; -a cron that only reports; a human habit. The first is the only one that -survives the operator being busy, which is the condition the whole ladder is -designed for. - -Whatever it is, the check must remain a **judgement** — re-read the grade, -re-check every stated blocker, read the owner's tracking record. A job that -stamps `clean` without doing that is worse than no job, because it manufactures -a stability signal that is false. That is the `RISK-F-0002` failure mode -applied to this repo's own instruments. - -### T02 — Inbox before grading, as a check rather than a habit - -```task -id: RISK-WP-0004-T02 -status: todo -priority: high -``` - -`RISK-WP-0001`'s residual says: if it slips again, make it a check. It has -slipped once, on the register's first day, and cost a wrong grade and a nearly -sent escalation. - -`make check` should compare each finding's `last_checked` against the newest -message this repo has received about that system, and report any finding whose -inbox has spoken more recently than its register has. That is mechanical and -does not require the tooling to understand the message. - -### T03 — Fail loud everywhere else too - -```task -id: RISK-WP-0004-T03 -status: todo -priority: medium -``` - -The `mitigated` defect was one instance of a class: the tooling quietly -tolerating something it did not expect. Sweep for the rest — unknown disclosure -states, unparseable dates, a `constraint_on` pointing at a finding that does -not exist, an `embargo_condition` on a finding that is not embargoed. - -Every one of them should be reported by name and keep the finding watched. - -### T04 — Exercise the on-request verification path once - -```task -id: RISK-WP-0004-T04 -status: todo -priority: medium -``` - -`RISK-F-0007` is accepted until production on the strength of a path nobody has -used. A route that has never been walked is a plan, not a route. - -Pick one named consumer boundary, request verification through the documented -path, and see what actually happens: who answers, what evidence comes back, -whether the finding's likelihood moves for that consumer, and what the record -looks like. The value is in the friction it exposes. - -### T05 — The verification credential - -```task -id: RISK-WP-0004-T05 -status: todo -priority: medium -``` - -`RISK-F-0009` and `RISK-F-0003`'s mitigation both rest on comparing files -because `ops-warden`'s operator token is expired and `bao policy read` cannot -be run. - -That is not a finding — nothing is wrong with the estate because a token -expired — but it means the register is grading deployed controls from their -source. Establish what this repo can legitimately verify itself, and what it -must always take from owners. **If the answer is "nothing", that is worth -knowing and writing down**, because it bounds every grade in the register. - -### T06 — Regulatory records on the same ladder - -```task -id: RISK-WP-0004-T06 -status: todo -priority: low -``` - -Duplicate of `RISK-WP-0003-T04`, kept here as a pointer rather than a second -copy. Whichever workplan reaches it first does it; the other closes with a -reference. - -## Non-goals - -- **No dashboard.** `REGISTER.md` and `make check` are the surface, and the - register is supposed to stay small enough to read. -- **No automated grading.** A machine may schedule, list and record. Severity, - disclosure and escalation stay judgements this repo makes. -- **No monitoring of other systems.** `RISK-N-0003` records that everything - here was found by reading rather than watching; this workplan does not - attempt to change that, and a register that grows probes becomes a second - engineering team. - -## Risks - -**A scheduled check becomes a rubber stamp.** The whole value of the ladder is -that a `1q` rung means something. Mitigation: T01 treats stamping without -judgement as a defect, and `clean_streak` makes a suspiciously smooth climb -visible. - -**The register spends its attention on itself.** Six tasks about the register's -own machinery, none of which fix a defect in the estate. Mitigation: T01, T02 -and T05 all exist because the register got something wrong in week one; if -week two produces no such items, this workplan should shrink rather than grow.