Files
ruvnet--RuView/api-docs/adr/ADR-321-decision-policy-action-authorization.md
T
2026-08-11 17:41:30 +00:00

102 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-321: Decision policy — action authorization conditioned on certificate class, freshness, uncertainty, and evidence
- **Status**: Accepted — initial implementation planned (ADR-300 phase 1)
- **Date**: 2026-08-11
- **Deciders**: ruv
- **Tags**: policy, authorization, safety, certificates, governed-action, phase-1
## Context
The perception substrate (ADR-300) makes RuView state *what it knows* and
*how well* — the capability certificate (ADR-318) binds hardware, environment,
model, calibration, metrics, expiry, and evidence level. But a certificate is a
statement of knowledge, not a grant of action. The same certificate that is
adequate to dim a light is wholly inadequate to release a door lock or clear an
industrial stop condition.
Without an explicit authorization layer, every consumer re-implements its own
(inconsistent, usually optimistic) rule for "is this good enough to act on,"
and a confident-but-out-of-domain inference can reach an actuator. That is the
exact failure the substrate exists to prevent. Decision policy therefore
belongs in **phase 1**, alongside the certificate it gates, not later.
This ADR realizes program invariant #1 (UNKNOWN is a first-class output, never
an error) and the action-side of the refined acceptance test: a drift-
invalidated capability must be *denied at the actuator* before a false
confident inference is acted upon.
## Decision
Introduce a `ruview-policy` crate providing an **action authorization gate**
that sits between governed spatial state and any actuator.
### 1. Assurance requirements per action
An `ActionClass` declares the assurance an action demands:
- `min_certificate_class` — the required `CapabilityCertificate` class (ADR-318).
- `max_certificate_age` / `min_domain_freshness` — the certificate must be
currently valid **and** the live domain signature (ADR-302) must not be in a
DEGRADED/UNKNOWN state (this is the staleness guard, program invariant on
certificate conditionality — see ADR-300).
- `max_uncertainty` — inference uncertainty ceiling.
- `min_evidence_level` — the L0L5 floor (ADR-282/ADR-304); e.g. a safety
action may require ≥ L3 (held-out room+subject validation).
Reference action classes (illustrative, configurable):
| Class | Example | Typical floor |
|---|---|---|
| `Convenience` | lighting, scenes | tolerant: L1+, higher uncertainty ok |
| `Security` | alerts, arming | stricter: valid cert, L2+, bounded uncertainty |
| `SafetyCritical` | door lock, machine stop | strict: fresh cert, L3+, low uncertainty, KNOWN domain only |
### 2. The authorization decision
`authorize(action, capability_certificate, live_state) -> Authorization` where
`live_state` carries the current `SourceState` (ADR-295), OOD/domain state
(ADR-302), and inference uncertainty. Rules:
- **Fail-closed.** Any unmet condition → `Deny { failed_condition }`. The denial
names the *specific* condition (expired cert, domain DEGRADED, uncertainty
over ceiling, evidence below floor, certificate class too low).
- **UNKNOWN denies high-assurance actions.** A domain in UNKNOWN (ADR-302)
cannot authorize `Security`/`SafetyCritical` actions; it may still authorize
`Convenience` if that class's policy permits, but the authorization records
that it proceeded under UNKNOWN.
- The decision is a **pure function** of (action class, certificate, live
state) — deterministic and unit-testable without a clock or actuator.
- Every authorization (allow or deny) is emitted as the terminal stage of the
witness chain (ADR-319), so "why was this actuator allowed/denied" is
auditable end-to-end.
### 3. No silent optimism
A missing certificate, an expired certificate, or an unrecognized action class
all deny by default. Absence of a policy is not permission.
## Consequences
- Action authorization becomes uniform and centrally reasoned instead of
per-consumer and optimistic; this is the "RuView Certify → constrains action"
boundary that is hard to commoditize.
- A behavior change for existing automations that acted directly on presence:
they now pass through the gate. Convenience-class defaults keep low-stakes
automations working; high-stakes actions must opt into stricter classes.
- Depends on ADR-318 (certificate), ADR-302 (domain/OOD state), ADR-295
(source state), ADR-304 (evidence). Built in the phase-1 dependent wave after
those types land.
## Validation
- Unit tests: each action class authorizes/denies correctly across the matrix
of (valid/expired/degraded cert × KNOWN/DEGRADED/UNKNOWN domain × uncertainty
above/below ceiling × evidence above/below floor); UNKNOWN denies
safety-critical; every deny names its failed condition; absence-of-policy
denies; determinism.
- Integration: the acceptance-test scenario (ADR-300) — post-certification room
change drives domain to DEGRADED→UNKNOWN, and a `SafetyCritical` authorization
is denied with `failed_condition = domain_not_known` *before* the inference
reaches the actuator, witness chain preserved.
- `cargo test -p ruview-policy`.