risk-nexus/docs/method/escalation.md
tegwick 4daff5503f RISK-WP-0001-T07: rule on what was waiting outside the register
Four in as findings — tenant-engine unfiltered event read (high),
audit-core read path bounded by a flag not by code (medium), apps-pg with
no backup at all (high, escalated on spend), and the unverified tenant
boundary itself (high now, critical at production, escalated on
ownership, fix_owner deliberately unset). Two out as notes — noisy
neighbours and erasure-versus-audit, both real, neither changing a
decision this month, both carrying an event to be re-read at.

The round amended the scale twice: build mode lowers impact as well as
likelihood, and non-adversarial findings get their own likelihood
reading. The escalation rule gained a ratio test that distinguishes a
first sweep from steady-state intake, and a batching rule.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 23:33:08 +02:00

181 lines
7.8 KiB
Markdown

---
id: RISK-METHOD-ESCALATION
type: method
title: "Escalation: what reaches the operator personally"
status: proposed
owner: the-custodian
drafted_by: risk-nexus
drafted: "2026-08-19"
workplan: RISK-WP-0001-T03
review_interval: 90d
---
# Escalation
`INTENT.md` gives `the-custodian` the duty of deciding what must reach the
operator personally rather than sitting in a register, and says the rule is not
yet written. This is the rule, drafted by `risk-nexus`.
**Status: `proposed`.** The custodian adopts it. It is not in force until the
front-matter says `adopted`, and an unadopted rule is worse than an unwritten
one because it looks like coverage.
Getting this wrong in either direction is a failure: escalating everything
makes the operator the queue, escalating nothing makes the register a place
where serious things go quiet.
## What escalation is
A message to the operator personally — inbox message to `the-custodian`, and
said out loud in whatever session is running — containing exactly four things:
1. the finding id and one sentence of what is true;
2. the decision being asked for;
3. what happens if no answer comes;
4. by when.
It is **not** a copy of the finding, not a status report, and not a request for
the operator to do the work. If the operator's answer would not change
anything, it was not an escalation.
The operator is expected to rule, or to defer explicitly with a date. Silence
is not resolution: an unanswered escalation past its date goes back into the
register as an open item on this repo, visible in `REGISTER.md`, and is raised
again once. It is never quietly dropped, and it is never repeated weekly until
someone answers to make it stop.
## The triggers
Each of the five candidates from `INTENT.md` is adopted, dropped, or bounded.
A trigger without a threshold is not a rule.
### 1. Real tenant data — adopted, bounded
Escalate when a finding exposes real data belonging to a real counterparty, or
**the decision that governs access to it**, while live and unfixed.
The bound matters: `INTENT.md` asks whether governing the access counts as
exposing the data. It does. An authorization oracle that can be forged is not
one step removed from the data; it is the step. Test data and empty tenants do
not trigger it — the word is *real*.
### 2. Legal or regulatory obligation — adopted, unbounded
Escalate when a finding creates or reveals an obligation with an outside
counterparty: a notification duty, a retention or erasure requirement, a
contractual commitment, an in-scope determination under a regime.
No threshold. The estate cannot decide unilaterally that an obligation is
small, and this repo is explicitly not the place where legal advice is given.
### 3. Spend — adopted, bounded
Escalate when remediation needs money that is not already committed:
**recurring above €50/month, or one-off above €500.**
Below those, the owning repo decides and records it. The numbers are a
starting proposal from this repo; the operator owns them and should overwrite
them on adoption if they are wrong.
### 4. Ownership disagreement — adopted, bounded by one exchange
Escalate when two repos disagree about who owns a fix **and one routing
exchange has already failed to settle it.**
Disagreement itself is normal and is the register's job to route. What needs
the operator is a *stuck* disagreement, because only the operator can assign
work across repos that will not take it. One exchange, then escalate — not
three, and not zero.
### 5. Stalled remediation — adopted, bounded by interval
Escalate when a finding's fix has not moved for **twice its review interval**
(`docs/method/review.md`): 14 days for `critical`, 60 for `high`, 180 for
`medium`. `low` findings do not escalate on staleness; they age out through
review.
"Not moved" means the fix owner's tracking record has not changed state and no
new evidence has arrived. A deliberate, stated deferral by the owner is
movement. Silence is not.
### 6. Ordering hazard across repos — added
Escalate once when remediating finding A in the wrong order relative to
finding B would create a **false attestation** — a control that starts
claiming a check happened when it did not.
This trigger is not in `INTENT.md`'s list. It is added because `RISK-F-0002`
produced the case: enabling the `ops-warden` signing gate against a forgeable
`flex-auth` would convert an absent control into a signed audit record
asserting an authorization that was never made. Ordering hazards are cheap to
avoid before the fact and expensive to detect afterwards, because the evidence
is what lies.
The escalation is a single acknowledgement of the ordering, not a standing
supervision request. Once the operator has the constraint, the register carries
it.
## What does not escalate
Stated so the rule bites in both directions.
- **Known, owned, and moving.** However bad. A `critical` finding with an
active fix and a responsive owner is register work.
- **Severity alone.** There is deliberately no "escalate everything
`critical`" trigger. Severity drives review cadence, not the operator's
attention. `RISK-F-0002` argued its own case for not escalating and was
right on triggers 1-5; it reaches the operator only through trigger 6, and
only once.
- **The reporter asking for it.** A repo can flag that it thinks something
needs the operator. That is input, not a decision. This repo rules.
- **Anything this repo can decide itself.** Severity, disclosure, filing
shape, review dates. Asking the operator to confirm a judgement that is this
repo's to make is how the operator becomes the queue.
## Testing the rule against the register
The draft was tested against the first three findings before it was proposed
(`docs/rulings/2026-08-19-first-grading.md`) and then against the four filed
out of the backlog the same day
(`docs/rulings/2026-08-19-second-grading.md`).
| Finding | Escalates | Trigger |
| --- | --- | --- |
| `RISK-F-0001` flex-auth oracle | yes | 1 — governs access to tenant data |
| `RISK-F-0002` signing gate off | yes, once | 6 — ordering hazard, jointly with `RISK-F-0001` |
| `RISK-F-0003` ungraded lanes | no | known, owned, tracked, moving |
| `RISK-F-0004` tenant-engine reads | no | no real tenant data yet |
| `RISK-F-0005` audit-core reads | no | known, owned, tracked, moving |
| `RISK-F-0006` apps-pg no backup | yes | 3 — recurring spend nobody can self-authorise |
| `RISK-F-0007` unverified boundary | yes | 4 — estate-wide and unowned |
`RISK-F-0002` is the case worth reading: `ops-warden` judged it did not need
the operator, the register agrees on triggers 1-5, and it escalates anyway on
6. That disagreement is the reason the rule was worth writing down.
## The ratio, and what it would mean
Four of seven escalate. That is more than this rule should produce in steady
state, and it is not evidence the rule is loose — it is what a **first sweep**
looks like. Three of the four are the backlog: items that sat outside the
register precisely because nobody had decided them, and undecided items are
exactly the population that needs the person who can decide.
The test therefore applies to intake, not to sweeps:
> If more than one in three **newly reported** findings escalates over any
> rolling ninety days, the rule is wrong, not the register.
If that trips, the first suspects are triggers 3 and 4 — spend thresholds set
too low, and "unowned" being read where "not yet routed" is meant.
## Escalations are batched
Four escalations are one conversation, not four interruptions. Open items go
to the operator together, in register order, each with its own decision and
its own default-if-no-answer. Escalating is not the same as escalating
separately, and a rule that produces four messages in an afternoon has made the
operator the queue by a different route.
`make check` lists everything awaiting an answer, so the batch is a report and
not somebody's recollection.