Assistant: claude-code Assistant-Model: opus Assistant-Process: 2583210@bnt-lap001 Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
376 lines
18 KiB
Markdown
376 lines
18 KiB
Markdown
---
|
||
id: RISK-WP-0001
|
||
type: workplan
|
||
title: "Make the register decidable: severity, disclosure, escalation, expiry"
|
||
domain: infotech
|
||
repo: risk-nexus
|
||
status: finished
|
||
owner: the-custodian
|
||
topic_slug: risk-nexus
|
||
created: "2026-08-19"
|
||
updated: "2026-08-20"
|
||
waiting_on:
|
||
- who: the-custodian
|
||
what: "register finding / note / regulatory-record as canon work-record kinds, or rule that the register is a different ontology"
|
||
since: "2026-08-20"
|
||
would_change: "C-31 stops being a standing warning; the estate's work-record backbone gains four species it currently cannot name"
|
||
default: "C-31 documented as expected noise for this repo, and the register stops treating it as a gap it can close"
|
||
default_at: "2026-09-17"
|
||
state_hub_workstream_id: "dadd2aa5-6c2a-5f38-ab30-f442f875b6a1"
|
||
---
|
||
|
||
# RISK-WP-0001 — make the register decidable
|
||
|
||
## Goal
|
||
|
||
Give this repo the four instruments it needs to answer a finding, and then
|
||
answer the three findings already sitting here.
|
||
|
||
Done means: every open finding carries a severity from a written scale, a
|
||
disclosure state from a written set, an escalation ruling from a written rule,
|
||
and a review date that something checks; the register has an index a person can
|
||
read in one sitting; and `RISK-F-0001`, `RISK-F-0002` and `RISK-F-0003` have
|
||
none of those fields at `unset`.
|
||
|
||
## The forcing case
|
||
|
||
Three findings are open. All three were filed correctly by their reporting
|
||
repos, and all three carry the same block:
|
||
|
||
```yaml
|
||
# risk-nexus's to set, not the reporter's (INTENT, "What it does not own").
|
||
severity: unset
|
||
disclosure: unset
|
||
escalation: unset
|
||
```
|
||
|
||
`RISK-F-0002` states the ask outright — it names severity, filing shape and
|
||
escalation as three decisions it is waiting on, and it declines to make them
|
||
itself. That is the boundary working exactly as `INTENT.md` describes it. It is
|
||
also a queue: the reporters have done their half, this repo has not built the
|
||
half that answers.
|
||
|
||
This is the failure `INTENT.md` says the repo must avoid, arriving early and in
|
||
its mildest form. Findings recorded, nothing decided, converts "we did not
|
||
know" into "we knew and did nothing" while changing no behaviour. Three
|
||
findings is the cheapest possible moment to fix it.
|
||
|
||
## What this workplan produces
|
||
|
||
Four instruments, one index, and one graded register.
|
||
|
||
| Instrument | Answers | Output |
|
||
| --- | --- | --- |
|
||
| Severity scale | how bad, how likely, and what is not a risk at all | `docs/method/severity.md` |
|
||
| Disclosure states | publish now, hold, or restrict — and the record that a hold was deliberate | `docs/method/disclosure.md` |
|
||
| Escalation rule | what reaches the operator personally | `docs/method/escalation.md` |
|
||
| Review and expiry | when a finding must be looked at again, and what happens when it is not | `docs/method/review.md` |
|
||
| Register index | the whole register, readable at once | `REGISTER.md` |
|
||
|
||
Each instrument is a governing document of this repo. It is written to be
|
||
publishable through `policy-nexus` later; this workplan does not publish it.
|
||
|
||
## Authority
|
||
|
||
- **This repo** sets severity, disclosure and escalation. Reporters do not, and
|
||
the current findings correctly leave them unset.
|
||
- **Owning repos** keep their own account of the defect and its fix. A finding
|
||
here points at that account; it does not replace it or edit it.
|
||
- **the-custodian** owns the escalation duty. This workplan writes the rule
|
||
down; adopting it is the custodian's call, and T04 asks for it explicitly.
|
||
- **Canon** is `the-custodian`'s and NetKingdom's. A severity scale is a
|
||
judgement instrument, not a standard, and must not be written as one.
|
||
|
||
## Non-goals
|
||
|
||
- **No remediation.** Not one line of another repo's fix. Findings route.
|
||
- **No publication.** `policy-nexus` is the surface; this workplan produces
|
||
documents that could be published, and publishes nothing.
|
||
- **No blocking gate.** In build mode a finding is information. If that ever
|
||
changes it is a recorded decision, not a side effect of this workplan.
|
||
- **No tool before the judgement.** The scale and the rule are written first
|
||
and applied by hand to three findings. Automation is T06 and is deliberately
|
||
the smallest thing that can nag.
|
||
- **No back-filling the estate.** Only findings already filed here, plus the
|
||
named waiting items in T07. Sweeping every repo for undiscovered risk is not
|
||
this workplan.
|
||
|
||
## Tasks
|
||
|
||
### T01 — Severity scale
|
||
|
||
```task
|
||
id: RISK-WP-0001-T01
|
||
status: done
|
||
priority: high
|
||
state_hub_task_id: "aae498d9-90d8-5f0b-9490-7cde4317f890"
|
||
```
|
||
|
||
Write the scale: impact bands, likelihood bands, how they combine, and the
|
||
threshold below which something is a note rather than a register entry.
|
||
|
||
Constraints it has to satisfy, taken from the findings already here:
|
||
|
||
- It must be able to score *the absence of a control* and *a control that lies*
|
||
differently. `RISK-F-0002` argues the second is worse than the first and asks
|
||
the register to say which it scored. A scale that cannot express that is not
|
||
finished.
|
||
- It must score against **build mode** honestly. "No real tenant data yet" is a
|
||
genuine likelihood input, not an excuse, and the scale must make the
|
||
production re-score automatic rather than optional.
|
||
- It must have a floor. `INTENT.md`: if a finding would not change anyone's
|
||
decision, it is a note. Say where that line is.
|
||
|
||
**Output:** `docs/method/severity.md`.
|
||
|
||
Completed 2026-08-19. `docs/method/severity.md`: impact `I1`-`I4` x likelihood `L1`-`L4` on a grid, the fidelity modifier (+1 impact band where the failure produces a false record rather than none), the headline-versus-constraint split, the build-mode double grade, and the floor. Amended the same day by T07 — build mode lowers impact as well as likelihood, and non-adversarial findings read likelihood as event probability.
|
||
|
||
### T02 — Disclosure states
|
||
|
||
```task
|
||
id: RISK-WP-0001-T02
|
||
status: done
|
||
priority: high
|
||
state_hub_task_id: "a185be6b-8097-5770-bbbd-fdd0f4db2ee7"
|
||
```
|
||
|
||
Define the disclosure states — at minimum publish-now, embargoed-until-fixed,
|
||
restricted — with the entry condition for each and what is recorded when a
|
||
finding is held.
|
||
|
||
Then revisit the standing deferral. `INTENT.md` records controlled disclosure
|
||
as deferred to production, on the reasoning that build mode has no users to
|
||
expose, and then records that `RISK-F-0001` tested that reasoning with a live
|
||
authorization bypass in the service every other service trusts. The deferral may
|
||
still be right. It must be re-taken with that case in hand and the reasoning
|
||
written down, because "publish or hold with nothing in between" is the state
|
||
`RISK-F-0001` is currently sitting in.
|
||
|
||
Whichever way it goes is a recorded decision, not a habit.
|
||
|
||
**Output:** `docs/method/disclosure.md`, including the re-taken deferral.
|
||
|
||
Completed 2026-08-19. `docs/method/disclosure.md`. The deferral was re-taken and **narrowed**: build-mode default stays publish, the live-and-reachable exception exists now and costs one front-matter field, the *mechanism* stays deferred as originally reasoned. All seven findings sit at `embargoed` with observable lift conditions.
|
||
|
||
### T03 — The escalation rule
|
||
|
||
```task
|
||
id: RISK-WP-0001-T03
|
||
status: done
|
||
priority: high
|
||
state_hub_task_id: "565046e4-8dea-5fcc-bcf2-72c8b6a23741"
|
||
```
|
||
|
||
Write the rule `INTENT.md` says is not yet written. Settle the five candidate
|
||
triggers it lists rather than restating them:
|
||
|
||
1. anything exposing real tenant data;
|
||
2. anything creating a legal or regulatory obligation;
|
||
3. anything requiring spend;
|
||
4. anything where two repos disagree about who owns the fix;
|
||
5. any finding whose remediation has stalled past a stated interval.
|
||
|
||
For each: adopt, drop, or bound it with a number. A trigger with no threshold
|
||
is not a rule. Say also what escalation *is* — a message, a conversation, a
|
||
flagged item — and what the operator is expected to do with one.
|
||
|
||
Test the draft against the three open findings before calling it done.
|
||
`RISK-F-0002` states its own view (it does not think it needs the operator);
|
||
the rule must reach an answer there on its own terms, and if it disagrees with
|
||
the reporter, that disagreement is the useful output.
|
||
|
||
The custodian adopts the rule. Ask, do not assume.
|
||
|
||
**Output:** `docs/method/escalation.md`.
|
||
|
||
In progress 2026-08-19. `docs/method/escalation.md` is written and `status: proposed`. All five INTENT triggers settled — 1 adopted and bounded, 2 adopted unbounded, 3 bounded at EUR 50/month or EUR 500 one-off, 4 bounded by one failed routing exchange, 5 bounded at twice the review interval — plus trigger 6 (ordering hazard producing a false attestation), added because `RISK-F-0002` produced the case. Tested against all seven findings before proposing, and against nine by the time it was adopted. The €50/month and €500 thresholds are now the operator's numbers rather than ours — they bound nothing so far, since the only spend decision taken (`RISK-F-0006`) was approved without a figure.
|
||
|
||
### T04 — Review dates and expiry
|
||
|
||
```task
|
||
id: RISK-WP-0001-T04
|
||
status: done
|
||
priority: medium
|
||
state_hub_task_id: "ae3dd735-a2fe-5b79-a8fe-3ad6d5d4a76d"
|
||
```
|
||
|
||
Every finding gets a review date on entry, set from its severity. Define the
|
||
intervals, what a review is (re-score, re-check the blocker, re-read the fix
|
||
owner's account), and what happens when one is missed.
|
||
|
||
`INTENT.md` is explicit that a finding sitting untouched past its review date is
|
||
itself a finding, and `RISK-F-0002` supplies the mechanism: a blocker is a claim
|
||
about the world at a date, its own blocker was invalidated in a day, and nothing
|
||
would have re-checked it. Overdue review therefore has to produce something —
|
||
an escalation, a severity bump, a register entry of its own — and this task
|
||
picks which.
|
||
|
||
**Output:** `docs/method/review.md`; the finding front-matter contract extended
|
||
with `review_by` and `last_reviewed`.
|
||
|
||
Completed 2026-08-19. `docs/method/review.md`: 7/30/90/180-day intervals by severity, the four questions a review answers — including `RISK-F-0002`'s "a blocker is a claim about the world at a date" as question 2 — what a missed review produces, the production re-score as an event rather than a date, and the three ways a finding may close.
|
||
|
||
### T05 — Register index
|
||
|
||
```task
|
||
id: RISK-WP-0001-T05
|
||
status: done
|
||
priority: medium
|
||
state_hub_task_id: "0a65417f-561a-5ff9-b055-3ae19bc03747"
|
||
```
|
||
|
||
One `REGISTER.md`: id, title, system, severity, disclosure, escalation, fix
|
||
owner, status, review date — one row per finding, newest first.
|
||
|
||
Generate it from the finding front-matter rather than maintaining it by hand;
|
||
a hand-maintained index drifts and then lies. Keep the generator small and in
|
||
this repo (`tools/`), and keep the output legible: if the table stops being
|
||
readable in one sitting, that is the "register too large to read" failure
|
||
arriving, and it is worth a note in the residuals rather than a wider table.
|
||
|
||
**Output:** `REGISTER.md`, its generator, and the front-matter contract the
|
||
generator relies on written down where a reporter will see it.
|
||
|
||
Completed 2026-08-19. `REGISTER.md`, generated by `tools/register_index.py` from the finding front-matter, with separate Constraints, Embargoes and Notes sections so a lower headline cannot hide a higher constraint. The filing contract is in `findings/README.md`, where a reporter will meet it. `make check` fails if the index is stale.
|
||
|
||
### T06 — Grade the three open findings
|
||
|
||
```task
|
||
id: RISK-WP-0001-T06
|
||
status: done
|
||
priority: high
|
||
state_hub_task_id: "336c0355-77ae-5b65-bb84-db1b79499eea"
|
||
```
|
||
|
||
Apply T01–T04 to `RISK-F-0001`, `RISK-F-0002` and `RISK-F-0003`. For each:
|
||
severity with its reasoning, disclosure state, escalation ruling, review date,
|
||
and confirmation that the fix owner recorded in the file is still the right
|
||
one.
|
||
|
||
Two specific questions are already on the table and must be answered here, not
|
||
deferred:
|
||
|
||
- **`RISK-F-0002`: peer or consequence?** It is filed as a peer of
|
||
`RISK-F-0001` because the fix owner differs, and it offers to be carried as a
|
||
dependency instead. Rule on it; `ops-warden` has said it will not re-file
|
||
either way.
|
||
- **The sequencing constraint.** `RISK-F-0002` states that `policy.enabled`
|
||
must not flip anywhere while `flex-auth`'s `/v1/check` answers unauthenticated
|
||
callers. That is a cross-repo ordering constraint between two open findings,
|
||
and the register is where it stays visible. Record it as such.
|
||
|
||
Where grading changes what an owning repo should do, that goes back as a
|
||
message to that repo, not as an edit in it.
|
||
|
||
**Acceptance:** no `unset` field remains in `findings/`.
|
||
|
||
Completed 2026-08-19, in three rounds — the third after reading the inbox, which is recorded in `docs/rulings/2026-08-19-third-grading.md` as this repo's own defect: `RISK-F-0001` was graded `critical` and escalated while two messages that changed the grade sat unread. Corrected to `high` and closed `fixed` the same day; both escalations withdrawn.
|
||
|
||
`docs/rulings/2026-08-19-first-grading.md`. `RISK-F-0001` critical, embargoed, escalated on trigger 1 — the INTENT question about whether governing access counts as exposure is answered yes. `RISK-F-0002` medium today with a `high` constraint on `RISK-F-0001`'s remediation, filed as a **peer**, and escalated only on the ordering, against the reporter's own reading. `RISK-F-0003` high, embargoed, no escalation. No `unset` field remains.
|
||
|
||
### T07 — Rule on what is waiting outside the register
|
||
|
||
```task
|
||
id: RISK-WP-0001-T07
|
||
status: done
|
||
priority: medium
|
||
state_hub_task_id: "85a93d4d-9d46-5323-820d-eff1c6b1dd7c"
|
||
```
|
||
|
||
`INTENT.md` names things visible but unfiled, held back pending the precedent
|
||
this workplan sets. Now that the floor from T01 exists, rule on each:
|
||
|
||
- the three further defects surfaced in the same round as `RISK-F-0001` and
|
||
recorded inside it as visible-but-unfiled;
|
||
- the unverified tenant boundary — no consumer's tenant boundary is verified
|
||
anywhere, Broken Object Level Authorization, currently open question 3 in an
|
||
unratified draft;
|
||
- the absent noisy-neighbour characterisation;
|
||
- the erasure-versus-audit tension.
|
||
|
||
Each becomes a finding with a full grade, or a recorded note with the reason it
|
||
is below the floor. "Still waiting on the precedent" stops being available as an
|
||
answer once T01 lands.
|
||
|
||
The last three live in NetKingdom's *Tenancy Posture*, a draft, and filing them
|
||
here does not amend it. A finding may say a standard is wrong; it cannot change
|
||
one.
|
||
|
||
Completed 2026-08-19. `docs/rulings/2026-08-19-second-grading.md`. Four filed — `RISK-F-0004` (tenant-engine), `RISK-F-0005` (audit-core), `RISK-F-0006` (apps-pg, escalated on spend), `RISK-F-0007` (the unverified tenant boundary, escalated on ownership, `fix_owner` deliberately `unset`). Two ruled notes — `RISK-N-0001` noisy neighbours, `RISK-N-0002` erasure-versus-audit — each with an event to be re-read at rather than a date. Filing `RISK-F-0007` does not amend *Tenancy Posture*.
|
||
|
||
### T08 — Nag
|
||
|
||
```task
|
||
id: RISK-WP-0001-T08
|
||
status: done
|
||
priority: low
|
||
state_hub_task_id: "5141e1bf-f3a7-50a9-a4b6-9d1f71f88470"
|
||
```
|
||
|
||
The smallest mechanism that makes silence visible: a check over the register
|
||
that reports findings past `review_by`, findings whose fix has not moved in the
|
||
interval T03 set, and any finding still carrying an `unset` field.
|
||
|
||
Report only. It writes nothing, escalates nothing on its own, and blocks
|
||
nothing. A human or the custodian acts on the output.
|
||
|
||
Runs from a `Makefile` target in this repo, in the same shape as the sibling
|
||
`policy-nexus` `make check`.
|
||
|
||
Completed 2026-08-19. `tools/register_check.py` behind `make check`: ungraded fields, overdue reviews, stalls at twice the interval, embargoes overdue for re-decision, escalations awaiting the operator, and what is owed at the production transition. Report only — it writes nothing and changes no field.
|
||
|
||
## Sequencing
|
||
|
||
T01 first — the floor it sets is what T02, T04, T05 and T07 lean on.
|
||
T02 and T03 can run in parallel after it; T03 wants a custodian answer and
|
||
should be raised early so the wait is not on the critical path.
|
||
T04 after T01. T05 after T04, since the index needs the review fields.
|
||
T06 after T01–T04. T07 after T01 and T06's precedent. T08 last, after T05.
|
||
|
||
Do not hold T06 for T07. Grading what is already filed is the point; deciding
|
||
what else to file is the follow-on.
|
||
|
||
## Risks
|
||
|
||
**The instruments become a standard.** A severity scale that reads like canon
|
||
invites arguments about the scale instead of about the risk. Mitigation: each
|
||
document states it is this repo's judgement instrument, revisable by this repo,
|
||
and cites the finding that forced it.
|
||
|
||
**Grading becomes re-investigation.** The three findings are well-evidenced by
|
||
their reporters. Mitigation: T06 scores what is in the file and asks the owner
|
||
where the file is silent; it does not re-derive the defect.
|
||
|
||
**The escalation rule is written and never adopted.** Mitigation: T03 is not
|
||
done until the custodian has ruled on it. An unadopted rule is worse than an
|
||
unwritten one, because it looks like coverage.
|
||
|
||
**Automation arrives before judgement and freezes a bad scale.** Mitigation:
|
||
T08 is last, is report-only, and has no authority to change a field.
|
||
|
||
**The register grows past readability while nobody notices.** Mitigation: T05
|
||
treats index length as a signal, and T07's floor is the instrument that keeps
|
||
notes out.
|
||
|
||
## Residuals
|
||
|
||
- **Publication handover.** `RISK-F-0001` (fixed) and `RISK-F-0008` (a
|
||
question, published as a question) are `disclosure: public` with
|
||
`publication: pending-handover`. Handing them to `policy-nexus` under its
|
||
publication contract is the first item of the next workplan; nothing in this
|
||
one publishes.
|
||
- **Inbox before grading.** Added to `docs/method/review.md` as question zero
|
||
after this workplan graded `RISK-F-0001` `critical` while its fix notice sat
|
||
unread. The mechanism is a habit, not a check — if it slips again, the answer
|
||
is a check in `make check` against message timestamps, and that is a task,
|
||
not a residual.
|
||
|
||
- Publication of these instruments through `policy-nexus` is a later workplan,
|
||
and only if T02 concludes anything here should be public.
|
||
- Regulatory intake — the second half of this repo's remit — is untouched here.
|
||
It needs its own workplan and its own retention rule; `INTENT.md` records that
|
||
regulation was previously "consulted and discarded", which is a different
|
||
failure from the one this workplan addresses.
|
||
- Routing mechanics between this register and owning repos are done by hand at
|
||
three findings. If the register reaches a size where hand-routing slips, that
|
||
is the trigger for a routing workplan, not for widening T08.
|