Reversibility-Aware Graduated Response (R1–R3 ladder)¶
What it is¶
Risk and reversibility are orthogonal axes. A high-risk read is fully
recoverable; a low-risk delete is not. FerrumDeck adds a Reversibility
dimension on the tool registry and maps it onto a graduated response at the
gateway tool-policy check, modelled on the DeepMind AI Control Roadmap R1–R3
control ladder:
| Reversibility | Response level | Rung | Behaviour |
|---|---|---|---|
reversible |
allow_and_log |
R1 | Monitor: allowed, logged for async review. No gate. |
costly |
allow_under_budget |
R2 | Allowed while the run's cost budget has headroom; escalates to R3 once exhausted. |
irreversible |
require_approval |
R3 | The existing human-in-the-loop approval gate. |
Deny-by-default: an unregistered / unclassified tool defaults to
irreversible (R3) — the most consequential rung.
How it composes¶
The rung is folded into the allowlist decision, more-restrictive-wins
(fd_policy::reversibility::combine, ranked the same as the crate precedence
Deny > RequiresApproval > Allow). It can therefore only ever add friction:
it can upgrade an Allow to RequiresApproval, but can never loosen a Deny
or an existing approval requirement.
reversibility (tool registry) + budget headroom (run)
└──────────────► graduated_response() ─► response_level
│
allowlist decision ──────────────► combine(more-restrictive-wins) ─► effective decision
Where it's emitted¶
The decision + reasoning is surfaced on every existing channel — no parallel store:
- OTel/GenAI span:
ferrumdeck.policy.response_level+ferrumdeck.policy.reversibilityon the tool-check span. - Immutable audit log: the
policy.*audit eventdetailscarryreversibility,response_level,response_rung,effective_decision,budget_headroom. - API:
POST /v1/runs/{id}/check-toolreturnsreversibility+response_level; the polledGET /v1/runs/{id}returnsresponse_level. - Dashboard: the run console renders an R1/R2/R3 badge from the polled run
endpoint. A realtime
policy.response.recordedSSE shape is defined for parity; the gateway→BFF push is deferred (same pattern asrun.forecast.updated/routing.decision.recorded).
Setting a tool's reversibility¶
POST /v1/registry/tools accepts an optional reversibility field
(reversible | costly | irreversible); unknown / absent normalizes to
irreversible. Stored on tools.reversibility (TEXT, default irreversible).
Verifying¶
- Rust:
cargo test -p fd-policy reversibility(the ladder + budget-headroom + combine unit tests). - Evals (deterministic, CI-gated):
make test-pythonrunstests/test_reversibility_gate.py— a reversible action passes without a gate, an irreversible one blocks until approval, and a costly one flips to approval when the budget is breached.
Design reference¶
DeepMind, An Approach to Technical AGI Safety — the AI Control Roadmap R1–R3
control-level ladder (monitor → conditional → gate). Anchor:
deepmind-ai-control-roadmap-r1-r3.