mirror of
https://github.com/ruvnet/RuView
synced 2026-08-11 20:41:44 +00:00
102 lines
5.0 KiB
Markdown
102 lines
5.0 KiB
Markdown
# 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 L0–L5 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`.
|