From b5633f98659533d9db17859e40c236615a6d1b23 Mon Sep 17 00:00:00 2001 From: ruvnet Date: Tue, 11 Aug 2026 17:41:30 +0000 Subject: [PATCH] deploy: bf17fc0407e28eb136a6da3b02f0a457a724efdb --- ...291-public-benchmark-evaluation-harness.md | 106 ++++++++++ .../ADR-292-wideband-80211ax-csi-ingest.md | 91 +++++++++ .../adr/ADR-293-vitals-ground-truth-rig.md | 93 +++++++++ api-docs/adr/ADR-294-wifi-veil-integration.md | 83 ++++++++ ...ADR-295-source-provenance-state-machine.md | 64 ++++++ ...DR-296-sensor-data-plane-bind-hardening.md | 59 ++++++ ...ADR-297-multi-node-semantic-correctness.md | 58 ++++++ .../adr/ADR-298-model-release-sanity-gates.md | 60 ++++++ ...ADR-299-csi-data-incident-repo-controls.md | 50 +++++ .../ADR-300-perception-substrate-program.md | 189 ++++++++++++++++++ .../ADR-301-automatic-domain-calibration.md | 149 ++++++++++++++ .../ADR-302-out-of-distribution-detection.md | 135 +++++++++++++ .../ADR-303-ground-truth-synchronization.md | 125 ++++++++++++ api-docs/adr/ADR-304-evidence-engine.md | 117 +++++++++++ .../ADR-305-authenticated-sensor-identity.md | 147 ++++++++++++++ .../adr/ADR-306-canonical-spatial-ontology.md | 142 +++++++++++++ .../ADR-307-persistent-identity-tracking.md | 135 +++++++++++++ .../adr/ADR-308-sensor-placement-optimizer.md | 138 +++++++++++++ api-docs/adr/ADR-309-active-sensing.md | 152 ++++++++++++++ .../ADR-310-80211bf-native-architecture.md | 147 ++++++++++++++ api-docs/adr/ADR-311-real-sensor-fusion.md | 140 +++++++++++++ .../adr/ADR-312-long-term-spatial-memory.md | 146 ++++++++++++++ .../adr/ADR-313-counterfactual-inference.md | 142 +++++++++++++ .../adr/ADR-314-information-gain-scheduler.md | 138 +++++++++++++ api-docs/adr/ADR-315-digital-rf-twin.md | 159 +++++++++++++++ api-docs/adr/ADR-316-fleet-control-plane.md | 156 +++++++++++++++ ...DR-317-benchmark-multi-domain-scorecard.md | 140 +++++++++++++ .../adr/ADR-318-capability-certificates.md | 138 +++++++++++++ api-docs/adr/ADR-319-witness-chain.md | 146 ++++++++++++++ api-docs/adr/ADR-320-sensor-hal.md | 150 ++++++++++++++ ...21-decision-policy-action-authorization.md | 101 ++++++++++ api-docs/adr/README.md | 31 +++ api-docs/user-guide.md | 73 +++++++ 33 files changed, 3900 insertions(+) create mode 100644 api-docs/adr/ADR-291-public-benchmark-evaluation-harness.md create mode 100644 api-docs/adr/ADR-292-wideband-80211ax-csi-ingest.md create mode 100644 api-docs/adr/ADR-293-vitals-ground-truth-rig.md create mode 100644 api-docs/adr/ADR-294-wifi-veil-integration.md create mode 100644 api-docs/adr/ADR-295-source-provenance-state-machine.md create mode 100644 api-docs/adr/ADR-296-sensor-data-plane-bind-hardening.md create mode 100644 api-docs/adr/ADR-297-multi-node-semantic-correctness.md create mode 100644 api-docs/adr/ADR-298-model-release-sanity-gates.md create mode 100644 api-docs/adr/ADR-299-csi-data-incident-repo-controls.md create mode 100644 api-docs/adr/ADR-300-perception-substrate-program.md create mode 100644 api-docs/adr/ADR-301-automatic-domain-calibration.md create mode 100644 api-docs/adr/ADR-302-out-of-distribution-detection.md create mode 100644 api-docs/adr/ADR-303-ground-truth-synchronization.md create mode 100644 api-docs/adr/ADR-304-evidence-engine.md create mode 100644 api-docs/adr/ADR-305-authenticated-sensor-identity.md create mode 100644 api-docs/adr/ADR-306-canonical-spatial-ontology.md create mode 100644 api-docs/adr/ADR-307-persistent-identity-tracking.md create mode 100644 api-docs/adr/ADR-308-sensor-placement-optimizer.md create mode 100644 api-docs/adr/ADR-309-active-sensing.md create mode 100644 api-docs/adr/ADR-310-80211bf-native-architecture.md create mode 100644 api-docs/adr/ADR-311-real-sensor-fusion.md create mode 100644 api-docs/adr/ADR-312-long-term-spatial-memory.md create mode 100644 api-docs/adr/ADR-313-counterfactual-inference.md create mode 100644 api-docs/adr/ADR-314-information-gain-scheduler.md create mode 100644 api-docs/adr/ADR-315-digital-rf-twin.md create mode 100644 api-docs/adr/ADR-316-fleet-control-plane.md create mode 100644 api-docs/adr/ADR-317-benchmark-multi-domain-scorecard.md create mode 100644 api-docs/adr/ADR-318-capability-certificates.md create mode 100644 api-docs/adr/ADR-319-witness-chain.md create mode 100644 api-docs/adr/ADR-320-sensor-hal.md create mode 100644 api-docs/adr/ADR-321-decision-policy-action-authorization.md diff --git a/api-docs/adr/ADR-291-public-benchmark-evaluation-harness.md b/api-docs/adr/ADR-291-public-benchmark-evaluation-harness.md new file mode 100644 index 00000000..da3312a7 --- /dev/null +++ b/api-docs/adr/ADR-291-public-benchmark-evaluation-harness.md @@ -0,0 +1,106 @@ +# ADR-291: Public-benchmark evaluation harness — Widar3.0 ingest, standard split protocols, leakage guards + +- **Status**: Accepted — initial implementation (this PR) +- **Date**: 2026-08-10 +- **Deciders**: ruv +- **Tags**: training, evaluation, benchmarks, widar, mm-fi, leakage, honesty + +## Context + +RuView implements the field's key techniques (CSI ratio, BVP features, MAE +pretraining, rapid adaptation) but reports results only on self-collected data +with self-defined metrics (e.g. the README's held-out temporal-triplet +accuracy). A 2026 deep-research sweep of the WiFi-sensing literature found: + +1. Cross-domain generalization is the field's central unsolved problem; the + only widely reproduced cross-domain result is Widar3.0's BVP benchmark. +2. MM-Fi (NeurIPS 2023) is the standard WiFi-pose benchmark, with defined + cross-subject and cross-environment protocols. +3. The field had a documented leakage reckoning in 2024–2025: window-level + random splits on continuous recordings inflate accuracy (one dataset's F1 + collapsed from ~90% to ~22% under subject-disjoint splits — Sensors + 24(10):3159; Signals 6(4):59). + +`wifi-densepose-train` already has an `MmFiDataset` NPY loader and a +deterministic `SyntheticCsiDataset`, but no Widar3.0 ingest, no standard split +protocols, and no structural leakage guard. CLAUDE.md already requires +mean-pose baselines and leakage-free held-out splits for pose PCK; nothing in +the code enforces this. + +Without leaderboard-comparable numbers, RuView's claims cannot be ranked +against published systems, which blocks both scientific credibility and +commercial (OEM licensing) conversations. + +## Options considered + +1. **Do nothing; keep self-collected metrics.** Rejected: perpetuates the + comparability gap. +2. **Port a Python eval stack (SenseFi) alongside the Rust pipeline.** + Rejected: violates the v2 Rust-workspace direction and adds an unreviewed + dependency surface. +3. **Extend `wifi-densepose-train` with native loaders + protocol machinery.** + Chosen. + +## Decision + +Extend `v2/crates/wifi-densepose-train` with three additions: + +### 1. Widar3.0 ingest (`dataset::widar`) + +- A parser for the Intel 5300 `.dat` CSI log format ("bfee" records) used by + the Widar3.0 raw distribution: framed records with a 3-byte header + (2-byte little-endian length + 1-byte code 0xBB), a 20-byte bfee header + (timestamp_low, bfee_count, Nrx, Ntx, RSSI a/b/c, noise, agc, antenna_sel, + len, rate), and a packed 10-bit-per-component complex CSI payload of + 30 subcarrier groups. Invalid records are skipped with a warning, not a + panic — untrusted file input is validated at the boundary per CLAUDE.md. +- A `WidarDataset` implementing the existing `CsiDataset` trait, mapping + Widar's `Nrx × Ntx × 30` CSI into windowed `CsiSample`s via the existing + subcarrier interpolation, with domain metadata (user, room, orientation, + gesture) parsed from Widar's documented directory/file naming convention. +- No network access: the loader reads a local dataset root. Dataset download + remains a documented manual step. + +### 2. Split protocols (`protocols`) + +- A `SplitProtocol` type expressing the standard evaluations: cross-subject + (MM-Fi style), cross-environment/room, cross-orientation (Widar style), and + random-baseline (explicitly labelled as leakage-prone, for comparison only). +- Split assignment is a pure function of sample metadata + a seed — fully + deterministic, no RNG state. + +### 3. Leakage guards (`protocols::leakage`) + +- A structural `LeakageAudit` that, given a proposed train/test split, + verifies: (a) subject-disjointness, (b) environment-disjointness where the + protocol claims it, (c) no two windows from the same continuous recording + span both sides of the split. A failed audit is an `Err`, not a warning. +- PCK/accuracy reporting requires a `MeanPoseBaseline` computed from the + training split only, and reports model-vs-baseline together, enforcing the + CLAUDE.md rule in the type system rather than by convention. +- Evaluation output is an evidence-tagged report (`MEASURED` requires a + reproducer command line embedded in the report; anything else is emitted as + `SYNTHETIC` or `CLAIMED`). + +## Consequences + +- RuView results become comparable to published numbers (Widar3.0 cross-domain + gesture; MM-Fi cross-subject pose) for the first time. +- The leakage audit will make some existing internal numbers look worse. That + is the point. +- Parsing a legacy binary format adds maintenance surface; mitigated by + fixture-based tests with synthetic, deterministically generated `.dat` + bytes (no dataset redistribution). +- Widar's raw distribution is Intel 5300-specific; ESP32-captured data + continues through existing loaders. The protocols/leakage machinery is + loader-agnostic. + +## Validation + +- `cargo test -p wifi-densepose-train` — unit tests for the bfee parser + (truncated, corrupt, and valid synthetic fixtures), split determinism, + leakage-audit rejection cases, and mean-pose baseline math. +- `cargo bench -p wifi-densepose-train` — criterion benchmark for parser + throughput and split assignment on synthetic corpora. +- No accuracy numbers are claimed by this ADR; it delivers the machinery to + produce MEASURED ones. diff --git a/api-docs/adr/ADR-292-wideband-80211ax-csi-ingest.md b/api-docs/adr/ADR-292-wideband-80211ax-csi-ingest.md new file mode 100644 index 00000000..3e5fd60b --- /dev/null +++ b/api-docs/adr/ADR-292-wideband-80211ax-csi-ingest.md @@ -0,0 +1,91 @@ +# ADR-292: Wideband 802.11ax CSI ingest — FeitCSI/AX210 adapter and subcarrier-agnostic plumbing + +- **Status**: Accepted — initial implementation (this PR) +- **Date**: 2026-08-10 +- **Deciders**: ruv +- **Tags**: hardware, csi, 80211ax, ax210, feitcsi, ingest, mat + +## Context + +RuView's CSI ingest (`wifi-densepose-mat/src/integration/hardware_adapter.rs`) +supports ESP32 serial streams, the legacy Intel 5300 tool, and Atheros/Nexmon +paths. All of these are 802.11n-class: ≤40 MHz bandwidth, ≤114 subcarriers, +2.4/5 GHz. + +The 2026 research sweep found the field's center of gravity has moved to +Intel AX200/AX210 NICs via PicoScenes (closed-source core) and FeitCSI +(open-source, GPL): 802.11ax CSI at up to 160 MHz / 1992 subcarriers, +including the 6 GHz band. This is both the research-grade tier today and the +shape of the data 802.11bf silicon will deliver from ~2026 onward. RuView's +`wifi-densepose-hardware` crate already models 802.11bf session types, but no +ingest path can carry wideband CSI into the pipeline. + +Without a wideband path, RuView cannot develop against the best available +signal, cannot compare ESP32-grade results to wideband upper bounds, and will +meet 802.11bf silicon with no tested plumbing for >114-subcarrier frames. + +## Options considered + +1. **PicoScenes `.csi` ingest.** Rejected for now: the format is produced by a + closed-source core and is versioned/complex; parsing it without a + maintained spec invites silent corruption. +2. **Raw pcap + radiotap parsing.** Rejected: duplicates what FeitCSI already + does on-device, and pulls a packet-capture dependency into the pipeline. +3. **FeitCSI file/stream ingest.** Chosen: FeitCSI is open-source (its header + layout is auditable against the source), targets AX200/AX210, covers + 20–160 MHz including 6 GHz, and emits a compact binary record per frame. + +## Decision + +Extend `v2/crates/wifi-densepose-mat/src/integration` with: + +### 1. `feitcsi` record parser + +- A validated parser for FeitCSI's binary CSI record layout (header with + CSI buffer length, rate/bandwidth/channel metadata, antenna counts, RSSI, + timestamp, followed by interleaved complex CSI). The parser is written + against the documented layout, is version-checked, and rejects + records whose declared dimensions disagree with the buffer length — + untrusted file/stream input is validated at the boundary. +- Bounded allocation: a hard cap on subcarrier count (4096) and antenna + count (8) so a corrupt length field cannot cause unbounded allocation. + +### 2. `DeviceType::FeitCsi` in the hardware adapter + +- File-replay mode (read a recorded FeitCSI capture deterministically) and a + streaming mode fed by an external process writing to a path/pipe. No + privileged operations inside the crate: RuView does not configure the NIC; + FeitCSI's own tooling owns that, per least-authority. + +### 3. Subcarrier-agnostic plumbing + +- Ingest carries native subcarrier dimensionality end-to-end and converts to + pipeline width explicitly via the existing interpolation/decimation stage, + recording the native → pipeline mapping in frame metadata so downstream + consumers know the true spectral resolution. Bandwidth (20–160 MHz) and + band (2.4/5/6 GHz) become first-class frame metadata. + +## Consequences + +- RuView gains a research-grade wideband development path and a tested + ingest shape for future 802.11bf reporting (truncated CIR is a natural + extension of the same plumbing). +- GPL FeitCSI is used as an external tool, never linked: only its output + format is parsed. No licensing contamination of the MIT workspace. +- The parser tracks an external project's format; version checks fail loudly + on mismatch rather than misparse. +- ESP32 remains the deployed sensor tier; wideband is a development/ + validation tier. Accuracy claims from wideband captures must be tagged with + the capture hardware. + +## Validation + +- `cargo test -p wifi-densepose-mat` — parser tests over synthetic fixtures: + valid records at 20/80/160 MHz shapes, truncated buffer, dimension + mismatch, version mismatch, allocation-cap enforcement; adapter replay + determinism. +- `cargo bench -p wifi-densepose-mat` — criterion benchmark for record parse + throughput at 1992-subcarrier frames. +- Hardware validation on real AX210 silicon is explicitly out of scope for + this PR and remains required (per CLAUDE.md) before any capture-path + hardware claim; the file-replay path is testable without silicon. diff --git a/api-docs/adr/ADR-293-vitals-ground-truth-rig.md b/api-docs/adr/ADR-293-vitals-ground-truth-rig.md new file mode 100644 index 00000000..c513d8fe --- /dev/null +++ b/api-docs/adr/ADR-293-vitals-ground-truth-rig.md @@ -0,0 +1,93 @@ +# ADR-293: Vitals ground-truth rig — reference ingest, time alignment, and agreement metrics + +- **Status**: Accepted — initial implementation (this PR) +- **Date**: 2026-08-10 +- **Deciders**: ruv +- **Tags**: vitals, validation, ground-truth, bland-altman, evidence, honesty + +## Context + +`wifi-densepose-vitals` (ADR-021) extracts breathing (0.1–0.5 Hz) and heart +rate (0.8–2.0 Hz) from CSI. The 2026 research sweep found that every credible +vitals result in the literature ships with reference-sensor ground truth +(chest strap, pulse oximeter, ECG, or PSG), and that WiFi heart-rate numbers +without stated scope (single person, static, line-of-sight, short range) are +systematically misleading. RuView currently has no way to produce a MEASURED +vitals number: there is no reference-signal ingest, no time alignment between +CSI-derived estimates and a reference device, and no agreement statistics. + +CLAUDE.md requires accuracy statements to be tagged MEASURED (with a +reproducer), CLAIMED, or SYNTHETIC. For vitals, MEASURED is currently +unreachable. + +## Options considered + +1. **Live BLE/ANT+ integration with reference devices.** Rejected for now: + drivers and pairing are a hardware/product concern; the blocking gap is + the evaluation math, not the radio link. +2. **File-based reference ingest + offline agreement analysis.** Chosen: + every consumer reference device (Polar, Garmin, oximeters) exports + timestamped series; a file boundary keeps the crate dependency-free and + the pipeline deterministic. + +## Decision + +Add a `groundtruth` module to `v2/crates/wifi-densepose-vitals`: + +### 1. Reference series ingest + +- `ReferenceSeries`: timestamped samples (unix millis + value) for one + measurand (`HeartRateBpm` or `BreathingRateBrpm`), with device metadata + (make/model, measurement principle). Parsed from CSV (`timestamp_ms,value` + with a header line); malformed rows are rejected with row-numbered errors — + untrusted file input validated at the boundary. Non-monotonic timestamps + are an error, not silently sorted. + +### 2. Time alignment + +- Constant-offset estimation by maximizing normalized cross-correlation of + the estimate series against the reference over a bounded lag window + (default ±30 s), on a common resampled grid (nearest-sample, no + interpolation of physiological values across gaps larger than a + configurable limit). +- Optional linear clock-drift fit (offset + rate) for long sessions. + Alignment parameters are reported, never silently applied. + +### 3. Agreement metrics + +- `AgreementReport`: n paired samples, coverage fraction (time where both + series had valid samples), MAE, RMSE, mean error (bias), Bland–Altman + 95% limits of agreement, and percentage-within-tolerance (configurable, + default ±2 bpm HR / ±1 brpm breathing). +- Session scope is mandatory metadata: subject count, motion state + (static/moving), line-of-sight (LOS/NLOS/through-wall), distance band. + A report without scope cannot be constructed. + +### 4. Evidence tagging + +- `EvidenceGrade::Measured` is only constructible when the report carries a + reference device, non-zero paired samples, minimum coverage, and a + reproducer command string; otherwise the report grades as `Claimed` (real + data, no reference) or `Synthetic` (generated input). This mirrors + ADR-291's enforcement-in-types approach and the CLAUDE.md tagging rule. + +## Consequences + +- RuView can convert vitals claims from CLAIMED to MEASURED with a + reproducible offline analysis, session by session, scope by scope. +- Honest reporting will likely show heart-rate performance below marketing + intuition, especially NLOS/moving — that is the purpose. +- CSV ingest means a manual export step per session; acceptable at current + scale, and the format is the de-facto export of consumer reference gear. +- No clinical claim is implied: agreement statistics against consumer + reference devices are engineering evidence, not medical validation. + +## Validation + +- `cargo test -p wifi-densepose-vitals` — CSV rejection cases, alignment + recovery of known synthetic offsets/drifts, agreement metrics against + hand-computed fixtures, evidence-grade constructibility rules. +- `cargo bench -p wifi-densepose-vitals` — criterion benchmark for alignment + over hour-scale synthetic sessions. +- Real-session validation (ESP32 capture + chest strap) remains a follow-up + requiring hardware evidence per CLAUDE.md. diff --git a/api-docs/adr/ADR-294-wifi-veil-integration.md b/api-docs/adr/ADR-294-wifi-veil-integration.md new file mode 100644 index 00000000..5e4c7691 --- /dev/null +++ b/api-docs/adr/ADR-294-wifi-veil-integration.md @@ -0,0 +1,83 @@ +# ADR-294: WiFi Veil integration — emission-shaping countermeasure as an advisory BFLD dependency + +- **Status**: Accepted — initial implementation (this PR) +- **Date**: 2026-08-10 +- **Deciders**: ruv +- **Tags**: privacy, bfld, bfi, wifi-veil, countermeasure, dependency + +## Context + +RuView's BFLD layer (ADR-118, ADR-141) senses via beamforming feedback while +enforcing structural privacy invariants on data entering the node. The 2026 +research sweep identified the complementary, unaddressed surface: a node's own +*outgoing* BFI is unencrypted and enables passive third-party +re-identification (BFId, ACM CCS 2025); IEEE 802.11bf-2025 shipped with no +privacy mechanism; and no commercial product occupies the countermeasure +category. + +[`wifi-veil`](https://github.com/ruvnet/wifi-veil) (codename VEIL, extracted +from this monorepo as a standalone crate) models a compliant emission-shaping +defense: keyed Givens rotations over the fine subspace of compressed +beamforming reports, energy-preserving (never jamming), reversible by a +keyed legitimate receiver. The crate is dependency-free, deterministic, +std-only, WASM-ready, dual MIT/Apache-2.0, and explicitly SYNTHETIC/L0: it +models waveform controls and never drives a radio. + +RuView should consume this capability rather than re-implement it, giving the +sensing stack a defensive counterpart under one evidence regime. + +## Options considered + +1. **Vendor the veil sources into a RuView crate.** Rejected: forks the + witness-pinned upstream and duplicates maintenance. +2. **crates.io dependency.** Not yet available (v0.1.0 unpublished at + decision time); revisit when released. +3. **Git dependency pinned to an exact rev, feature-gated in + `wifi-densepose-bfld`.** Chosen. + +## Decision + +- Add `wifi-veil` to `v2/Cargo.toml` `[workspace.dependencies]` as a git + dependency pinned to rev `018468b5d2bf41f35c552910f35659830af0eb91` + (v0.1.0). Exact-rev pinning preserves provenance and reproducibility for a + pre-release upstream; bumping the rev is an explicit, reviewable change. +- Gate it in `wifi-densepose-bfld` behind a new `veil` feature + (`veil = ["std", "dep:wifi-veil"]`), off by default — the default build + remains dependency-light and unchanged. +- New `bfld::veil` module (advisory-only): + - `ShieldAssessment`: stable projection of wifi-veil's deterministic + attacker-vs-protector `ExperimentReport` (re-ID accuracy shield-off/on, + chance level, throughput ratio, energy-conservation audit), always + carrying the `SYNTHETIC/L0` evidence label. + - `assess` / `assess_default`: run the deterministic experiment. + - `optimized_shield`: wrap `hyper_optimize` to derive the + optimizer-shipped shield config plus its verifying assessment. +- Boundaries, stated structurally and in docs: + - **Advisory only.** Nothing in the integration emits RF, alters frames, + or relaxes any BFLD gate/invariant (I1–I3 untouched). + - **Evidence honesty.** Every veil-derived figure is labeled + `SYNTHETIC/L0`; no MEASURED claim is possible from this path (hardware + validation lives in wifi-veil's own P5 roadmap). + - ESP32 nodes cannot shield their own feedback (per wifi-veil's platform + matrix); the integration therefore informs posture and reporting, not + on-node emission control. + +## Consequences + +- RuView gains a sense-and-defend posture no commercial offering has, under + a single claim taxonomy. +- First git dependency in the workspace: builds now fetch one pinned + external rev. Acceptable: the crate is dependency-free, small, witness- + pinned upstream, and license-compatible (MIT OR Apache-2.0 into MIT). +- Feature-gated consumers (e.g. sensing-server privacy reporting, the + desktop UI) can surface shield assessments later without new deps. +- When wifi-veil publishes to crates.io, switch the workspace entry to a + version requirement in a follow-up ADR amendment. + +## Validation + +- `cargo test -p wifi-densepose-bfld --features veil` — determinism, + shield-reduces-re-ID, compliance (energy conservation), chance-band + attainment, evidence labeling, optimizer wrapper. +- `cargo test -p wifi-densepose-bfld` (default features) — unchanged + behavior with the feature off. diff --git a/api-docs/adr/ADR-295-source-provenance-state-machine.md b/api-docs/adr/ADR-295-source-provenance-state-machine.md new file mode 100644 index 00000000..c101d9e6 --- /dev/null +++ b/api-docs/adr/ADR-295-source-provenance-state-machine.md @@ -0,0 +1,64 @@ +# ADR-295: Source provenance state machine — synthetic can never present as live + +- **Status**: Accepted — initial implementation (this PR) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: provenance, honesty, ui, sensing-server, security + +## Context + +An August 2026 external review found two provenance defects on the release +path: + +1. The pose-fusion simulator starts in demo mode; on any page port other than + 3000 the WebSocket target falls back to `localhost:8765`, and if the + connection fails the simulator keeps running while the status still reads + "ready" — producing a convincing moving visualization with no live CSI + (issue 1557). +2. The main sensing client labels the source **live** when the authenticated + status endpoint returns an error for lack of authorization, until a real + frame happens to correct it (issue 1526). + +The common root cause: source state is a boolean (live vs not), so "unknown" +collapses to "live". CLAUDE.md requires MEASURED/CLAIMED/SYNTHETIC labeling +and forbids presenting synthetic output as real. + +## Decision + +Define one canonical, mutually exclusive `SourceState` enum shared by the +sensing server and every UI/client that renders a source: + +- `Synthetic` — generated data (simulator/replay of synthetic fixtures). +- `LiveVerified` — frames from an authenticated, attested source. +- `LiveUnverified` — frames arriving but provenance not yet confirmed. +- `Stale` — last frame older than a configured freshness window. +- `Disconnected` — no source. + +Rules enforced structurally: + +- **`Unknown` is not a state.** Any ambiguous condition resolves to + `LiveUnverified`, `Stale`, or `Disconnected` — never `LiveVerified`. +- A status-endpoint error resolves to `Disconnected`/`LiveUnverified`, never + live-verified. +- The simulator constructs `Synthetic` and cannot transition to any `Live*` + state without a verified frame. +- `Synthetic` is watermarked in every view and every export. +- Transitions are a pure function of (last-frame-age, auth-status, + source-kind) so they are unit-testable without a clock or a socket. + +Scope of this PR: the shared `SourceState` type + transition function + tests +in the sensing server, and wiring of the two identified surfaces (pose-fusion +simulator status, sensing client source label). Broader UI adoption follows. + +## Consequences + +- Closes the "synthetic shown as live" and "unknown shown as live" classes. +- A small breaking change to any consumer currently reading a boolean source + flag; mitigated by exposing a compatibility accessor during migration. + +## Validation + +- Unit tests for every transition, especially: auth-error → not-live; + simulator → never live without a verified frame; freshness expiry → `Stale`; + watermark present on synthetic export. +- `cargo test -p wifi-densepose-sensing-server`. diff --git a/api-docs/adr/ADR-296-sensor-data-plane-bind-hardening.md b/api-docs/adr/ADR-296-sensor-data-plane-bind-hardening.md new file mode 100644 index 00000000..d6c02792 --- /dev/null +++ b/api-docs/adr/ADR-296-sensor-data-plane-bind-hardening.md @@ -0,0 +1,59 @@ +# ADR-296: Sensor data-plane hardening — UDP bind control and source allowlist (step one) + +- **Status**: Accepted — initial implementation (this PR) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: security, udp, sensor-ingest, sensing-server + +## Context + +The CSI UDP receiver binds `0.0.0.0:{udp_port}` unconditionally +(`main.rs:5706`), with no equivalent of the HTTP `--bind-addr` flag (which +correctly defaults to `127.0.0.1`), no source allowlist, no message +authentication, no device identity, and no replay defense. Any host that can +reach the UDP port can inject a valid-shaped frame, flip an auto-detecting +server into a live source state, and influence presence/vital/automation +outputs (issue 1394). + +An IP allowlist does not stop LAN spoofing, but bind control plus an allowlist +is the correct, shippable first step; per-device keys + authenticated +encryption + monotonic sequence + freshness window + replay rejection is the +full fix and is larger. + +## Decision + +**This PR (step one):** + +- Add `--udp-bind` (env `RUVIEW_UDP_BIND`), **defaulting to `127.0.0.1`**. + Binding to a routable address is now an explicit operator choice, mirroring + the HTTP path. Desktop/appliance defaults stay loopback. +- Add an optional source IP/CIDR allowlist (`--udp-allow`); when set, frames + from other sources are dropped and counted. Loopback is always allowed. +- Emit a startup security log line stating the bind scope and whether an + allowlist is active; refuse a routable bind without an allowlist unless an + explicit `--udp-insecure-lan` override is passed (parallel to the existing + Docker HTTP refusal). +- Publish a `SECURITY.md`/advisory note describing the threat model and safe + deployment. + +**Explicitly deferred to a follow-up ADR (step two):** per-device provisioned +keys, MAC/AEAD, device identifiers, monotonic sequence numbers, freshness +window, and replay rejection. This ADR documents that gap rather than +implying the data plane is authenticated. + +## Consequences + +- Removes the default open-to-LAN exposure with a one-line-safe default. +- Not spoof-proof on a trusted LAN — the advisory says so plainly, and the + override name (`--udp-insecure-lan`) makes the residual risk legible. +- A behavior change for anyone relying on the old implicit `0.0.0.0` default; + called out in the changelog and the startup log. + +## Validation + +- Unit tests: default bind is loopback; routable bind without allowlist is + refused unless overridden; allowlist accept/drop with counting; loopback + always allowed. +- `cargo test -p wifi-densepose-sensing-server`. +- Real-silicon validation of the LAN path remains required before any + deployment claim. diff --git a/api-docs/adr/ADR-297-multi-node-semantic-correctness.md b/api-docs/adr/ADR-297-multi-node-semantic-correctness.md new file mode 100644 index 00000000..f4e0b8dd --- /dev/null +++ b/api-docs/adr/ADR-297-multi-node-semantic-correctness.md @@ -0,0 +1,58 @@ +# ADR-297: Multi-node semantic correctness — per-node inference, node-keyed rate limiting, stale state + +- **Status**: Accepted — initial implementation (this PR) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: multi-node, mqtt, home-assistant, correctness, sensing-server + +## Context + +The external review confirmed three defects on the multi-node path — the core +mechanism RuView uses to reduce blind spots and room dependence: + +1. The active `NodeInfo` payload carries RSSI/position/subcarrier/sync but **no + per-node classification**; the MQTT mapper reads `node.classification` and + falls back to the room aggregate when absent, so every node can publish the + same aggregate presence value (issues 1540, 1554). +2. The MQTT `RateLimiter` is keyed by `EntityKind` only + (`mqtt/state.rs:65`), so one node consumes the numeric publish slot and the + others are suppressed until the interval expires, while availability still + says online (issue 1541). +3. In the UDP vital path, top-level classification is taken from the + latest-arriving node while other features are fused, so with disagreeing + nodes room presence can flip at packet frequency (issue 1555). + +## Decision + +- **Separate the types.** Introduce `NodeInference` (per-node classification + + confidence + freshness) distinct from `RoomInference` (the fused room + aggregate). `NodeInfo` carries a `NodeInference`; the room aggregate is + computed explicitly and never overwrites node state. No silent fallback from + node to room. +- **Key the rate limiter by (node, entity).** `RateLimiter` becomes keyed on + `(NodeId, EntityKind)` so nodes no longer starve each other; per-entity + behavior per node is preserved. +- **Deterministic fusion.** Room classification is a pure function of the set + of current per-node inferences (e.g. freshness-weighted vote), not + last-writer-wins; identical inputs yield identical room state. +- **Stale entities cannot stay online.** An entity whose backing node has not + reported within N expected publish intervals transitions to unavailable/ + stale rather than holding a frozen value while availability says online. + +## Consequences + +- Multi-node HA/MQTT output becomes semantically correct; distinct nodes + report distinct state and no longer suppress one another. +- Schema change to `NodeInfo`/the MQTT contract; existing single-node + deployments keep working (one node = one inference). Consumers reading the + old aggregate-only shape need the migration accessor. +- Aligns with ADR-295 (freshness) and the review's call for one canonical + `NodeInference`/`RoomInference` contract. + +## Validation + +- Unit/integration tests: per-node classification round-trips through the MQTT + mapper with no room fallback; two nodes with different rates both publish + (no starvation); disagreeing nodes produce deterministic, non-flapping room + state; a silent node's entities go stale, not frozen-online. +- `cargo test -p wifi-densepose-sensing-server`. diff --git a/api-docs/adr/ADR-298-model-release-sanity-gates.md b/api-docs/adr/ADR-298-model-release-sanity-gates.md new file mode 100644 index 00000000..6af231e1 --- /dev/null +++ b/api-docs/adr/ADR-298-model-release-sanity-gates.md @@ -0,0 +1,60 @@ +# ADR-298: Model release sanity gates — block degenerate and mislabeled model artifacts + +- **Status**: Accepted — initial implementation (this PR) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: models, evaluation, release-gate, honesty, presence + +## Context + +The external review (corroborating issue 1521) showed the published presence +head is mathematically degenerate: with L2-normalized embeddings, a weight +norm ≈ 3.67 against a bias ≈ 8.19 makes the smallest possible logit positive, +so predicted presence probability is ≥ ~0.989 for every valid input — the +decision boundary is unreachable and the head is effectively constant. The +README then labeled a temporal-triplet accuracy (a representation-ordering +metric) as "presence accuracy" — a category error. + +Nothing in the release path catches a constant classifier, an unreachable +boundary, or a metric-name mismatch. A machine check would have. + +## Decision + +Add a `model_gates` module (in `wifi-densepose-train`) plus a CI gate that, +for any classifier artifact proposed for release, fails on: + +- **Constant output** — output variance below a threshold across a diverse + probe set (including the degenerate-embedding probe from issue 1521). +- **Unreachable decision boundary** — for a normalized-embedding linear head, + check whether `bias` sign dominates `‖weight‖` so the logit cannot change + sign; fail if the boundary is analytically unreachable. +- **Degenerate class balance** — predicted-positive rate at/above a ceiling + (e.g. > 99%) on a balanced probe set. +- **Missing/blank baseline** — a report without a paired mean-pose/majority + baseline (ties into ADR-291 `EvaluationReport`). +- **Metric-name provenance** — a metric may not be surfaced under a task name + that does not match its computed kind (temporal-triplet ≠ presence); + enforced by making the metric carry its kind and the label derive from it. + +Each gate emits a structured, human-readable failure explaining the defect and +the offending numbers. + +## Consequences + +- The specific degenerate presence head cannot ship again, and the + temporal-triplet-as-presence mislabel is structurally prevented. +- Some existing artifacts will fail the gate on introduction — intended; they + should fail. +- The gate is heuristic, not a correctness proof; it catches the known + failure shapes, not all bad models. + +## Validation + +- Unit tests: the issue-1521 weights fail the unreachable-boundary and + constant-output gates; a healthy synthetic head passes; a temporal-triplet + metric cannot be constructed with a presence label. +- `cargo test -p wifi-densepose-train`; the CI gate runs in the model-check + workflow. +- This ADR does **not** withdraw the already-published artifact (an + outward-facing action requiring maintainer sign-off) — it prevents + recurrence and documents the model-card correction. diff --git a/api-docs/adr/ADR-299-csi-data-incident-repo-controls.md b/api-docs/adr/ADR-299-csi-data-incident-repo-controls.md new file mode 100644 index 00000000..edb677da --- /dev/null +++ b/api-docs/adr/ADR-299-csi-data-incident-repo-controls.md @@ -0,0 +1,50 @@ +# ADR-299: Repository CSI data-incident controls — ignore rules and a pre-commit/CI policy check + +- **Status**: Accepted — controls implemented; tree remediation gated on owner sign-off +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: privacy, data-governance, ci, security, incident + +## Context + +The external review found ~64.6 MB of tracked raw CSI recordings under +`data/recordings/` and `v2/data/recordings/` (largest an ~61.8 MB overnight +capture). CLAUDE.md explicitly prohibits committing CSI or person data. The +`.gitignore` rule pointed only at a pre-rename path +(`rust-port/wifi-densepose-rs/data/recordings/`) and did not cover the active +directories, which is how the captures were committed. Raw CSI is person data +(it encodes breathing, movement, presence), so this is a data incident, not a +formatting nit. + +## Decision + +**Implemented now (mechanical, no data-ownership judgment):** + +- Fix `.gitignore` to cover `data/recordings/`, `v2/data/recordings/`, the + legacy path, and `*.csi.jsonl` / `*.csi.meta.json` globs (done in this PR). +- Add a policy check (pre-commit hook + CI job) that fails when CSI-format + files (`*.csi.jsonl`, `*.csi.meta.json`) or large JSONL captures are staged + or present as tracked files, with a message pointing here. Tests may use + only synthetic or expressly-consented minimal fixtures. + +**Explicitly gated on data-owner sign-off (NOT done autonomously):** + +- Removing the existing recordings from the tree, and any history rewrite, are + outward-facing/destructive and require the data owner to first establish + provenance, consent, purpose, retention authority, and redistribution + rights. The review is correct that rewriting `origin` does not erase forks + and clones; coordination is required. This ADR records the controls and the + required follow-up; it does not delete the data. + +## Consequences + +- No new CSI captures can be committed (ignore + policy check). +- The existing tracked recordings remain until the owner decides; the incident + is documented and the guard prevents worsening it. +- CI gains one fast policy job; contributors get a local pre-commit check. + +## Validation + +- Policy-check unit tests: a staged `*.csi.jsonl` fails; a synthetic fixture + under an allowed test path passes; the check is deterministic and offline. +- Manual confirmation that the new ignore globs cover both active directories. diff --git a/api-docs/adr/ADR-300-perception-substrate-program.md b/api-docs/adr/ADR-300-perception-substrate-program.md new file mode 100644 index 00000000..11b4d087 --- /dev/null +++ b/api-docs/adr/ADR-300-perception-substrate-program.md @@ -0,0 +1,189 @@ +# ADR-300: RuView perception substrate — a phased program for the calibration, evidence, trust, and deployment layer + +- **Status**: Accepted — program framing; child ADRs carry their own status +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: program, architecture, calibration, evidence, provenance, fusion, fleet, epic + +## Context + +Three independent analyses converged on the same conclusion in 2026: a deep +research sweep of the WiFi-sensing state of the art, an external technical and +industry review, and an internal strategic assessment. All three found that +RuView's gap is **not another sensing modality** but the horizontal layer that +turns RF research into repeatable spatial infrastructure — measurement, +calibration, out-of-distribution awareness, evidence accounting, authenticated +identity, a canonical spatial model, and fleet deployment. + +Several of these primitives already have foundations in the tree and should be +**unified and made to produce signed, expiring certificates**, not rebuilt: + +- `wifi-densepose-calibration` (enrollment, bank, anchor, runtime, specialist). +- `frame::EvidenceLevel` L0–L5 as mandatory policy (ADR-282). +- AetherArena benchmark infrastructure — v0 complete, CI-gated, witness ledger, + live HF Space (ADR-149); board intentionally empty (benchmark-first). +- RuField provenance/signature types (ADR-260/262/277/279) and BFLD + attestation (ADR-141). +- `worldgraph` crate; `wifi-densepose-mat/tracking` (tracker, fingerprint). +- The in-flight ADR-295 (provenance state machine), ADR-296 (authenticated + data plane, step one), ADR-298 (model sanity gates) — the first bricks. + +## What RuView is optimizing for + +Not inference capability — **epistemic reliability**: + +``` +signal → observation → calibration → inference → uncertainty → evidence + → certificate → policy → governed action +``` + +That pipeline is the product. The defensible category is not "RuView perceives +the physical world" but "RuView determines what machines are justified in +believing about it, proves why, and constrains what they may do with that +belief." + +### Four non-negotiable program rules + +Every child ADR and implementation is bound by these: + +1. **UNKNOWN is a first-class output, never an error condition.** A surface that + cannot answer says UNKNOWN and stays legible; it does not throw, default to a + confident class, or silently hold a stale value. +2. **Capability certificates bind cryptographically.** Hardware, environment, + model, calibration, metrics, expiry, and evidence level are bound under one + signature (ADR-318/ADR-305). An unsigned or partially-bound certificate is + not a certificate. +3. **One canonical semantics downstream.** Every surface (MQTT, REST, WebSocket, + RuField, Matter, agents, UI) consumes the same Observation → Inference → + GovernedEvent types (ADR-306). No transport- or UI-specific reinterpretation. +4. **Benchmarks expose worst-domain performance and confidence intervals.** + Pooled accuracy is never sufficient for promotion (ADR-317). + +### Certificate conditionality (the staleness guard) + +The central architectural risk is **certificate staleness**: a room can remain +syntactically calibrated while its RF distribution has drifted enough to +invalidate the certificate. Therefore a capability certificate is **conditional +on a continuously evaluated domain signature** (ADR-302), not a one-time stamp. +Crossing the OOD threshold automatically degrades state and triggers +recalibration rather than silently continuing: + +``` +VALID → DEGRADED → UNKNOWN (auto-degrade on domain drift; triggers recalibration) +``` + +This binds ADR-301 (calibration), ADR-302 (OOD), ADR-318 (certificate), and +ADR-321 (policy): a degraded/unknown domain must invalidate the affected +capability *before* a false confident inference reaches an actuator. + +### Commercial framing — three primitives, not one product + +- **RuView Runtime** — provides perception. +- **RuView Certify** — establishes what a deployment can legitimately claim + (calibration + evidence + capability certificate + policy). +- **RuView Trust / Fleet** — keeps that claim valid across hardware, firmware, + models, and environmental drift (ADR-316). + +Certify and Trust are the parts that are hard to commoditize; presence +detection alone is not. + +## Decision + +Adopt a **21-primitive phased program**. Each primitive gets a child ADR +(ADR-301…ADR-321) that owns its detailed decision, status, and validation. +This ADR owns the framing, the dependency order, and the phase assignment. + +### Primitive → ADR map + +| # | Primitive | ADR | Phase | +|---|---|---|---| +| 1 | Automatic domain calibration | ADR-301 | 1 | +| 2 | Out-of-distribution detection | ADR-302 | 1 | +| 3 | Ground-truth synchronization | ADR-303 | 2 | +| 4 | Evidence engine | ADR-304 | 1 | +| 5 | Authenticated sensor identity | ADR-305 | 1 | +| 6 | Canonical spatial ontology | ADR-306 | 1 | +| 7 | Persistent identity & tracking | ADR-307 | 2 | +| 8 | Sensor placement optimizer | ADR-308 | 3 | +| 9 | Active sensing | ADR-309 | 3 | +| 10 | 802.11bf-native architecture | ADR-310 | 2 | +| 11 | Real sensor fusion | ADR-311 | 2 | +| 12 | Long-term spatial memory | ADR-312 | 3 | +| 13 | Counterfactual inference | ADR-313 | 3 | +| 14 | Information-gain scheduler | ADR-314 | 3 | +| 15 | Digital RF twin | ADR-315 | 3 | +| 16 | Fleet control plane | ADR-316 | 2 | +| 17 | Real benchmark service (multi-domain scorecard) | ADR-317 | 1 | +| 18 | Capability certificates | ADR-318 | 1 | +| 19 | Witness chain | ADR-319 | 1 | +| 20 | RuView sensor HAL | ADR-320 | 2 | +| 21 | Decision policy — action authorization | ADR-321 | 1 | + +### Dependency order (why phase, not score, drives sequencing) + +``` + ADR-306 spatial ontology ──┐ + ADR-305 auth identity ─────┼──► ADR-301 calibration cert ──► ADR-302 OOD gating + │ │ │ + └──► ADR-319 witness chain │ (VALID→DEGRADED→UNKNOWN) + │ ▼ + ADR-304 evidence engine ──► ADR-318 capability certificate + │ │ (conditional on domain signature) + │ ▼ + │ ADR-321 decision policy ──► governed action + └──► ADR-317 benchmark scorecard (per-PR gate) +``` + +- **Phase 1 (the certificate spine, built now):** foundational roots 303, 302, + 301, 298 (implemented first, in their own crates); then the dependent wave + 316, 299, 315, 314, 318. This set is exactly the acceptance test decomposed + and is buildable without new hardware (types, logic, signatures, tests). The + dependent wave adds the staleness guard (299 auto-degrades 315) and the + action gate (318) that denies at the actuator on a degraded/unknown domain. +- **Phase 2 (integration & operations):** 300 ground truth, 304 tracking, 307 + 802.11bf-native, 308 fusion, 313 fleet, 317 HAL. Depends on the spine. +- **Phase 3 (higher-ceiling, research-forward):** 305 placement optimizer, 306 + active sensing, 309 spatial memory, 310 counterfactual, 311 info-gain + scheduler, 312 RF twin. Sit on top of the fused world state. + +Phase-2 and phase-3 child ADRs are authored as **Proposed** (design intent, +validation plan) and are not implemented by the phase-1 swarm. + +### Acceptance test A — onboarding (from the strategic assessment) + +> Connect a new sensor type in an unseen room. Within 30 minutes RuView should +> identify the hardware (HAL, ADR-320), calibrate the environment (ADR-301), +> quantify whether it can reliably sense the requested phenomenon (ADR-302), +> generate a signed capability certificate (ADR-318), expose governed spatial +> events (ADR-306), and return UNKNOWN whenever evidence falls outside that +> certificate (ADR-302). + +### Acceptance test B — drift invalidation (the staleness guard) + +> Deliberately change the room after certification — move furniture, change the +> AP channel, or substitute hardware. RuView should detect distribution drift +> (ADR-302), invalidate the affected capability (ADR-318) **before** a false +> confident inference reaches an actuator (ADR-321 denies with the specific +> failed condition), emit UNKNOWN, preserve the complete witness chain +> (ADR-319), and explain exactly which certificate condition failed. + +Test B is the load-bearing one: it proves the substrate fails safe, not just +that it perceives well. Phase 1 makes every clause except HAL testable in +software; HAL (phase 2) +closes the "identify the hardware" clause. + +## Consequences + +- One coherent substrate replaces overlapping ad-hoc schemas; every surface + (MQTT, REST, WebSocket, RuField, Matter, agents) eventually consumes the + ADR-306 ontology and the ADR-318 certificate. +- Headline applications (pose/vitals/pointcloud models) are explicitly **not** + the investment focus during this program, per the strategic direction. +- Later ADRs may be revised as the spine lands; that is expected for a phased + program and is why phase-2/3 ADRs ship as Proposed. + +## Validation + +- Each child ADR defines its own tests. The program-level exit is the + acceptance test above, run end-to-end once phase 1 lands, and encoded as an + AetherArena scenario (ADR-317). diff --git a/api-docs/adr/ADR-301-automatic-domain-calibration.md b/api-docs/adr/ADR-301-automatic-domain-calibration.md new file mode 100644 index 00000000..4d48606c --- /dev/null +++ b/api-docs/adr/ADR-301-automatic-domain-calibration.md @@ -0,0 +1,149 @@ +# ADR-301: Automatic domain calibration — signed, versioned, invalidatable room fingerprint + +- **Status**: Accepted — initial implementation planned (ADR-300 phase 1) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: calibration, provenance, drift, evidence, honesty, substrate + +## Context + +This ADR is primitive 1 of the perception-substrate program (ADR-300) and the +first brick of that program's "certificate spine" (ADR-300 phase 1). It depends +on the canonical spatial ontology (ADR-306) to name *which space* it +characterizes, on authenticated sensor identity (ADR-305) to bind a fingerprint +to *which signed device* produced it, and on the witness chain (ADR-319) to +anchor the resulting artifact. Its output is consumed directly by +out-of-distribution detection (ADR-302). + +WiFi sensing is only reproducible inside the environment it was tuned for. +Multipath, furniture geometry, transceiver placement, and AP channel all shape +the CSI distribution, so a model that reads a room correctly one week can drift +silently the next. RuView already has the raw ingredients for room-aware +sensing but not a single portable, signed, expiring artifact that says "this is +the room, here is when it was measured, and here is the evidence that it is +still the same room." + +Existing scaffolding to build on, not rebuild (`v2/crates/wifi-densepose-calibration`): + +- `enrollment` / `anchor` — guided human anchors with an adaptive quality gate. +- `bank` / `specialist` / `runtime` — a versioned bank of small specialist + models and a confidence-gated mixture runtime (`RoomState`), including the + crate's existing honest `STALE` degradation when the ADR-135 empty-room + baseline drifts. +- `geometry` / `geometry_embedding` — transceiver-geometry record and its + fixed-length conditioning featurization (ADR-152). + +What is missing is (a) an *automatic* observe-only characterization phase that +does not require a human enrollment ritual, (b) empty-vs-occupied baseline +separation as a first-class pair, (c) a signed, versioned, comparable +`CalibrationCertificate` artifact, and (d) explicit invalidation on drift rather +than a soft `STALE` flag buried in the runtime. + +## Options considered + +1. **Keep calibration internal to the runtime (status quo).** Rejected: the + room characterization exists only as in-process state; it cannot be signed, + shipped, compared across time, or presented as evidence to ADR-302/ADR-318. +2. **Build a new calibration crate.** Rejected: `wifi-densepose-calibration` + already owns enrollment, the specialist bank, geometry embedding, and the + baseline-drift concept. A parallel crate would fork the room model. +3. **Extend `wifi-densepose-calibration` with an automatic characterization + phase and a signed certificate artifact.** Chosen. + +## Decision + +Extend `v2/crates/wifi-densepose-calibration` with an `autocal` characterization +phase and a `certificate` artifact module. The target UX is: + +> install → observe (~10 min) → room fingerprint → calibration certificate → +> sensing. + +### 1. Automatic characterization (`autocal`) + +- An observe-only pass (default ~10 minutes, configurable) that collects CSI + without requiring guided human anchors, reusing the `anchor` quality gate to + reject frames it cannot trust. It layers on the existing ADR-135 empty-room + baseline rather than replacing it. +- Produces a `RoomFingerprint`: a bounded, fixed-length statistical summary of + the room's CSI distribution (subcarrier amplitude/phase moments, multipath + structure, occupancy-band energy), plus the `geometry_embedding` when a + geometry record is present. The fingerprint is the distance-comparable object + ADR-302 measures against; its schema is versioned. + +### 2. Empty / occupied baseline pair + +- Characterization establishes a paired baseline: an **empty** distribution + (no occupant motion) and an **occupied** distribution (motion present), + separated by the existing occupancy signal rather than a manual label. Both + are stored on the fingerprint so downstream OOD gating can distinguish "the + empty room changed" (furniture/geometry drift) from "occupancy statistics + changed" (different subject dynamics). + +### 3. `CalibrationCertificate` artifact + +- A serializable `CalibrationCertificate` binding: the `RoomFingerprint`; a + space identifier from the ADR-306 ontology; the signing sensor identity from + ADR-305; `captured_at_unix_s`; a monotonic `version`; a schema version; the + calibration `tier`; and an `EvidenceLevel` (L0–L5, ADR-282) — an automatic + characterization on real captured CSI is at most L1/L2 and is labelled as + such, never L3+. +- The certificate is **signed** using RuField provenance/signature types + (ADR-260/262/277/279) and anchored in the witness chain (ADR-319). Signature + and witness anchoring are mandatory: an unsigned certificate is not a valid + certificate. +- Two certificates for the same space are **comparable**: `distance(a, b)` + returns a bounded fingerprint distance, which is the primitive ADR-302 uses + to gate KNOWN → DEGRADED → UNKNOWN. + +### 4. Invalidation and continuous drift compensation + +- A certificate carries an explicit validity policy: it is invalidated when + fingerprint distance against live traffic exceeds a threshold, when the AP + channel or transceiver geometry changes, when the signing device identity + changes, or on age expiry. Invalidation is an explicit state transition that + emits a witness record (ADR-319), not a silent `STALE` flag. +- Continuous drift compensation runs as a bounded online update of the + fingerprint within a **compatibility envelope**: small drift is absorbed and + logged; drift beyond the envelope invalidates the certificate and forces + re-characterization. Compensation never silently rewrites a signed + certificate — it produces a new version, preserving the append-only history. + +### Provenance and honesty discipline + +- No accuracy number is claimed by this ADR; it delivers the artifact and the + distance/invalidation machinery. Any certificate produced from generated CSI + is L0/`Synthetic` by construction; the constructor rejects labelling + synthetic characterization as measured (ADR-279 invariant 6, ADR-282 ladder). +- Certificates never leave the edge except through the governed control plane + (ADR-277); a room fingerprint is treated as potentially sensitive spatial + data, not free telemetry. + +## Consequences + +- Room characterization becomes a portable, signed, versioned artifact that + ADR-302 (OOD), ADR-318 (capability certificates), and ADR-317 (benchmark) + can consume without re-deriving room state. +- The automatic observe-only path lowers deployment friction (no mandatory + enrollment ritual) but yields a weaker evidence level than guided enrollment; + the certificate states which path produced it so consumers can weight it. +- Explicit invalidation means RuView will sometimes refuse to sense a changed + room until re-characterization. That refusal is the intended honest behavior, + surfaced by ADR-302, not a regression. +- The existing enrollment/bank/runtime path is preserved; `autocal` is an + additional entry point that produces the same `RoomFingerprint` object the + guided path can also emit. + +## Validation + +- `cargo test -p wifi-densepose-calibration` — fingerprint determinism from + fixed synthetic CSI; empty/occupied separation on synthetic occupancy; + certificate signing/verification round-trip and tamper rejection; + `distance()` monotonicity on progressively perturbed fixtures; invalidation + transitions (channel change, geometry change, age, drift-envelope breach) + each emit the expected witness record; constructor rejects synthetic→measured + mislabeling. +- Cross-ADR: an ADR-302 test consumes a certificate and asserts the gating + state transitions on a drifted fingerprint. +- Real-silicon characterization (ESP32 capture over a real 10-minute window) + remains a follow-up requiring hardware evidence per CLAUDE.md; a successful + build or synthetic run is not hardware evidence. diff --git a/api-docs/adr/ADR-302-out-of-distribution-detection.md b/api-docs/adr/ADR-302-out-of-distribution-detection.md new file mode 100644 index 00000000..88726158 --- /dev/null +++ b/api-docs/adr/ADR-302-out-of-distribution-detection.md @@ -0,0 +1,135 @@ +# ADR-302: Out-of-distribution detection — KNOWN / DEGRADED / UNKNOWN gating + +- **Status**: Accepted — initial implementation planned (ADR-300 phase 1) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: ood, calibration, uncertainty, quality, evidence, honesty, substrate + +## Context + +This ADR is primitive 2 of the perception-substrate program (ADR-300) and part +of the phase-1 certificate spine. It sits directly downstream of automatic +domain calibration (ADR-301): the `CalibrationCertificate` and its +`RoomFingerprint` are the reference distribution this ADR measures against. It +reuses fusion-layer quality scoring (ADR-137) as one of its inputs and feeds +its state into the evidence engine (ADR-304) and capability certificates +(ADR-318). + +The central unsolved problem of WiFi sensing is cross-domain generalization: a +model trained (or calibrated) in one room degrades unpredictably in another, or +in the same room after furniture moves, the AP changes channel, or the radio +hardware is swapped. A model that keeps returning confident classifications +under these conditions is the single most misleading failure mode in the field, +and it is the failure the strategic assessment (ADR-300) named explicitly. +Confidence alone is insufficient: a softmax head is perfectly capable of being +confidently wrong on out-of-distribution input. RuView must be able to say +"I do not recognize this situation" instead of guessing. + +Today RuView has partial signals but no unified gate: + +- ADR-301 produces a comparable `RoomFingerprint` and a `distance()` metric. +- ADR-137 `QualityScore` carries fusion coherence, evidence references, and + contradiction flags per fused frame. +- Model heads emit confidence/uncertainty, but nothing combines domain + distance, signal quality, calibration compatibility, and uncertainty into a + single decision, and nothing forces a model to stop emitting confident labels + when it leaves its calibrated domain. + +## Options considered + +1. **Threshold on model confidence alone.** Rejected: confidently-wrong OOD + predictions are exactly the failure mode; confidence is necessary but not + sufficient. +2. **A per-model bespoke OOD check inside each task head.** Rejected: + duplicates logic, cannot be audited uniformly, and does not compose with the + calibration certificate or the evidence engine. +3. **A shared OOD gate that every inference passes through, fusing four signals + against the ADR-301 certificate.** Chosen. + +## Decision + +Add an out-of-distribution gate — implemented in a shared crate consumed by the +task-head runtime (`wifi-densepose-calibration::runtime` and the model serving +path) — that attaches a `DomainState` to **every** inference. + +### 1. Four inputs, one decision + +Each inference carries four measured quantities: + +1. **Domain distance** — fingerprint distance (ADR-301 `distance()`) between + live traffic and the active `CalibrationCertificate`, split into the + empty-baseline and occupied-baseline components so geometry drift and + occupancy-statistics drift are distinguishable. +2. **Signal quality** — reuse the ADR-137 quality scoring signals (fusion + coherence, contradiction flags) plus per-frame SNR/validity. +3. **Calibration compatibility** — is a valid, non-invalidated certificate + present for this space (ADR-306) and this signed device (ADR-305)? An + expired, invalidated, or device-mismatched certificate is itself a + compatibility failure. +4. **Uncertainty** — the model head's own predictive uncertainty. + +### 2. State machine: KNOWN → DEGRADED → UNKNOWN + +- **KNOWN** — domain distance within the certificate's compatibility envelope, + quality above threshold, certificate valid and compatible, uncertainty low. + Confident classifications are returned. +- **DEGRADED** — one or more signals crossed a soft threshold (e.g. moderate + fingerprint drift within the envelope, elevated uncertainty, a tolerated + ADR-137 contradiction flag). Classifications are returned but flagged + degraded with the specific reason; downstream consumers must treat them as + lower-evidence. +- **UNKNOWN** — the room changed materially (empty-baseline drift beyond the + envelope, AP channel change, transceiver-geometry change, hardware/device + change, or an invalidated/absent certificate). RuView **stops returning + confident classifications** and returns UNKNOWN with the triggering cause. + This is the required behavior, not an error. + +State transitions are hysteretic (separate enter/exit thresholds) so the gate +does not flap on noise. The state, the four input values, and the triggering +cause are all reported — never a bare label. + +### 3. Certificate-bound, honest by construction + +- The gate is meaningless without a certificate: with no valid ADR-301 + certificate for the current space/device, the default state is UNKNOWN, not + KNOWN. Absence of evidence is treated as absence of capability. +- The `DomainState` and its inputs are emitted to the evidence engine + (ADR-304) as part of every inference record, and are an input to the ADR-318 + capability certificate (a model's capability is bounded by the domain it can + hold KNOWN in). +- No accuracy number is claimed here; the ADR delivers the gating machinery. + The gate's own thresholds are calibration parameters, reported with each + decision. + +## Consequences + +- RuView gains a uniform, auditable answer to "should I trust this inference?" + that combines domain, quality, calibration, and uncertainty rather than + confidence alone. +- Deployments will see more DEGRADED/UNKNOWN results than a + confidence-only system, especially right after a room changes. That increase + is the product working: it is the difference between honest RF perception and + confidently-wrong output. +- Every task head that opts into the substrate must route through the gate; + heads that bypass it cannot claim a KNOWN state or earn an ADR-318 + certificate. +- The gate couples model serving to the presence of a live calibration + certificate, making ADR-301 a hard dependency of confident inference — the + intended coupling. + +## Validation + +- `cargo test` on the OOD crate — state-machine transitions on synthetic + fixtures: in-envelope drift stays KNOWN; soft-threshold breach → DEGRADED; + empty-baseline drift beyond envelope, channel change, geometry change, + device mismatch, and invalidated/absent certificate each → UNKNOWN; + hysteresis prevents flapping under injected noise; missing certificate + defaults to UNKNOWN. +- Cross-ADR: consumes an ADR-301 certificate and asserts a drifted fingerprint + drives the expected transition; asserts the `DomainState` is present on every + emitted inference record consumed by ADR-304. +- No confident classification is emitted in the UNKNOWN state in any test — + enforced as an assertion, not a convention. +- Real-silicon OOD behavior (moving furniture / changing AP channel on a live + ESP32 capture and observing the transition) remains a follow-up requiring + hardware evidence per CLAUDE.md. diff --git a/api-docs/adr/ADR-303-ground-truth-synchronization.md b/api-docs/adr/ADR-303-ground-truth-synchronization.md new file mode 100644 index 00000000..0b0928cd --- /dev/null +++ b/api-docs/adr/ADR-303-ground-truth-synchronization.md @@ -0,0 +1,125 @@ +# ADR-303: Ground-truth synchronization — reference sensors as a formal validation plane + +- **Status**: Accepted — initial implementation (ADR-300 phase 2) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: ground-truth, validation, fusion, evidence, benchmark, honesty, substrate + +## Context + +This ADR is primitive 3 of the perception-substrate program (ADR-300), authored +as **Proposed** in phase 2: it is design intent and a validation plan, not +implemented by the phase-1 swarm. It sits on top of the phase-1 certificate +spine and feeds the evidence engine (ADR-304) and the real benchmark service +(ADR-317). It generalizes the vitals ground-truth rig (ADR-293) from a single +measurand to a modality-agnostic plane. + +RuView's evidence discipline (CLAUDE.md; ADR-282 ladder) requires MEASURED +accuracy claims to be backed by an independent reference. ADR-293 built exactly +this for vitals: reference-series ingest, time alignment (cross-correlation +lag + optional clock-drift fit), and agreement statistics (MAE/RMSE/bias/ +Bland–Altman/within-tolerance), with an `EvidenceGrade` that is only +constructible as `Measured` when a real reference, non-zero paired samples, +minimum coverage, and a reproducer are present. That machinery is measurand- and +device-shaped: it knows about heart rate and breathing rate. + +The substrate needs the same discipline for *every* phenomenon RuView senses — +presence, count, localization, pose, posture, activity — and for reference +sources of many modalities (cameras, mmWave, pressure mats, wearables, pulse +oximeters, microphones, manual labels). The critical design decision is that +these reference sensors form a **validation plane**, not additional inference +inputs. + +## Options considered + +1. **Fuse reference sensors as extra inference inputs.** Rejected on principle: + folding cameras/mmWave into the estimator would make RuView's RF claims + unfalsifiable — the reference would be training the thing it is meant to + check, and a camera-fed result is no longer a camera-free RF result. It + would also violate the ADR-282 layering (RuView is probabilistic + exteroception, never ground truth) and the honesty rule against presenting + fused-with-camera output as WiFi sensing. +2. **One-off rigs per measurand (extend ADR-293 ad hoc each time).** Rejected: + duplicates alignment/agreement code per phenomenon and never yields a shared + validation surface for the benchmark. +3. **A first-class, modality-agnostic `GroundTruth` API that is strictly a + validation plane.** Chosen. + +## Decision + +Introduce a `GroundTruth` API — a modality-agnostic validation plane that +compares RF inference against independent observation and never feeds it. + +### 1. Modality-agnostic reference ingest + +- A `ReferenceObservation` generalizing ADR-293's `ReferenceSeries`: a + timestamped, typed observation of a `Phenomenon` (presence, count, + localization, pose keypoints, posture, activity, heart rate, breathing rate) + from a `ReferenceModality` (camera, mmWave, pressure, wearable, pulse + oximeter, microphone, manual label), with device/source metadata and the + measurement principle recorded. +- Untrusted reference files are validated at the boundary (row-numbered + rejections, non-monotonic timestamps are errors), reusing ADR-293's ingest + discipline. Camera/mmWave references arrive as exported label/keypoint + streams, not live model feeds. + +### 2. Synchronization + +- Generalize ADR-293's time alignment (bounded-lag normalized cross-correlation + + optional linear clock-drift fit) to arbitrary measurands on a common + resampled grid, with no interpolation across gaps beyond a configurable + limit. Alignment parameters are always reported, never silently applied. +- Spatial synchronization where relevant: reference observations are expressed + in the ADR-306 spatial ontology so an RF localization/pose result and a + camera/mmWave observation are compared in one coordinate frame. + +### 3. Agreement as validation, not fusion + +- A modality-appropriate `AgreementReport` per phenomenon: continuous + measurands reuse ADR-293's MAE/RMSE/bias/Bland–Altman/within-tolerance; + categorical/detection phenomena (presence, activity) report confusion-matrix + metrics; spatial phenomena report localization error percentiles and pose + PCK **with the mandatory mean-pose baseline and leakage-free split** + (CLAUDE.md; ADR-291). +- Session scope is mandatory metadata (subject count, motion state, LOS/NLOS/ + through-wall, distance band) — a report without scope cannot be constructed, + as in ADR-293. + +### 4. Evidence and isolation guarantees + +- The plane is one-directional by type: the inference path has no read access + to `GroundTruth` at runtime. A build/test-time isolation check (and the type + boundary) prevents a reference observation from becoming an estimator input. +- Reports carry an `EvidenceLevel` (ADR-282) and an `EvidenceGrade` + constructible as `Measured` only with a real reference, paired samples, + coverage, and a reproducer (ADR-293 rule). Reports feed the ADR-304 evidence + engine and are the substrate ADR-317 scores against. + +## Consequences + +- Every phenomenon RuView senses gets the same MEASURED-vs-independent-observer + discipline vitals already has, in one shared surface. +- Keeping references strictly as validation preserves the falsifiability and + the camera-free identity of RF results; it costs the (tempting) accuracy a + camera-fused estimator would show, which is the correct trade. +- Reference capture is an operational burden (a camera/mmWave rig per validated + session); acceptable because it is a validation activity, not a runtime + requirement, and it is what turns CLAIMED into MEASURED. +- Because this is Proposed (phase 2), the API shape may be revised once the + phase-1 spine (ADR-301/299/301/303) lands and the benchmark (ADR-317) + exercises it. + +## Validation + +- Unit tests (planned): modality-agnostic ingest rejection cases; alignment + recovery of known synthetic offsets/drifts across measurands; agreement math + per phenomenon against hand-computed fixtures; pose PCK path requires a + mean-pose baseline and rejects leaky splits; evidence-grade constructibility; + the isolation check fails a build that wires a reference into the inference + path. +- Cross-ADR: an ADR-317 benchmark scenario consumes `GroundTruth` reports as + its scored reference; ADR-304 ingests the agreement reports as evidence + records. +- Real-session validation (RF capture synchronized with a real camera/mmWave/ + pressure/wearable reference) is the phase-2 exit and requires hardware + evidence per CLAUDE.md; a synthetic run is not hardware evidence. diff --git a/api-docs/adr/ADR-304-evidence-engine.md b/api-docs/adr/ADR-304-evidence-engine.md new file mode 100644 index 00000000..3fd9ba2f --- /dev/null +++ b/api-docs/adr/ADR-304-evidence-engine.md @@ -0,0 +1,117 @@ +# ADR-304: Evidence engine — MLflow for physical sensing + +- **Status**: Accepted — initial implementation planned (ADR-300 phase 1) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: evidence, provenance, ledger, accuracy, drift, benchmark, honesty, substrate + +## Context + +This ADR is primitive 4 of the perception-substrate program (ADR-300) and a +central pillar of the phase-1 certificate spine. It consumes the domain state +from out-of-distribution detection (ADR-302) and the calibration age from the +calibration certificate (ADR-301), it is the store that capability certificates +(ADR-318) are minted from, and it is the accuracy source the real benchmark +service (ADR-317) reads. In phase 2 it ingests agreement reports from the +ground-truth plane (ADR-303). + +The strategic assessment (ADR-300) judged this primitive **more commercially +important than another pose architecture**: what unblocks OEM and integrator +conversations is not a higher headline number but a defensible, auditable record +of how a model actually performs, per room, per device, per subject, over time. +MLflow made ML experiments trackable; physical sensing needs the equivalent for +deployed accuracy, drift, and evidence level — an append-only ledger, not a +dashboard that overwrites yesterday's number. + +RuView already has the constituent evidence types; what is missing is the ledger +that unifies them per deployment context: + +- RuField provenance/signature types (ADR-260/262/277/279) — the signed, + provenance-bearing record types to reuse rather than reinvent. +- The AetherArena witness-ledger pattern (ADR-149) — an append-only, + witness-anchored ledger of scored results, the structural template here. +- `frame::EvidenceLevel` L0–L5 (ADR-282) — the mandatory evidence tag every + record carries. +- ADR-302 `DomainState`, ADR-137 `QualityScore`, ADR-301 certificate version + and age — the per-inference signals to accumulate. + +## Options considered + +1. **Log accuracy to flat files / metrics dashboards.** Rejected: mutable, + un-signed, un-scoped, and not comparable over time — the exact gap. +2. **Reuse a general experiment tracker (MLflow itself).** Rejected: it is + experiment-time, not deployment-time; it has no notion of room/device/ + subject context, calibration age, evidence level, or signed provenance, and + it would add an external service dependency contrary to the substrate's + edge-first, dependency-light direction. +3. **A native append-only evidence ledger reusing RuField record types and the + AetherArena ledger pattern.** Chosen. + +## Decision + +Build an **evidence engine**: a per-`(room, device, subject)` append-only +accuracy ledger that every model automatically writes to. + +### 1. The evidence record + +- An `EvidenceRecord` keyed by context — space id (ADR-306), signed device id + (ADR-305), and subject id where consented and available — carrying: model + version; calibration certificate version and **age** (ADR-301); the ADR-302 + `DomainState` (KNOWN/DEGRADED/UNKNOWN) and its four inputs; the ADR-137 + quality signals; predictive uncertainty; and, when a reference is present + (ADR-303), the agreement result (accuracy, false-positive rate). Each record + carries exactly one `EvidenceLevel` (L0–L5, ADR-282). +- Records are **append-only** and signed with RuField signature types + (ADR-260/262/277/279); the ledger is anchored in the witness chain (ADR-319), + following the AetherArena witness-ledger pattern (ADR-149). No record is ever + mutated in place — a correction is a new record. + +### 2. Per-context accuracy accounting + +- The engine maintains, per `(room, device, subject)` context: measured + accuracy (only where an ADR-303 reference backs it — otherwise the record is + CLAIMED/SYNTHETIC, never MEASURED), false-positive rate, drift trajectory + (fingerprint distance over time from ADR-301), the fraction of inferences in + each domain state, calibration age distribution, and model-version history. +- Aggregation is a pure function over the append-only log at a queried time — + the ledger is the source of truth; summaries are derived, never authoritative + (mirroring CLAUDE.md's "source over summaries" rule). + +### 3. Honesty enforced in the record + +- The engine cannot upgrade an evidence level; a level is set by the record's + provenance at write time (synthetic input → L0/`Synthetic`; no reference → + CLAIMED; reference + reproducer → MEASURED), reusing the ADR-282/ADR-291/ + ADR-293 constructor discipline. A benchmark or certificate reading the ledger + gets the honest level, not an optimistic rollup. +- No benchmark numbers are invented by this ADR; it delivers the ledger and the + accounting. Empty contexts report "no evidence," which downstream (ADR-318) + must treat as no capability. + +## Consequences + +- RuView gains a single auditable answer to "how well does this model actually + work, here, on this device, for this subject, and how fresh is the + calibration?" — the artifact OEM/integrator diligence actually asks for. +- ADR-318 capability certificates become derivable (a certificate is a signed + attestation over a slice of the ledger) and ADR-317 gains a real accuracy + source per PR instead of self-reported numbers. +- The append-only, signed design has storage and key-management cost; bounded + by per-context retention policy and by reusing the existing RuField/witness + infrastructure rather than a new store. +- Some contexts will show sparse or unflattering evidence. Surfacing that is the + point; the engine must never paper over a thin context with a global average. + +## Validation + +- `cargo test` on the evidence-engine crate — append-only invariant (no + in-place mutation; corrections are new records); per-context aggregation math + against fixtures; evidence-level is set by provenance and cannot be upgraded; + signature round-trip and tamper rejection; witness anchoring; empty-context + queries return "no evidence" not a fabricated number. +- Cross-ADR: ingests ADR-302 `DomainState` and (phase 2) ADR-303 agreement + reports; an ADR-318 test mints a certificate from a ledger slice and an + ADR-317 test reads accuracy from the ledger. +- Real-deployment evidence (a populated ledger from live ESP32 captures with + ADR-303 references) is the maturity milestone and requires hardware evidence + per CLAUDE.md; a synthetic ledger is L0 by construction. diff --git a/api-docs/adr/ADR-305-authenticated-sensor-identity.md b/api-docs/adr/ADR-305-authenticated-sensor-identity.md new file mode 100644 index 00000000..7fec3e46 --- /dev/null +++ b/api-docs/adr/ADR-305-authenticated-sensor-identity.md @@ -0,0 +1,147 @@ +# ADR-305: Authenticated sensor identity — RF chain of custody + +- **Status**: Accepted — initial implementation planned (ADR-300 phase 1) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: security, identity, provenance, sensor-ingest, attestation, phase-1 + +## Context + +This ADR is a child of **ADR-300** (perception substrate program) and owns +primitive #5, *authenticated sensor identity*. In the ADR-300 dependency DAG it +is a spine root that, together with **ADR-306** (canonical spatial ontology), +feeds **ADR-301** (calibration certificate) and **ADR-319** (witness chain). + +RuView's inference outputs are only as trustworthy as the measurements that +produced them, yet today a measurement's origin is essentially assertional. The +UDP data plane accepts frames from any reachable host: **ADR-296** shipped step +one — a loopback-default bind (`--udp-bind`) and an optional source +IP/CIDR allowlist — and explicitly deferred to a follow-up ADR "per-device +provisioned keys, MAC/AEAD, device identifiers, monotonic sequence numbers, +freshness window, and replay rejection." **This ADR is that step two.** ADR-296 +correctly documented that an IP allowlist does not stop LAN spoofing; a +cryptographic device identity is what closes that gap. + +Foundations already exist in the tree and must be reused rather than rebuilt: + +- `wifi-densepose-rufield` provides `DeviceId`, `Signature`, `SignatureBlock`, + `FrameProvenance`, `ProvenanceClass`, and `SignatureVerifyError` — the type + vocabulary for a signed frame. +- `wifi-densepose-bfld` provides `CapabilityAttestation` and + `PrivacyAttestationProof` (BFLD attestation, ADR-141) — the device-side + attestation surface. +- **ADR-295** defines the source-provenance state machine and freshness + (`SpatialStateFreshness`); a monotonic sequence and freshness window slot + into that machine rather than duplicating it. + +The gap is not new primitives but an **end-to-end chain of custody**: a frame +must be traceable as `device → signed measurement → sequence → timestamp → +calibration → inference → signed event`, with every link verified at the +ingest boundary per CLAUDE.md ("validate untrusted input at every network, +hardware, and FFI boundary; default to least authority"). + +## Options considered + +1. **Stop at ADR-296 (bind + IP allowlist).** Rejected: ADR-296 itself names + this insufficient on a trusted LAN; any on-subnet host can still spoof a + device. +2. **TLS/DTLS transport authentication only.** Rejected: authenticates the + *channel*, not the *measurement*. It does not survive store-and-forward, + does not bind a sequence number into the signed object, and gives the + downstream evidence/witness layers nothing to re-verify offline. +3. **Per-device signing keys with a signed measurement envelope, monotonic + sequence, and freshness window, reusing the RuField/BFLD types.** Chosen. + +## Decision + +Introduce an **authenticated frame envelope** carried through the sensing +server, built from existing RuField/BFLD types. + +### 1. Per-device provisioned identity + +- Each radio (ESP32-S3/C6 node or adapter) is provisioned with a keypair; the + device holds the private key, the server holds the enrolled public key bound + to a `DeviceId`. Provisioning is an explicit, authorized enrollment step — a + device is untrusted until an operator enrolls its public key. Private keys are + never logged or committed (CLAUDE.md credential rule); the ESP32 side follows + `firmware/esp32-csi-node` key-handling notes. +- The enrollment record binds `DeviceId → public key → capabilities` + (via `CapabilityAttestation`, ADR-141), so a device can only assert + measurements for phenomena it is attested to sense. This is what **ADR-318** + (capability certificate) later consumes. + +### 2. Signed measurement envelope + +- A frame on the wire becomes a `SignatureBlock` over the canonical + serialization of `{DeviceId, sequence, timestamp, measurement-hash}`. The + measurement itself (CSI/CIR payload) is covered by the hash so tampering is + detectable without embedding the whole payload twice. +- Verification uses `Signature`/`SignatureVerifyError` from + `wifi-densepose-rufield`. A frame that fails signature verification is + dropped and counted, exactly as ADR-296 drops disallowed sources — an `Err` + at the boundary, never a warning that proceeds. + +### 3. Monotonic sequence + freshness (replay defense) + +- Each device maintains a strictly monotonic per-device sequence number. The + server tracks the last accepted sequence per `DeviceId`; a non-increasing + sequence is rejected as a replay. +- A freshness window bounds `timestamp` against the server clock skew budget; + stale frames are rejected. This reuses ADR-295's `SpatialStateFreshness` + rather than inventing a parallel notion of staleness, and composes with + ADR-297's stale-node handling. + +### 4. Chain of custody into the event + +- On successful verification the frame's `FrameProvenance` records the verified + `DeviceId`, sequence, and timestamp. Calibration (ADR-301) and inference + annotate their transforms, and the emitted spatial event (ADR-306 ontology) + carries a signed provenance lineage. `ProvenanceClass` still enforces the + synthetic/measured invariant from ADR-282/ADR-279 (invariant 6): a measured + chain of custody can never be aliased to synthetic and vice-versa. +- This end-to-end signed lineage is the substrate the **ADR-319** witness chain + serializes and the **ADR-318** capability certificate points at as evidence. + +### Compatibility + +- The envelope is **opt-in per deployment** and negotiated at enrollment. An + un-enrolled single-node desktop deployment keeps working unauthenticated + behind ADR-296's loopback default; a routable, multi-node, or fleet + deployment (ADR-316) requires enrolled identities. The startup security log + (ADR-296) is extended to state whether frame authentication is active. + +## Consequences + +- LAN spoofing and replay — the residual risks ADR-296 named plainly — are + closed for enrolled deployments. The measurement, not merely the channel, is + authenticated, so the guarantee survives store-and-forward into the witness + chain. +- Enrollment/key-management is now an operational responsibility (provisioning, + rotation, revocation). This is documented as a deployment step; key rotation + and revocation lists are specified here but their fleet distribution is + owned by ADR-316. +- Signature verification adds per-frame CPU cost at ingest; bounded and + measured in validation below. It is a deliberate cost for a verifiable chain + of custody. +- A schema addition to the frame contract; un-enrolled deployments are + unaffected, and the migration accessor mirrors ADR-297's approach. +- **No spoof-resistance claim is MEASURED until validated on real silicon** + (CLAUDE.md hardware rule): a passing unit/integration suite demonstrates the + logic, not the fielded device path. + +## Validation + +- Unit tests (`cargo test -p wifi-densepose-sensing-server`, + `-p wifi-densepose-rufield`): valid envelope accepted; bad signature + rejected and counted; non-monotonic sequence rejected as replay; out-of- + window timestamp rejected; un-enrolled `DeviceId` rejected; measured/synthetic + provenance aliasing rejected (ADR-279 invariant 6). +- Integration test: a captured/synthesized multi-frame stream produces a + verifiable `device → … → signed event` lineage that ADR-319 can serialize and + re-verify offline. +- Benchmark (`cargo bench`): per-frame verification cost, to bound ingest + overhead. +- **Real-silicon evidence required** before any deployment-grade + authentication claim: a captured boot/runtime log from an enrolled ESP32 node + signing frames end-to-end. A successful build or simulator run is not + hardware evidence. diff --git a/api-docs/adr/ADR-306-canonical-spatial-ontology.md b/api-docs/adr/ADR-306-canonical-spatial-ontology.md new file mode 100644 index 00000000..4ef71d6b --- /dev/null +++ b/api-docs/adr/ADR-306-canonical-spatial-ontology.md @@ -0,0 +1,142 @@ +# ADR-306: Canonical spatial ontology — one Site→…→Event model for every surface + +- **Status**: Accepted — initial implementation planned (ADR-300 phase 1) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: ontology, worldgraph, schema, mqtt, matter, rufield, phase-1 + +## Context + +This ADR is a child of **ADR-300** and owns primitive #6, *canonical spatial +ontology*. In the ADR-300 DAG it is a spine root alongside **ADR-305** +(authenticated identity) and feeds every downstream primitive that must speak +about *where* and *what*: **ADR-301** (calibration), **ADR-307** (tracking, +consumes `Track`/`Person`), **ADR-319** (witness chain), and every external +surface named in the ADR-300 consequences (MQTT, REST, WebSocket, RuField, +Matter, agents). + +RuView currently expresses "where something is" in several overlapping, +per-surface schemas: the MQTT/Home-Assistant mapper has its own node/room +shapes (**ADR-297** just introduced `NodeInference`/`RoomInference` to +disambiguate node vs. room state); the `worldgraph` crate models a spatial +graph; RuField carries `SemanticProvenance`; Matter/HomeKit has its own area +model. The same physical fact — "a person is in the kitchen" — is re-encoded +differently on each surface, and the review called for "one canonical +`NodeInference`/`RoomInference` contract" (ADR-297 consequences). Without a +single semantic model, every new surface multiplies the translation matrix and +each translation is a place where provenance and evidence level (ADR-282) can +be silently dropped. + +Substantial scaffolding already exists and must be **reused/extended, not +rebuilt**. `v2/crates/worldgraph/wifi-densepose-worldgraph` already defines: + +- `WorldNode` variants including `Room { area_id, name, bounds_enu, floor }`, + `Zone { parent_room, … }`, `Wall { rf_attenuation_db }`, and `Doorway`. +- `WorldEdge` variants including `Observes { quality, last_seen_unix_ms }`, + `LocatedIn { since_unix_ms }`, `AdjacentTo { via_doorway }`, and `Supports`. +- `WorldGraph`, `WorldGraphSnapshot`, `WorldId`, `SemanticProvenance`, + `PersonPosition`, and a HomeCore `area_id` linkage join key (ADR-127). + +The `worldgraph` crate is therefore the natural home for the canonical model. +What is missing is (a) the full `Site → Building → Floor → Space → Zone` +containment spine above `Room`, (b) first-class `Sensor`, `Object`, +`Observation`, `Track`, and `Event` node types, (c) one canonical serialization +that every surface consumes, and (d) a documented migration path from the +existing per-surface schemas. + +## Options considered + +1. **Leave each surface with its own schema; add adapters pairwise.** Rejected: + O(surfaces²) translations, and provenance/evidence loss at each hop. +2. **Invent a new top-level ontology crate.** Rejected: `worldgraph` already + models rooms, zones, walls, doorways, observation edges, and HomeCore + linkage; a parallel crate would fork the world model. +3. **Extend `worldgraph` into the canonical ontology and make every surface a + projection of it.** Chosen. + +## Decision + +Adopt **one canonical spatial ontology**, hosted in the `worldgraph` crate, +that every RuView surface reads from and writes to. + +### 1. The containment spine and entity types + +Define the full node taxonomy as an extension of the existing `WorldNode`: + +``` +Site ▸ Building ▸ Floor ▸ Space ▸ Zone + └─▸ { Sensor, Person, Object, + Observation, Track, Event } +``` + +- `Site`, `Building`, `Floor`, `Space` are new containment `WorldNode` + variants above the existing `Room` (mapped to `Space`, keeping its `area_id` + and `bounds_enu`) and `Zone`. `Wall`/`Doorway` remain as topological + elements. Containment reuses the existing `LocatedIn`/`AdjacentTo` edge + vocabulary; a new `PartOf` edge expresses the pure hierarchy + (Zone `PartOf` Space `PartOf` Floor …). +- `Sensor` is the entity **ADR-305** authenticates (`DeviceId` as its stable + identity) and **ADR-320** (HAL, phase 2) describes the hardware of. `Person`, + `Object`, `Observation`, `Track`, and `Event` are first-class nodes. + `Observes`/`LocatedIn` edges already carry quality and dwell timestamps. +- `Track` and `Person` are defined **here** as the ontology contract that + **ADR-307** (persistent tracking) produces and updates. `Observation` is what + an authenticated frame (ADR-305) becomes after calibration (ADR-301), and + `Event` is the governed output that ADR-318 certifies and ADR-319 witnesses. + +### 2. Canonical serialization + +- A single, versioned serialization (serde-based, stable field names) is the + one wire/at-rest representation. Every surface — MQTT/Home-Assistant, REST, + WebSocket, RuField observations, Matter/HomeKit, agent queries — is a + **projection** of this model, not an independent schema. `NodeInference` and + `RoomInference` (ADR-297) become projections of `Sensor→Observes` and the + `Space`-level fused inference respectively, so ADR-297's node/room separation + is preserved by construction rather than re-encoded per surface. +- Every node and edge carries `SemanticProvenance` and exactly one + `EvidenceLevel` (L0–L5, ADR-282 policy): the evidence ladder travels *with* + the fact across every projection, so no surface can silently upgrade or drop + it. + +### 3. Migration path + +- Each existing per-surface schema gets a documented, tested bidirectional + mapping to/from the canonical model, plus a migration accessor for consumers + reading the old shape (mirroring ADR-297's migration accessor). Surfaces are + cut over one at a time; a surface is "canonical" once its projection is the + only encoder it uses. Until cutover, the mapping layer is authoritative and + round-trip-tested so no fact is lost in translation. +- The `worldgraph` HomeCore `area_id` linkage (ADR-127) remains the join key + between the ontology's `Space` and external area registries. + +## Consequences + +- The translation matrix collapses from O(surfaces²) to O(surfaces): each + surface implements one projection. New surfaces (ROS 2, OpenUSD, OPC UA per + ADR-282's roadmap) plug in as additional projections. +- Provenance and evidence level are carried uniformly; a fact cannot cross a + surface boundary and lose its lineage or its L-level. +- A schema change reaching every surface; managed by the versioned + serialization and per-surface migration accessors. Single-node deployments + keep working (one `Sensor`, one `Space`). +- The ontology is a *representation*, not an inference engine: it says nothing + about *how* a `Track` or `Event` is produced — that is owned by ADR-307, + ADR-301, ADR-302, and the model layer. This ADR does not itself make any + accuracy claim to grade. +- Extending `worldgraph` grows one crate's surface rather than forking a second + world model; the geo/worldmodel sub-crates continue to build on the same node + vocabulary. + +## Validation + +- Unit tests (`cargo test -p wifi-densepose-worldgraph`): containment-spine + construction and invariants (a `Zone` is `PartOf` exactly one `Space`, a + `Space` on exactly one `Floor`, etc.); round-trip serialization of every node + and edge type; every node/edge carries exactly one `EvidenceLevel`. +- Migration tests: each per-surface schema maps to the canonical model and back + with no loss of provenance or evidence level; `NodeInference`/`RoomInference` + (ADR-297) project and re-project identically. +- Contract test: a single canonical `Event` renders correctly through the MQTT, + REST, and WebSocket projections from one source of truth. +- No accuracy numbers are claimed; this ADR delivers the shared representation + the rest of the phase-1 spine writes into. diff --git a/api-docs/adr/ADR-307-persistent-identity-tracking.md b/api-docs/adr/ADR-307-persistent-identity-tracking.md new file mode 100644 index 00000000..8aa5c75d --- /dev/null +++ b/api-docs/adr/ADR-307-persistent-identity-tracking.md @@ -0,0 +1,135 @@ +# ADR-307: Persistent identity & tracking — privacy-preserving probabilistic tracks + +- **Status**: Accepted — initial implementation (ADR-300 phase 2) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: tracking, identity, privacy, fusion, worldgraph, phase-2 + +## Context + +This ADR is a child of **ADR-300** and owns primitive #7, *persistent identity +& tracking*. In the ADR-300 DAG it is a phase-2 primitive sitting on the +phase-1 spine: it **consumes the ADR-306 ontology** (producing and updating the +`Track` and `Person` node types defined there), it relies on **ADR-305** +authenticated identity so that the observations it associates have a verified +origin, and its outputs are governed `Event`s that ADR-318/ADR-319 can certify +and witness. + +The product need is to reason about *persistent entities* — "person_7 entered +the kitchen, then the hallway, then the bedroom" — across radios, modalities, +rooms, and time. The hard constraint is that this must happen **without +establishing civil identity**. RuView is camera-free (ADR-282), and a +persistent pseudonymous track must never become, or be joinable to, a real- +world named individual. This is a privacy property to be enforced *by +construction*, not a policy footnote. + +Substantial scaffolding already exists in +`v2/crates/wifi-densepose-mat/src/tracking` and must be **reused/extended, not +rebuilt**: + +- `SurvivorTracker`, `TrackedSurvivor`, `TrackId`, `TrackerConfig`, + `TrackLifecycle`, and `TrackState` — a multi-target tracker with lifecycle + (tentative/active/lost/terminal) and a `TrackId` backed by a UUID + (`as_uuid`). +- `KalmanState` with `predict`/`update`, `position`, `velocity`, + `position_uncertainty`, and `mahalanobis_distance_sq` — the motion model and + gating distance. +- `CsiFingerprint`, `DetectionObservation`, `AssociationResult`, and the + `can_reidentify`/`matches`/`mark_rescued`/`rescue` re-identification surface — + the appearance/fingerprint channel for track continuity. + +What is missing is (a) continuity **across radios, modalities, and rooms** (the +tracker today reasons within a node/room context), (b) a **persistent** entity +that survives track loss and hand-off between spaces, and (c) an explicit +**privacy boundary** that guarantees no civil-identity binding. + +## Options considered + +1. **Per-room independent trackers, no cross-room identity.** Rejected: cannot + express "person_7 moved kitchen → hallway → bedroom"; loses the entity at + every room boundary. +2. **Global identity keyed on a strong biometric fingerprint.** Rejected: a + fingerprint strong enough to re-identify across long gaps trends toward a + civil-identity-grade biometric — exactly what the privacy constraint + forbids. +3. **Probabilistic persistent tracks with bounded, decaying pseudonymous + association, built on the existing MAT tracker.** Chosen. + +## Decision + +Extend `wifi-densepose-mat/tracking` into a **cross-domain persistent track +layer** that produces ADR-306 `Track`/`Person` nodes. + +### 1. Persistent probabilistic entity + +- A persistent entity is a pseudonymous `Person` node (ADR-306) with a stable + synthetic id (e.g. `person_7`) backed by the existing `TrackId`/UUID. It + aggregates one or more `SurvivorTracker` tracks over time and space and holds + a **probabilistic** continuity belief — association is never asserted as + certain, and every hand-off carries a confidence. +- Continuity across a track-loss gap reuses the existing re-identification + surface (`can_reidentify`, `CsiFingerprint`, `AssociationResult`), extended + with a **time- and distance-decayed** association prior so that confidence in + "same entity" falls with the size of the gap. Beyond a bounded horizon the + association is dropped and a new pseudonym is minted rather than forcing a + join — under-linking is the privacy-safe failure mode. + +### 2. Cross-radio / cross-modality / cross-room continuity + +- Association operates over the ADR-306 ontology graph: `Observes` edges from + multiple `Sensor`s and `AdjacentTo`/`Doorway` topology constrain plausible + hand-offs (a person can only move between adjacent spaces). The existing + `mahalanobis_distance_sq` gating extends to a fused observation across + modalities rather than a single node's detections. +- Fusion here is track-level association; the underlying multi-modality fusion + (radar/mmWave per ADR-063, multistatic per ADR-029, and real sensor fusion + per ADR-311) supplies the observations. This ADR depends on those for the raw + cross-modality evidence and does not re-implement sensor fusion. + +### 3. Privacy boundary (by construction) + +- **No civil-identity binding.** The persistent id is a synthetic pseudonym + with no field, edge, or join key to any name, account, phone, MAC, or other + civil identifier. The type carries no such field, so binding is impossible in + the schema, not merely discouraged. +- The `CsiFingerprint` used for re-identification is **bounded and decaying**: + it is scoped to short-horizon continuity, is not persisted as a long-term + biometric template, and expires. This keeps re-identification useful for + "same person across the hallway" while structurally unable to serve "this is + the same person who visited last month." +- Every `Track`/`Person`/`Event` produced carries `SemanticProvenance` and an + `EvidenceLevel` (ADR-282), and honors the ADR-277/ADR-280 edge governance and + ADR-141 attestation — a pseudonymous track is still governed P-class data. + Tracking accuracy is a per-domain claim to be tagged MEASURED/CLAIMED/ + SYNTHETIC with a reproducer; **this ADR claims no accuracy number.** + +## Consequences + +- RuView can express persistent, cross-room trajectories for automation and + analytics while remaining camera-free and civil-identity-free. +- The privacy-safe failure mode is **under-linking** (mint a fresh pseudonym + when unsure), which will fragment a trajectory across long gaps or sparse + coverage. This is a deliberate trade: a fragmented pseudonym is safe, a + wrong civil-identity join is not. +- Extends an existing tracker rather than forking one; single-room single-radio + deployments keep the current behavior (one entity = one track). +- Cross-modality quality depends on ADR-311/ADR-063/ADR-029 landing; until then + continuity is WiFi-primary and its limits are stated, not hidden. +- Being phase 2, this ADR is design intent; it will be revised as the ADR-306 + ontology and ADR-305 identity spine finalize. + +## Validation + +- Unit tests (`cargo test -p wifi-densepose-mat`): decayed association prior + (confidence falls with gap; drops beyond horizon → new pseudonym); + topology-constrained hand-off (no association across non-adjacent spaces); + schema check that a `Person`/`Track` carries no civil-identifier field. +- Integration test against a synthetic multi-room, multi-radio scenario: + a scripted walk kitchen → hallway → bedroom yields one persistent pseudonym + with per-hand-off confidence, and a deliberately ambiguous crossing produces + two pseudonyms rather than a false join. +- Evidence discipline: any tracking-continuity accuracy is reported only with + the ADR-291 leakage-free protocol and an evidence tag; no number is asserted + here. +- Privacy review: confirm no persisted long-term biometric template and no + civil-identity join path, as an explicit checklist item before any pilot. diff --git a/api-docs/adr/ADR-308-sensor-placement-optimizer.md b/api-docs/adr/ADR-308-sensor-placement-optimizer.md new file mode 100644 index 00000000..2062bbed --- /dev/null +++ b/api-docs/adr/ADR-308-sensor-placement-optimizer.md @@ -0,0 +1,138 @@ +# ADR-308: Sensor placement optimizer — floorplan + inventory → recommended positions + +- **Status**: Accepted — initial implementation (ADR-300 phase 3) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: placement, planning, rf-twin, coverage, worldgraph, phase-3 + +## Context + +This ADR is a child of **ADR-300** and owns primitive #8, *sensor placement +optimizer*. In the ADR-300 DAG it is a phase-3, research-forward primitive that +sits on top of the fused world state and is tightly coupled to **ADR-315** +(digital RF twin): the twin provides the propagation simulation this optimizer +plans against. It reads the **ADR-306** canonical ontology for the physical +scene and, after install, compares its predictions against ADR-302 observability +and the ADR-318 capability certificate. + +The problem it solves is the single most common cause of a bad RuView +deployment: sensors placed by guesswork. Whether a room can be reliably sensed +depends on AP/sensor geometry relative to walls, Fresnel-zone clearance, +multipath structure, and where people actually move. Today an installer has no +principled way to answer "where do I put the two nodes I have so the kitchen is +observable?" — and no way, after install, to know whether reality matched the +plan. This is a genuine **differentiator**: it turns RuView from "sense +whatever the given placement happens to allow" into "recommend the placement +that makes the requested sensing feasible." + +Relevant existing assets to build on rather than duplicate: + +- The `worldgraph` crate models the physical scene the optimizer plans over: + `Room`/`Space` with `bounds_enu`, `Wall { rf_attenuation_db }` (drywall ≈ 3 + dB, brick ≈ 12 dB), `Doorway`, and `Zone` — enough geometry and coarse RF + attenuation to seed a coverage model, plus `Sensor` nodes (ADR-306) for + candidate positions. +- **ADR-315** (RF twin, phase 3) is the propagation/multipath simulator; this + optimizer is a *consumer* of the twin, not a second simulator. +- **ADR-302** (OOD/observability) and **ADR-318** (capability certificate) + define what "reliably sense the requested phenomenon" means, so the optimizer + can optimize against the same observability metric the runtime later gates on. +- **ADR-029** (multistatic) and **ADR-063** (mmWave fusion) inform which link + geometries are useful for which phenomena. + +## Options considered + +1. **Static placement guidelines in docs (e.g. "one node per room, opposite + the door").** Rejected: ignores the specific floorplan, wall materials, and + the actual hardware inventory; gives no uncertainty and no post-install + feedback. +2. **Full electromagnetic solver per site.** Rejected for the default path: + too heavy for an installer workflow and overkill relative to the coarse + `rf_attenuation_db` scene RuView actually has; reserved as an optional + high-fidelity backend inside ADR-315. +3. **A coverage optimizer that consumes the ADR-315 RF twin over the ADR-306 + scene, then validates predicted vs. measured observability after install.** + Chosen. + +## Decision + +Define a **placement optimizer** that takes a floor plan (ADR-306 scene) and a +hardware inventory and recommends sensor positions, then closes the loop after +install. + +### 1. Inputs + +- The ADR-306 canonical scene: `Space`/`Zone` bounds, `Wall` segments with + `rf_attenuation_db`, `Doorway` topology, and any already-placed `Sensor` + nodes. +- A hardware inventory: the count and type of available radios (ESP32-S3/C6 + nodes, mmWave, adapters) with their capability envelopes (what each can + sense, per ADR-318 / ADR-320 HAL descriptors). +- A sensing objective: which phenomenon must be observable in which + `Space`/`Zone` (presence, vitals, pose), expressed against the ADR-302 + observability metric. + +### 2. Prediction + +- For a candidate placement, query the **ADR-315 RF twin** for simulated RF + coverage: path loss through `Wall` attenuation, **Fresnel-zone clearance** + between link endpoints, and coarse **multipath** structure. From that derive + an **expected observability** and an **uncertainty** for each objective in + each space — reusing the same observability definition ADR-302 gates on so the + plan and the runtime speak one language. +- Search over candidate positions (the inventory bounds the count; the scene + bounds the geometry) to recommend the placement that maximizes objective + observability, reporting expected observability **and its uncertainty** per + space — never a single confident number for a simulated result. + +### 3. Post-install loop + +- After install, compare **predicted vs. measured** observability using the + ADR-302 runtime observability signal from the freshly enrolled (ADR-305), + calibrated (ADR-301) sensors. Where measurement disagrees with prediction, + recommend adjustments (move, re-aim, add a node) and feed the residual back + to improve the ADR-315 twin's scene parameters (e.g. a wall's effective + attenuation). + +### Evidence discipline + +- Predicted coverage is a **simulation** (evidence level L0 per ADR-282) and is + labelled `SYNTHETIC`; it is a *recommendation*, never a sensing claim. +- The predicted-vs-measured comparison is the only place a `MEASURED` statement + appears, and only with a reproducer and real-silicon observability data + (CLAUDE.md hardware rule). The optimizer never presents a simulated coverage + map as evidence that a room *is* being sensed. + +## Consequences + +- Installers get a principled, floorplan-specific placement plan and, crucially, + a post-install check that says whether reality matched the plan — a + differentiating capability over guess-and-check deployment. +- Quality is bounded by the fidelity of the ADR-315 RF twin and the coarseness + of the `worldgraph` scene (2D walls, coarse attenuation). The optimizer + reports uncertainty rather than overstating a coarse model; higher fidelity + is an ADR-315 concern. +- Hard dependency on ADR-315 (twin), ADR-302 (observability metric), and + ADR-306 (scene); this ADR does not build a simulator or an observability + metric of its own. +- Being phase 3, this is design intent sitting on the fused world state; it is + expected to be revised as ADR-315 and the phase-1 spine land. +- No claim that recommended placement *guarantees* sensing — it maximizes + modelled observability subject to inventory and geometry, with explicit + uncertainty. + +## Validation + +- Unit tests: coverage/observability prediction is a deterministic function of + scene + placement + twin parameters; Fresnel-zone and wall-attenuation math + against known analytic cases; search returns the modelled-optimal placement on + small synthetic scenes. +- Integration test: on a synthetic floorplan with a known-good and a + known-bad placement, the optimizer ranks them correctly and reports higher + uncertainty for the marginal case. +- Post-install loop test: injected predicted-vs-measured disagreement produces a + sensible adjustment recommendation and a twin-parameter residual. +- Field validation (deferred, real-silicon): predicted vs. measured + observability on an instrumented real site, reported as `MEASURED` with a + reproducer. Until then all coverage output is `SYNTHETIC`/L0. No coverage or + accuracy number is asserted by this ADR. diff --git a/api-docs/adr/ADR-309-active-sensing.md b/api-docs/adr/ADR-309-active-sensing.md new file mode 100644 index 00000000..c6b6aa20 --- /dev/null +++ b/api-docs/adr/ADR-309-active-sensing.md @@ -0,0 +1,152 @@ +# ADR-309: Active sensing — closed-loop RF experiment control + +- **Status**: Accepted — initial implementation (ADR-300 phase 3) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: active-sensing, control-plane, closed-loop, information-gain, actuation, phase-3 + +## Context + +This ADR is a child of **ADR-300** and owns primitive #9, *active sensing*. In +the ADR-300 phasing it is a phase-3 primitive that sits on top of the fused +world state produced by **ADR-311** (real sensor fusion) and is driven by the +information budget of **ADR-314** (information-gain scheduler). It is authored +as **Proposed**: design intent and validation plan, not a phase-1 build. + +The default posture of every current RuView path is **passive**: RF traffic +happens for its own reasons (a device transmits, a beacon fires), RuView +observes whatever CSI/CIR arrives, and the pipeline extracts what it can from +that incidental signal. The strategic assessment behind ADR-300 named the next +step: move from *RF-happens → observe* to **RuView-controls-RF → observe the +response → optimize the next measurement**. That turns sensing into a +closed-loop experiment — the system chooses what to measure to resolve the +uncertainty it currently has, rather than accepting the measurements the +environment happens to offer. + +Substantial control-plane scaffolding already exists and must be +**reused/extended, not rebuilt**: + +- **ADR-280** (active sensing / programmable perception, *implemented* in + `ruview-unified/src/control.rs`) already defines the governed control surface + this ADR closes the loop over: `SensingTask` (evidence-aware, fail-closed + admission), `SensingAction` + `InformationGoal` (a deliberate act of + evidence-gathering against a stated hypothesis, bounded by a `PrivacyClass` + P0–P5 ceiling), `ActiveSensingPlanner` (age-of-information scheduler), + `CoherentSensorGroup` (coherent fusion fails closed), and `request_actuation` + → `ActuationReceipt` for governed RIS/movable/fluid-antenna actuation. +- ADR-280 explicitly recorded that **information-gain *estimation* is not + implemented** — "the planner uses staleness heuristics, not mutual + information; RIS drivers, actual multi-AP coherence measurement, and OTFS + waveform control are hardware-dependent roadmap items." ADR-309 is the ADR + that closes exactly those gaps, in coordination with ADR-314. + +The missing piece is not the actuation surface — ADR-280 built that and made it +fail closed — but the **loop**: a controller that reads the current fused-state +uncertainty, selects a *controllable measurement configuration* expected to +reduce it most, requests it through the ADR-280 governed surface, observes the +response, and updates its belief before choosing the next measurement. + +## Options considered + +1. **Stay passive; only schedule which incidental observations to keep.** This + is roughly today's `ActiveSensingPlanner` (staleness-priority over regions). + Rejected as the endpoint: it optimizes *attention* over uncontrolled RF, not + the *measurement* itself. It remains the fallback when nothing is + controllable. +2. **Open-loop measurement scripting** (a fixed sweep of channels/bandwidths). + Rejected: a fixed sweep spends the RF/energy/privacy budget the same way + regardless of what is already known; it cannot concentrate measurement where + uncertainty actually is. +3. **Closed-loop experiment control** — read uncertainty, pick the controllable + configuration with highest expected information gain per unit cost/privacy, + actuate through the ADR-280 governed surface, observe, update, repeat. + Chosen. + +## Decision + +Adopt **closed-loop RF experiment control** as a phase-3 controller layered on +the ADR-280 surface. RuView selects and drives the controllable degrees of +freedom of the RF measurement, then optimizes the next measurement from the +observed response. + +### 1. Controllable degrees of freedom + +Define an `ExperimentControl` vocabulary over the configuration axes RuView can +influence on hardware that exposes them (each axis is optional and +capability-gated by ADR-320's HAL, so an ESP32-only deployment simply has an +empty controllable set and degrades to the passive planner): + +- **Channel / band** and **bandwidth** (which spectrum to probe; reuses the + ADR-292 wideband subcarrier-agnostic metadata). +- **Packet timing / cadence** (when to solicit a sounding, and at what rate). +- **Antenna / chain selection** (which subset of a distributed aperture to + activate — bounded by the ADR-280 `CoherentSensorGroup` compatibility proof). +- **Beam / RIS configuration** (which rooms and people become observable — + governed exactly as ADR-280 §6 requires, via `request_actuation` and an + `ActuationReceipt`). +- **802.11bf measurement parameters** (TB/non-TB, reporting config) once + ADR-310 exposes standardized sensing as a native measurement type. + +### 2. The loop + +``` +fused-state uncertainty (ADR-311) + │ + ▼ +info-gain ranking of ExperimentControl options (ADR-314) + │ select argmax E[ΔI] / (cost, energy, privacy ceiling) + ▼ +governed request (ADR-280 admit_task / request_actuation, fail-closed) + │ + ▼ +observe response → update belief (ADR-311) → repeat +``` + +The controller never bypasses the ADR-280 admission and actuation gates: every +solicited measurement is a `SensingTask`/`SensingAction`, every environment +change is an `ActuationReceipt`, and every step composes with the ADR-277 +policy engine. Information gain is what **ADR-314** supplies (the mutual- +information estimate ADR-280 deferred); ADR-309 owns the *control loop* that +consumes that estimate and drives the hardware. + +### 3. Governance and honesty boundary + +- Actuation and solicitation stay fail-closed and privacy-ceilinged: a + closed-loop experiment cannot widen the P0–P5 ceiling of the task it serves, + and cannot steer a beam into a zone that does not grant the purpose (ADR-280 + `actuation_requires_policy_authorization`). +- Any accuracy or "traffic-reduction" claim from the closed loop is tagged + **MEASURED** only with a named reproducer over a stated scenario, **SYNTHETIC** + for simulated apertures, and **CLAIMED** otherwise. Real multi-AP coherent + measurement and RIS actuation remain **hardware-dependent** and require + real-silicon evidence (a captured runtime log) before any hardware claim, per + CLAUDE.md. No number is invented here. + +## Consequences + +- Sensing becomes an experiment: RuView spends its RF/energy/privacy budget on + the measurements that most reduce current uncertainty, instead of processing + whatever incidental traffic arrives. +- The loop is only as strong as its two dependencies: ADR-311 must expose a + usable uncertainty surface and ADR-314 must produce trustworthy information- + gain estimates. Where either is absent, the controller degrades to the + ADR-280 staleness planner rather than acting on a fabricated gain estimate. +- Controllability is hardware-bounded. On commodity ESP32 sensors the + controllable set may be limited to cadence; the full loop (bandwidth, antenna, + beam) needs NICs/RIS that expose those axes, surfaced through ADR-320. +- This ADR adds a controller; it does not re-open ADR-280's raw-export or + actuation-governance decisions, which remain authoritative and fail-closed. + +## Validation + +- Design-level acceptance (phase 3): a simulated closed loop over a synthetic + scene reduces terminal fused-state uncertainty faster than (a) the passive + ADR-280 staleness planner and (b) an open-loop fixed sweep, at equal + measurement budget — reported **SYNTHETIC**, with the scenario and seed named. +- Governance tests: every solicited measurement and actuation in the loop is + admitted through the ADR-280 fail-closed path; a loop step that would exceed + the task's privacy ceiling or steer into an ungranted zone is denied. +- Degradation test: with an empty controllable set (ESP32-only), the controller + falls back to the staleness planner with no error and no fabricated gain. +- Hardware validation of bandwidth/antenna/beam actuation is explicitly out of + scope until real silicon exposes those axes and produces a captured log. diff --git a/api-docs/adr/ADR-310-80211bf-native-architecture.md b/api-docs/adr/ADR-310-80211bf-native-architecture.md new file mode 100644 index 00000000..3b52ff05 --- /dev/null +++ b/api-docs/adr/ADR-310-80211bf-native-architecture.md @@ -0,0 +1,147 @@ +# ADR-310: 802.11bf-native architecture — standardized WLAN sensing as native measurement types + +- **Status**: Proposed (ADR-300 phase 2) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: 80211bf, wlan-sensing, standards, measurement-types, hal, phase-2 + +## Context + +This ADR is a child of **ADR-300** and owns primitive #10, *802.11bf-native +architecture*. In the ADR-300 phasing it is a phase-2 integration primitive: it +sits on the phase-1 spine (authenticated identity ADR-305, spatial ontology +ADR-306, evidence engine ADR-304) and **feeds ADR-320** (the RuView sensor HAL), +which is the clause of the acceptance test that "identifies the hardware." It is +authored as **Proposed**. + +**IEEE 802.11bf-2025 ("WLAN Sensing") was published 2025-09-26** — verified +against the IEEE SA record in `wifi-densepose-hardware` (`ieee80211bf/mod.rs` +header, "evidence grade MEASURED", ADR-152 §1.1). Standardization is complete +for sub-7 GHz and >45 GHz (DMG) bands: formal sensing measurement setup, +measurement instances, feedback/reporting, and sensing-by-proxy (SBP). This +changes RuView's strategic frame: rather than treating every WiFi measurement as +an *opportunistic* extraction from incidental traffic, RuView can be the **open +reference sensing stack around the standard** — the day commodity silicon +exposes it. + +Substantial scaffolding already exists and must be **reused/extended, not +rebuilt**. `v2/crates/wifi-densepose-hardware/src/ieee80211bf/` already models +the standardized procedure surface as forward-compatible types (ADR-152/153): + +- `types` — `SpecProfile` version gates, `SensingRole`/`TransceiverRole`, + `MeasurementSetupParams`, `SensingCapabilities` negotiation, and required + `ConsentMode` governance metadata on every setup. +- `messages` — `SensingMeasurementSetupRequest/Response`, + `SensingMeasurementInstance`, `SensingMeasurementReport`, `CsiReportPayload`, + `SbpRequest/Response`, `SensingSessionTermination`. +- `session` — a deterministic FSM (`Idle → SetupNegotiating → Active → + Terminating → Idle`) with rejection paths, single-role enforcement, and SBP + proxy mode; `table` (responder-side setup registry); `transport` (the + `SensingTransport` seam, a `SimTransport` test double, and an + `OpportunisticCsiBridge` that maps today's opportunistic CSI onto the + standardized report path). + +The module's own honesty note is authoritative and carried forward here: it is +**not a certified 802.11bf implementation**, and **no commodity silicon — ESP32 +included — implements the standard yet**; the OTA frame binding lands when a +chipset exposes it. Wideband ingest plumbing is already in place too: **ADR-292** +(FeitCSI/AX210) carries native subcarrier dimensionality end-to-end and records +the native→pipeline mapping, and noted that "truncated CIR is a natural +extension of the same plumbing." + +What is missing is architectural, not protocol scaffolding: normalized CSI is +still treated as *the* WiFi input. The standardized sensing measurements +(TB/non-TB soundings, truncated CIR / PDP reports) are modeled as protocol +messages but are **not yet first-class native measurement types** that flow +through calibration (ADR-301), fusion (ADR-311), and the ontology (ADR-306) on +equal footing with normalized CSI. + +## Options considered + +1. **Keep 802.11bf as a protocol model only; always down-convert its reports to + normalized CSI at ingest.** Rejected: truncated CIR/PDP carry range-resolved + multipath structure that flattening to a CSI matrix discards; it also wastes + the standard's native report semantics. +2. **Fork a parallel "bf pipeline" alongside the CSI pipeline.** Rejected: + duplicates calibration, fusion, ontology, and evidence plumbing, and re-opens + the O(surfaces²) translation problem ADR-306 exists to close. +3. **Promote standardized sensing measurements to native measurement types + inside the existing pipeline**, with normalized CSI as one measurement type + among several. Chosen. + +## Decision + +Adopt an **802.11bf-native architecture**: standardized WLAN sensing +measurements become **additional native measurement types**, alongside — not +replacing — normalized CSI. + +### 1. Native measurement types + +- Define the standardized reports the `ieee80211bf` module already models + (TB and non-TB soundings; truncated CIR; PDP) as first-class + `MeasurementType` variants that the pipeline carries end-to-end, each tagged + with its `SpecProfile` and band. Normalized CSI remains one such type; the + `OpportunisticCsiBridge` remains the path for silicon that only offers + incidental CSI. +- Truncated CIR/PDP reuse the **ADR-292** subcarrier-agnostic / native- + dimensionality plumbing (truncated CIR is the stated natural extension); the + native→pipeline mapping is recorded in frame metadata so downstream stages + know the true range/spectral resolution of a bf report vs. an interpolated CSI + frame. + +### 2. Ontology and governance binding + +- Each standardized measurement becomes an ADR-306 `Observation` node from an + ADR-305-authenticated `Sensor`, carrying `SemanticProvenance` and exactly one + `EvidenceLevel` (L0–L5, ADR-282). The `ieee80211bf` `ConsentMode` metadata — + required on every setup — composes with the ADR-277 policy engine, so a + standardized session is admitted under the same governance as any other + sensing task (ADR-280). +- SBP (sensing-by-proxy) sessions attribute the report to the proxying and the + sensing entities distinctly, so provenance is not laundered through the proxy. + +### 3. HAL feed (ADR-320) + +- The capability set a device advertises — which `MeasurementType`s, bands, + bandwidths, roles, and `SpecProfile` it supports — is exactly the descriptor + **ADR-320** (HAL) needs to "identify the hardware." ADR-310 defines that + capability descriptor as the projection of `SensingCapabilities`; ADR-320 + consumes it. A device that implements no bf profile advertises only the + opportunistic-CSI capability. + +## Consequences + +- RuView is positioned as the open reference stack *around* the standard: when a + chipset exposes 802.11bf, its native reports flow through calibration, fusion, + ontology, and evidence with no bespoke pipeline — the plumbing is already + tested against `SimTransport` and synthetic fixtures. +- Normalized CSI is demoted from "the WiFi input" to "one measurement type," + which is the correct framing for a multi-measurement future and prevents the + bf path from being a second-class citizen. +- **No hardware claim is made or implied.** No commodity silicon implements + 802.11bf yet; this ADR wires the *types and flow*, tested in simulation. Any + OTA/native-report accuracy claim requires real silicon evidence (a captured + log) per CLAUDE.md, and any wideband number must be tagged with the capture + hardware (ADR-292). No benchmark number is invented here. +- This ADR does not re-open ADR-152/153's decision to avoid OTA frame binding + until silicon exists; it consumes that surface and adds the pipeline + integration. + +## Validation + +- `cargo test -p wifi-densepose-hardware` — existing `ieee80211bf` FSM, + table, and transport tests continue to pass; new tests assert that a + `SensingMeasurementReport` (TB and non-TB) and a truncated-CIR/PDP report + round-trip through the pipeline as native `MeasurementType`s. +- `cargo test -p wifi-densepose-mat` — truncated CIR ingest reuses the ADR-292 + subcarrier-agnostic path and records the native→pipeline mapping; dimension/ + version validation on standardized reports mirrors the FeitCSI parser gates. +- Ontology/governance tests: each standardized measurement becomes an ADR-306 + `Observation` from an ADR-305-authenticated `Sensor` with one `EvidenceLevel`; + `ConsentMode` composes with ADR-277 admission; SBP attributes proxy vs. sensor + provenance distinctly. +- HAL contract test: the ADR-320 capability descriptor is derivable from + `SensingCapabilities`; a bf-less device advertises only opportunistic CSI. +- All measurement-type flows are simulation-tested (`SimTransport`, synthetic + fixtures); OTA binding and any hardware accuracy claim remain out of scope + until real silicon exposes the standard. diff --git a/api-docs/adr/ADR-311-real-sensor-fusion.md b/api-docs/adr/ADR-311-real-sensor-fusion.md new file mode 100644 index 00000000..a91de3f7 --- /dev/null +++ b/api-docs/adr/ADR-311-real-sensor-fusion.md @@ -0,0 +1,140 @@ +# ADR-311: Real sensor fusion — uncertainty-aware, multiple observations → one world state + +- **Status**: Accepted — initial implementation (ADR-300 phase 2) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: fusion, uncertainty, multimodal, world-state, ontology, phase-2 + +## Context + +This ADR is a child of **ADR-300** and owns primitive #11, *real sensor fusion*. +In the ADR-300 DAG it is a phase-2 integration primitive: it **consumes ADR-306** +(canonical spatial ontology) and **produces the single fused world state** that +the phase-3 primitives build on — **ADR-312** (long-term spatial memory), +**ADR-313** (counterfactual inference), and **ADR-315** (digital RF twin). It is +authored as **Proposed**. + +The defining invariant is not "support more modalities" but the *shape of the +output*: **multiple observations must resolve to one probabilistic world state, +not many feeds into a visualization.** A dashboard that shows a WiFi layer, a +mmWave layer, and a BLE layer side by side is not fusion; it pushes the +reconciliation onto the human. Real fusion produces one uncertainty-aware state +that every downstream consumer reads, with each contributing observation's +provenance and confidence still recoverable. + +Substantial scaffolding already exists and must be **reused/extended, not +rebuilt**: + +- **ADR-063** (60 GHz mmWave ↔ WiFi CSI fusion, *Proposed*) established the + first cross-modal fusion case: pairing noisy CSI-derived vitals with clinical- + grade mmWave FMCW radar (Seeed MR60BHA2 over UART, with a **live hardware + capture** logged on 2026-03-15). ADR-311 generalizes that pairwise case into + an N-modality, uncertainty-aware fusion. +- **ADR-137** (fusion-engine quality scoring, *Accepted — partial*) already + built the auditable-quality building block: it identified that the multistatic + fusers (`wifi-densepose-signal/src/ruvsense/multistatic.rs`, + `wifi-densepose-ruvector/src/viewpoint/fusion.rs`) discarded the evidence they + used, and specified a single auditable record — "this fused output is + trustworthy because X, Y, Z, but be aware of contradiction C" — with evidence + references and contradiction flags. ADR-311 reuses that record as the + provenance/quality carrier of the fused state. +- **ADR-280** `CoherentSensorGroup` (fail-closed coherent fusion) and + **ADR-306** `Observation`/`Track`/`Event` node types are the input and output + vocabulary respectively. + +What is missing is the **uncertainty-aware combiner across heterogeneous +modalities**: a fusion stage that takes authenticated observations from WiFi, +BLE, UWB, mmWave, acoustic, IMU, lidar, and cameras (only where policy permits), +each with its own uncertainty, and emits one probabilistic `WorldState` — with +per-observation contradiction flags, not a stack of independent feeds. + +## Options considered + +1. **Per-modality feeds rendered together** (today's implicit model on some + surfaces). Rejected: it is visualization, not fusion; contradictions are + never reconciled and there is no single state to reason over. +2. **Hard-switch "best modality wins"** (e.g., always prefer mmWave vitals over + CSI vitals). Rejected: throws away corroborating evidence and cannot express + *disagreement* — the very thing ADR-137's contradiction flags exist to + surface — and degrades badly when the preferred modality is absent or OOD. +3. **Uncertainty-weighted probabilistic fusion into one world state**, reusing + ADR-137's auditable quality record and ADR-280's fail-closed coherence gate. + Chosen. + +## Decision + +Adopt **uncertainty-aware multimodal fusion** whose invariant output is one +probabilistic world state. + +### 1. Inputs: authenticated, ontology-typed observations + +- Inputs are ADR-306 `Observation` nodes from **ADR-305-authenticated** sensors. + Supported modalities: WiFi (CSI / 802.11bf native reports via ADR-310), BLE, + UWB, mmWave (ADR-063), acoustic, IMU, lidar, and cameras. Cameras and any + higher privacy-class modality enter fusion **only where the ADR-277 policy + engine permits** — camera-free coverage is a RuView invariant (ADR-282), so + cameras are an opt-in, policy-gated input, never assumed present. +- Each observation carries its own uncertainty and exactly one `EvidenceLevel` + (ADR-282). An observation flagged out-of-distribution by **ADR-302** is + down-weighted or excluded per its OOD verdict rather than silently averaged in. + +### 2. Combiner: uncertainty-weighted, contradiction-aware + +- Observations are combined by their uncertainty into one probabilistic + `WorldState` over the ADR-306 entities (`Person`, `Object`, `Track`, and the + per-`Space` inference). The combiner does **not** collapse disagreement: when + modalities conflict beyond their stated uncertainty, the fused output carries + ADR-137 **contradiction flags** and the evidence references that produced + them, so a consumer can see *that* WiFi and mmWave disagree and *why*. +- Coherent multi-node fusion inherits ADR-280's fail-closed + `CoherentSensorGroup` gate: no coherent combination unless sync, phase, and + geometry compatibility are proven; otherwise the group degrades to incoherent + combination rather than producing confident nonsense. + +### 3. Output: one world state, provenance preserved + +- The output is a single `WorldState` written into the ADR-306 ontology, with + every fused value retaining recoverable per-observation provenance and the + ADR-137 quality record. This is the state ADR-312/310/312 consume; they read + one probabilistic world, not a modality stack. +- The fused state carries an aggregate uncertainty and an evidence level derived + from its inputs (never upgraded above the weakest contributing L-level for a + given claim). + +## Consequences + +- Downstream primitives (spatial memory, counterfactual, RF twin) build on one + probabilistic world state with uniform uncertainty and provenance, instead of + re-implementing reconciliation per consumer. +- Contradictions become first-class signal, not noise: ADR-137's record means a + disagreement between mmWave and CSI is surfaced and auditable, which is also + what lets ADR-302 and the evidence engine (ADR-304) reason about reliability. +- Fusion is uncertainty-honest: an OOD or low-evidence observation is + down-weighted, not averaged in as if trustworthy; a fused claim never presents + a stronger evidence level than its weakest necessary input. +- **No accuracy or "camera-grade" claim is made.** ADR-063's mmWave path has a + real-silicon capture; the multimodal combiner's accuracy is not asserted here. + Any fused-accuracy number requires a named reproducer tagged MEASURED / + SYNTHETIC / CLAIMED, and WiFi sensing is never presented as camera-grade + (CLAUDE.md, ADR-282). No number is invented. +- Cameras remain a governed, opt-in input; enabling them does not weaken the + camera-free coverage guarantee for deployments that exclude them. + +## Validation + +- `cargo test -p wifi-densepose-ruvector` / `-p wifi-densepose-signal` — the + ADR-137 quality record and contradiction flags travel with the fused output; + the ADR-280 `CoherentSensorGroup` gate still fails closed under + clock/phase/geometry violation. +- Fusion invariant test: N modality observations over one scene resolve to a + single `WorldState` node in the ADR-306 ontology (not N feeds), with + per-observation provenance recoverable and one aggregate evidence level. +- Uncertainty tests: a high-uncertainty or ADR-302-flagged-OOD observation is + down-weighted/excluded; conflicting modalities produce a contradiction flag + rather than a silently averaged value; the fused evidence level never exceeds + the weakest necessary input. +- Governance test: a camera or higher-privacy modality is admitted into fusion + only when the ADR-277 policy engine permits; otherwise it is excluded and the + fused state notes the exclusion. +- Any accuracy comparison (e.g., fused vitals vs. mmWave-only) is reported with + its evidence tag and reproducer; none is asserted in this ADR. diff --git a/api-docs/adr/ADR-312-long-term-spatial-memory.md b/api-docs/adr/ADR-312-long-term-spatial-memory.md new file mode 100644 index 00000000..4e8c15bd --- /dev/null +++ b/api-docs/adr/ADR-312-long-term-spatial-memory.md @@ -0,0 +1,146 @@ +# ADR-312: Long-term spatial memory — learn the normal physics of a location + +- **Status**: Accepted — initial implementation (ADR-300 phase 3) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: spatial-memory, ruvector, anomaly-detection, temporal, world-state, phase-3 + +## Context + +This ADR is a child of **ADR-300** and owns primitive #12, *long-term spatial +memory*. In the ADR-300 phasing it is a phase-3 primitive that sits on the fused +world state produced by **ADR-311** (real sensor fusion) and **ties to ADR-315** +(digital RF twin): spatial memory is the *learned normal* that a twin can +simulate against and that anomaly detection compares against. It is authored as +**Proposed**. + +The capability is to **learn the normal physics of a location** so anomalies +surface *without training a detector for every anomaly*. Concretely, the system +should learn statements like: "a chair is normally here"; "this bedroom is +usually occupied between these hours"; "the RF propagation of this space +changed"; "this machine's vibration signature changed"; "a new reflector +appeared." None of these is a labeled anomaly class — they are *deviations from +a learned baseline of normality*. This is the difference between supervised +anomaly detection (which needs examples of every failure) and **baseline-relative +anomaly detection** (which needs only a well-characterized normal). + +Substantial substrate already exists and must be **reused/extended, not +rebuilt**: + +- **RuVector** (`v2/crates/wifi-densepose-ruvector`) is the designated substrate + in the ADR-282 layer stack ("persistent objects, Gaussian fields, scene + graphs, temporal memory"). It already provides the vector/temporal machinery + this ADR needs — HNSW indexing (`hnsw.rs`, `hnsw_quantized.rs`), an event log + (`event_log.rs`), coverage and estimator surfaces, and the `crv`/`mat` + temporal sub-modules — so long-term spatial memory is a *consumer and + organizer* of RuVector primitives, not a new store. +- **ADR-306** supplies the entity vocabulary the memory is indexed by (`Space`, + `Object`, `Sensor`, `Track`, `Event`); **ADR-311** supplies the fused, + uncertainty-carrying `WorldState` snapshots that memory accumulates over time. +- **ADR-135** (empty-room baseline calibration) and **ADR-301** (automatic + domain calibration) already establish a *calibration-time* baseline of a + space; ADR-312 extends that from a one-shot baseline to a **continuously + learned, time-of-day-aware** model of normal. + +What is missing is the **temporal normality model**: a per-`Space` learned +distribution of fused world states over time (including periodicity — hour of +day, day of week), plus RF-propagation and modality-signature baselines, against +which a live fused state is scored for deviation. + +## Options considered + +1. **Supervised anomaly classifiers per anomaly type.** Rejected: it needs + labeled examples of every anomaly (fall, intrusion, machine fault, moved + furniture), which do not exist for most spaces and do not transfer between + rooms; it also cannot catch a *novel* anomaly it was never trained on. +2. **Single static baseline** (the ADR-135 empty-room snapshot, used forever). + Rejected as the endpoint: it cannot express *when* a space is normally + occupied, cannot track slow legitimate drift (furniture rearranged on + purpose), and flags every diurnal change as anomalous. +3. **Continuously learned, time-aware normality model on the RuVector + substrate**, scoring live fused state against learned normal. Chosen. + +## Decision + +Adopt a **long-term spatial memory** that learns each location's normal physics +on the RuVector substrate and scores live fused state against it. + +### 1. What "normal" is learned over + +Per ADR-306 `Space` (and the entities within it), accumulate the ADR-311 fused +`WorldState` over time into a learned normality model covering: + +- **Occupancy / activity periodicity** — the distribution of presence and + activity by hour-of-day and day-of-week (the "bedroom usually occupied certain + hours" case). +- **Static scene layout** — persistent `Object` positions and the expected + reflector set (the "chair normally here" / "new reflector appeared" cases), + building on the ADR-135/298 baseline. +- **RF-propagation baseline** — the space's normal multipath/propagation + signature (the "RF propagation changed" case). +- **Per-modality signatures** — e.g., a machine's normal vibration/acoustic/IMU + signature (the "vibration signature changed" case). + +Each learned baseline carries its own uncertainty and an `EvidenceLevel` +(ADR-282); a baseline learned from replay is L1, from a field pilot L4, and is +never presented above the evidence of the observations it was learned from. + +### 2. Substrate: RuVector, temporally compressed + +- The memory is stored and indexed on RuVector (HNSW for nearest-normal recall, + the event log for the temporal stream, the temporal sub-modules for + compression). Long-horizon history is temporally compressed — recent detail + retained, older history summarized — so memory cost is bounded rather than + growing linearly forever. +- The memory is *keyed by* the ADR-306 ontology, so "normal for this `Space` at + this hour" is a first-class query, and slow legitimate drift updates the + baseline (with provenance) instead of accumulating as permanent anomaly. + +### 3. Anomaly = deviation from learned normal + +- A live fused `WorldState` is scored against the applicable learned baseline + (matched by space and time context). A deviation beyond the baseline's + uncertainty is surfaced as an ADR-306 `Event` — *without* a per-anomaly + detector — carrying the baseline it deviated from, the deviation magnitude, + and its evidence level. Whether that event is actionable is a policy/consumer + decision (ADR-277), not this layer's. +- The learned normal is exactly what **ADR-315** (RF twin) can simulate against: + the twin proposes an expected state, spatial memory supplies the learned + actual-normal, and their divergence is a physically grounded anomaly signal. + +## Consequences + +- Anomaly detection generalizes: a space gets deviation detection from its own + learned normal, so a novel anomaly (never labeled anywhere) still registers as + a deviation, and the model transfers to a new room by *learning that room's* + normal rather than importing a foreign detector. +- Bounded memory: temporal compression keeps long-horizon memory finite; the + trade-off is that fine detail of old history is summarized, which is acceptable + for a normality baseline. +- Legitimate change is not a permanent false positive: slow drift updates the + baseline with provenance, distinguishing "furniture deliberately rearranged" + (baseline shifts) from "reflector appeared unexpectedly" (deviation event). +- **No accuracy claim is made.** Deviation-detection quality is not asserted + here; any detection-rate or false-positive number requires a named reproducer + tagged MEASURED / SYNTHETIC / CLAIMED, and a health/safety framing stays within + the ADR-282 bounded-claims discipline (decision support, not diagnosis). No + number is invented. +- The memory is governed: learned baselines are observations of a space, subject + to the same ADR-277 retention/privacy policy as the fused state they summarize; + no raw P0 RF is retained to build a baseline. + +## Validation + +- `cargo test -p wifi-densepose-ruvector` — the normality model builds on the + existing HNSW/event-log/temporal primitives; nearest-normal recall and + temporal-compression bounds are exercised on synthetic streams. +- Baseline/deviation tests: a synthetic scene with a known injected change (moved + `Object`, altered propagation, altered modality signature) produces a deviation + `Event` against the learned normal *without* a per-anomaly detector; an + unchanged diurnal cycle produces none (no false positive on normal periodicity). +- Drift test: a slow legitimate change updates the baseline (with provenance) + rather than emitting a persistent anomaly; an abrupt change does emit one. +- Evidence test: a learned baseline carries the evidence level of its source + observations and is never presented above it; retention honors ADR-277. +- Twin-linkage design check (with ADR-315): divergence between a twin-simulated + expected state and the learned normal is expressible as a deviation signal. diff --git a/api-docs/adr/ADR-313-counterfactual-inference.md b/api-docs/adr/ADR-313-counterfactual-inference.md new file mode 100644 index 00000000..31ee46c4 --- /dev/null +++ b/api-docs/adr/ADR-313-counterfactual-inference.md @@ -0,0 +1,142 @@ +# ADR-313: Counterfactual inference — generative spatial reasoning + +- **Status**: Accepted — initial implementation (ADR-300 phase 3) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: inference, generative, counterfactual, rf-twin, fusion, uncertainty, phase-3 + +## Context + +This ADR is a child of **ADR-300** (perception substrate program) and owns +primitive #13, *counterfactual inference*. In the ADR-300 DAG it is a phase-3, +research-forward primitive that sits on top of the fused world state: it +**consumes ADR-311** (real sensor fusion) for the current fused estimate and +**ADR-315** (digital RF twin) for the twin's expected measurement +distributions. It is design intent, authored as Proposed, and is expected to be +revised as the phase-1 spine and the phase-2 fusion layer land. + +RuView today reasons discriminatively: a task head maps measurements to a label +or a pose. That answers "what does the classifier say?" but not the questions an +operator actually asks — *would these RF measurements still make sense if nobody +were present? Does one person explain the observation better than two?* Those +are counterfactual questions, and a classifier cannot answer them because it has +no model of what a measurement *should* look like under a hypothesized world +state. A discriminative head asked about an empty room simply emits its +best-effort label; it cannot say "the observation is better explained by +absence." + +The step this ADR proposes is toward a **generative spatial model**: given a +hypothesized scene state (occupancy, count, coarse positions) and the ADR-315 +twin's propagation model for the deployment, predict the *expected* measurement +distribution, then score how well each hypothesis explains the observed +measurement. The best-explaining hypothesis — including the *nobody-present* +null hypothesis — is the answer, and the margin between hypotheses is a +first-class uncertainty signal. + +Relevant existing assets to build on rather than duplicate: + +- **ADR-311** (fusion) already produces the fused world estimate and its + covariance; the counterfactual layer scores hypotheses *relative to* that + estimate rather than re-fusing raw measurements. +- **ADR-315** (RF twin) is the generative forward model — per-deployment + geometry, radio locations, and expected measurement distributions. This ADR + is a *consumer* of the twin's forward simulator, not a second simulator. +- **ADR-302** (OOD/observability) already owns the `UNKNOWN` verdict; the + null-hypothesis ("nobody present better explains this than any occupancy + hypothesis") and the "no hypothesis explains this" case route through ADR-302, + not a parallel gate. +- `frame::EvidenceLevel` L0–L5 (ADR-282) and the ADR-304 evidence engine + account for the resulting confidence. + +## Options considered + +1. **Keep only discriminative heads.** Rejected: cannot express absence, + cannot compare "one person vs. two" as competing explanations, and gives a + confident label even when no world state explains the data. +2. **A second, independently trained generative network with its own forward + model.** Rejected for the default path: duplicates the ADR-315 twin's + propagation model, invites the two models to disagree, and multiplies the + surface that must be validated. Reserved only if the twin's analytic forward + model proves insufficient for a phenomenon. +3. **A hypothesis-scoring layer that uses the ADR-315 twin as the forward model + and the ADR-311 fused state as the hypothesis prior, routing low-margin and + null-dominant cases to the ADR-302 UNKNOWN verdict.** Chosen. + +## Decision + +Define a **counterfactual inference layer** that scores a small set of scene +hypotheses against observed measurements using the digital RF twin as the +generative forward model. + +### 1. Hypothesis set + +- Hypotheses are drawn from the ADR-311 fused state and its neighbourhood: the + current estimate, the **null hypothesis** (nobody present), and a bounded set + of nearby alternatives (±1 occupant, shifted position). The fused estimate + supplies the prior so the search stays small and grounded rather than + enumerating an open world. +- The hypothesis space is expressed over the **ADR-306** canonical ontology + (`Space`/`Zone`, occupant count, coarse position), so a counterfactual result + is a governed spatial statement, not an opaque score. + +### 2. Forward model and scoring + +- For each hypothesis, query the **ADR-315 twin** for the expected measurement + distribution given that scene state and the deployment's propagation model. + Score the observed measurement's likelihood under each hypothesis's expected + distribution. +- The answer is the maximum-likelihood hypothesis; the **margin** between the + top hypotheses (and between the top hypothesis and the null) is the + confidence signal, carried into the ADR-304 evidence engine. + +### 3. Routing to UNKNOWN + +- When the null hypothesis dominates, the layer reports *absence*, not a + low-confidence occupancy label. +- When **no** hypothesis explains the observation well (all likelihoods low, or + the winning margin below threshold), the result routes to the **ADR-302** + `UNKNOWN` verdict — the observation is outside what the twin can explain, and + the honest output is "I cannot account for this," never a forced label. + +### Evidence discipline + +- Twin-predicted distributions are a **simulation** (evidence level L0 per + ADR-282) labelled `SYNTHETIC`; a counterfactual verdict inherits the evidence + level of its weakest input and is never presented as camera-grade ground + truth (CLAUDE.md honesty rule). +- Any accuracy statement about counterfactual discrimination (e.g. "distinguishes + one occupant from two") requires the mean-pose-style baseline discipline of + CLAUDE.md, a leakage-free held-out split, and a reproducer before it may be + tagged `MEASURED`. This ADR asserts **no** such number. + +## Consequences + +- RuView gains the ability to answer absence and "which explanation is better" + questions that discriminative heads structurally cannot — a step toward + generative spatial reasoning and a differentiator for security and + facility-monitoring applications where *absence* is the valuable signal. +- Quality is bounded by the fidelity of the ADR-315 twin's forward model and the + ADR-311 fused prior; the layer reports margins and defers to ADR-302 UNKNOWN + rather than overstating a coarse model. +- Hard dependency on ADR-311 (fused state and covariance) and ADR-315 (forward + model); this ADR builds neither a fusion engine nor a propagation simulator of + its own. +- Being phase 3, this is design intent sitting on the fused world state; it is + expected to be revised as ADR-311 and ADR-315 land, and it is not implemented + by the phase-1 swarm. + +## Validation + +- Unit tests: hypothesis likelihood scoring is a deterministic function of + observed measurement + hypothesis + twin parameters; the null hypothesis wins + on a synthesized empty-room measurement; a two-occupant measurement scores the + two-occupant hypothesis above the one-occupant hypothesis on a controlled + synthetic case. +- Integration test: measurements the twin cannot explain (out-of-model + scattering) drive the layer to the ADR-302 UNKNOWN verdict rather than a + forced occupancy label; margins propagate into the ADR-304 evidence engine. +- Held-out discrimination (deferred, real-silicon): one-vs-two and + presence-vs-absence discrimination on a leakage-free held-out split with a + mean-pose baseline, reported as `MEASURED` with a reproducer. Until then all + counterfactual output is `SYNTHETIC`/L0. No discrimination accuracy number is + asserted by this ADR. diff --git a/api-docs/adr/ADR-314-information-gain-scheduler.md b/api-docs/adr/ADR-314-information-gain-scheduler.md new file mode 100644 index 00000000..92c8b312 --- /dev/null +++ b/api-docs/adr/ADR-314-information-gain-scheduler.md @@ -0,0 +1,138 @@ +# ADR-314: Information-gain scheduler — sample the most informative radios + +- **Status**: Accepted — initial implementation (ADR-300 phase 3) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: scheduling, active-sensing, information-gain, edge, energy, fusion, phase-3 + +## Context + +This ADR is a child of **ADR-300** (perception substrate program) and owns +primitive #14, *information-gain scheduler*. In the ADR-300 DAG it is a phase-3, +research-forward primitive that sits on top of the fused world state and +**pairs with ADR-309** (active sensing): ADR-309 decides *what to probe* +(waveform, sensing task); this ADR decides *which radios/modalities to spend +budget on next*. It is authored as Proposed and is not implemented by the +phase-1 swarm. + +With multiple sensors, processing every stream at full rate is wasteful: many +radios are, at any moment, contributing little to the current estimate while +consuming compute, energy, and bandwidth — the three scarce resources on the +edge nodes RuView targets (ESP32-S3/C6 and small gateways). Treating all sensors +equally is precisely the design that does not survive a real deployment of +"hundreds of sensors." + +The scheduler assigns each candidate sensor/modality a value + +``` +Value(sensor) ≈ expected uncertainty reduction / (compute + energy + bandwidth) +``` + +and spends the next sampling/processing budget on the highest-value sensors. +Expected uncertainty reduction is estimated *before* paying for the measurement, +which is why the scheduler needs a model of what each sensor is likely to tell +it — supplied by the fused state's covariance and the RF twin's forward model, +not by actually sampling. + +Relevant existing assets to build on rather than duplicate: + +- **ADR-311** (fusion) maintains the fused state and its covariance — the + current uncertainty the scheduler is trying to reduce. Expected uncertainty + reduction is computed against that covariance, not a private one. +- **ADR-315** (RF twin) provides the per-sensor forward model used to predict a + candidate measurement's expected informativeness before sampling. +- **ADR-320** (RuView sensor HAL, phase 2) exposes each radio's real + compute/energy/bandwidth cost descriptors; the denominator is read from the + HAL, not guessed per platform. +- **ADR-309** (active sensing) is the paired actuator: the scheduler ranks + sensors, ADR-309 chooses the probe on the chosen sensor. +- **ADR-302** (observability) defines the phenomenon the estimate is *for*, so + the scheduler prioritizes uncertainty reduction on the objective that matters, + not on nuisance dimensions. + +## Options considered + +1. **Round-robin / process-everything scheduling.** Rejected: burns edge + compute and energy on redundant streams and does not scale to large fleets; + the strategic and external reviews named exactly this as an edge-deployment + blocker. +2. **Static priority per sensor type (e.g. always prefer mmWave).** Rejected: + ignores that a sensor's *current* informativeness depends on the scene and + the present uncertainty — a well-placed WiFi link can dominate an occluded + mmWave node in a given moment. +3. **A value-of-information scheduler that ranks sensors by expected uncertainty + reduction per unit cost, using the ADR-311 covariance and ADR-315 forward + model, with costs from the ADR-320 HAL.** Chosen. + +## Decision + +Define an **information-gain scheduler** that allocates the next +sampling/processing budget across available radios by value of information. + +### 1. Value function + +- For each candidate sensor/modality, estimate **expected uncertainty + reduction** on the ADR-302 objective by evaluating how much a predicted + measurement (via the **ADR-315** forward model) would shrink the **ADR-311** + fused-state covariance — a value-of-information estimate made *before* paying + for the measurement. +- Divide by the sensor's **cost** — compute + energy + bandwidth — read from the + **ADR-320** HAL descriptors. The exact weighting of the three cost terms is a + deployment policy (a battery node weights energy heavily; a wired gateway + weights bandwidth), configured, not hardcoded. + +### 2. Allocation + +- Rank candidates by value and spend the budget on the top set, subject to a + configurable floor that guarantees each sensor is sampled at least + occasionally (so a sensor whose value is currently low is not starved into + permanent blindness and can be re-evaluated as the scene changes). +- The scheduler emits an allocation, not a measurement; **ADR-309** active + sensing chooses the probe/waveform on each selected sensor, and the fusion + layer (ADR-311) incorporates the result. + +### 3. Governance and honesty + +- Skipping a sensor for a cycle is a *deliberate* reduction in coverage; the + scheduler records which sensors were sampled so downstream evidence (ADR-304) + reflects the actual sensing that occurred, and observability (ADR-302) can + raise `UNKNOWN` for a zone that went under-sampled rather than reporting a + stale estimate as current. + +### Evidence discipline + +- Expected-uncertainty-reduction estimates are model predictions from the + ADR-315 twin (simulation, L0 per ADR-282, `SYNTHETIC`); a scheduling decision + is a resource choice, never a sensing claim. +- Any energy/latency/throughput improvement figure requires real-silicon + measurement with a reproducer before it is tagged `MEASURED` (CLAUDE.md + hardware rule). This ADR asserts **no** efficiency number. + +## Consequences + +- Edge deployments spend scarce compute, energy, and bandwidth where they buy + the most certainty, making "hundreds of sensors" operationally tractable — a + capability the reviews flagged as critical for edge deployment. +- Quality is bounded by the accuracy of the ADR-315 forward model (informativeness + prediction) and ADR-320 cost descriptors; a poor forward model degrades to + near-round-robin, which is safe but not optimal. The sampling floor bounds the + worst case. +- Hard dependency on ADR-311 (covariance), ADR-315 (forward model), and ADR-320 + (cost descriptors), and paired with ADR-309; this ADR builds none of those. +- Being phase 3, this is design intent sitting on the fused world state and is + expected to be revised as ADR-309, ADR-311, ADR-315, and the ADR-320 HAL land. + +## Validation + +- Unit tests: the value function is a deterministic function of covariance + + forward model + cost descriptors; a sensor predicted to reduce objective + uncertainty more per unit cost ranks above one that reduces it less; the + sampling floor guarantees eventual re-evaluation of a low-value sensor. +- Integration test: on a synthetic multi-sensor scene, the scheduler reduces + objective uncertainty faster per unit modelled cost than round-robin, and + raises ADR-302 UNKNOWN for a deliberately starved zone rather than reporting a + stale estimate. +- Field validation (deferred, real-silicon): energy/latency/throughput on an + instrumented multi-node deployment, reported as `MEASURED` with a reproducer. + Until then all informativeness and cost figures are `SYNTHETIC`/L0. No + efficiency number is asserted by this ADR. diff --git a/api-docs/adr/ADR-315-digital-rf-twin.md b/api-docs/adr/ADR-315-digital-rf-twin.md new file mode 100644 index 00000000..26da1d4c --- /dev/null +++ b/api-docs/adr/ADR-315-digital-rf-twin.md @@ -0,0 +1,159 @@ +# ADR-315: Digital RF twin — persistent per-deployment RF model + +- **Status**: Accepted — initial implementation (ADR-300 phase 3) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: rf-twin, digital-twin, propagation, calibration, spatial-memory, worldgraph, phase-3 + +## Context + +This ADR is a child of **ADR-300** (perception substrate program) and owns +primitive #15, *digital RF twin*. In the ADR-300 DAG it is a phase-3, +research-forward primitive that underpins several other phase-3 primitives: +**ADR-308** (placement optimizer) plans against the twin's propagation model, +**ADR-313** (counterfactual inference) uses it as the generative forward model, +and **ADR-314** (information-gain scheduler) uses it to predict per-sensor +informativeness. It ties directly to **ADR-301** (calibration), **ADR-308** +(placement), and **ADR-312** (long-term spatial memory). It is authored as +Proposed and is not implemented by the phase-1 swarm. + +RuView today has no persistent, per-deployment model of the RF environment. +Calibration state, observed multipath, and radio geometry exist transiently +inside a running session; when the process restarts or a change happens +overnight, there is nothing that says "this is what this room's RF looked like +yesterday." Without a persistent baseline, a physical change — furniture moved, +a wall opened, a machine relocated, an intruder present — has nothing to be a +*delta against*. It is just a different measurement, indistinguishable from +noise or drift. + +The **digital RF twin** is that persistent baseline: a per-deployment model +holding + +- **geometry and radio locations** (from the ADR-306 scene / worldgraph), +- **propagation history** and **observed multipath** structure, +- **calibration state** (from ADR-301), +- **expected measurement distributions** for each link and phenomenon. + +Once the twin exists, a physical change becomes a **measurable delta against the +twin** rather than an unexplained measurement. This is what connects RuView to +facility management (what changed in this space?), security (is there an +unexplained presence?), robotics (has the map drifted?), and industrial +monitoring (did the plant layout change?) — the applications the strategic +assessment named as the value beyond a single detector. + +Relevant existing assets to build on rather than duplicate: + +- The `worldgraph` crate already models the physical scene — `Room`/`Space` + with `bounds_enu`, `Wall { rf_attenuation_db }`, `Doorway`, `Zone`, and + `Sensor` nodes (ADR-306). The twin *annotates and persists* this scene with RF + state; it does not invent a second geometry. +- `wifi-densepose-calibration` (enrollment, bank, anchor, runtime, specialist) + holds the calibration state the twin persists; the twin references and + versions calibration records, it does not reimplement calibration. +- **ADR-312** (long-term spatial memory, phase 3) is the persistence and + temporal-history substrate; the twin is a *structured occupant* of that + memory, not a separate database. +- **ADR-305** (authenticated identity) and **ADR-295** (provenance) mean the + measurements that update the twin carry verified lineage, so a delta is + attributable rather than anonymous. + +## Options considered + +1. **No persistent RF model (status quo).** Rejected: every change looks like + noise; nothing supports "what changed since yesterday?", which is the + question the facility/security/industrial applications actually ask. +2. **A full electromagnetic digital twin (per-site ray-tracing / FDTD kept in + sync in real time).** Rejected for the default path: far heavier than the + coarse `rf_attenuation_db` scene RuView actually has and impractical on edge + hardware. A high-fidelity solver is retained as an *optional backend* the + twin can call, not the baseline. +3. **A persistent, per-deployment RF model layered over the ADR-306 scene and + ADR-312 memory: geometry + radio locations + calibration state + observed + multipath + expected measurement distributions, updated by verified + measurements, exposing changes as deltas.** Chosen. + +## Decision + +Define the **digital RF twin** as a persistent, versioned, per-deployment model +of the RF environment, layered over existing scene, calibration, and memory +assets. + +### 1. State the twin holds + +- **Geometry and radio locations** referenced from the ADR-306 / worldgraph + scene (not copied). +- **Calibration state** referenced and versioned from + `wifi-densepose-calibration` (ADR-301), so the twin knows *which* calibration + a stored distribution was captured under. +- **Observed multipath and propagation history** — a bounded temporal summary + of per-link channel structure, stored in ADR-312 spatial memory. +- **Expected measurement distributions** per link and phenomenon — the forward + model ADR-308, ADR-313, and ADR-314 consume. + +### 2. Update and delta + +- Verified measurements (ADR-305 identity, ADR-295 provenance) update the twin's + distributions online, bounded by ADR-301 calibration validity. A new + observation is compared to the twin's expected distribution; the **delta** — + and its statistical significance against the twin's own variance — is the + primary output. A change large relative to the twin's modelled variance is a + *detected physical change*, not noise. +- The twin is **versioned**: a calibration event, a deliberate geometry edit, or + an accepted physical change advances the twin version, so history is + auditable and a delta is always relative to a named baseline. + +### 3. Consumers + +- **ADR-308** queries the twin's propagation model to plan placements. +- **ADR-313** uses the twin's expected distributions as the generative forward + model for hypothesis scoring. +- **ADR-314** uses per-sensor expected informativeness from the twin. +- Facility/security/robotics/industrial integrations read the twin's change + deltas as governed ADR-306 spatial events. + +### Evidence discipline + +- The twin's expected distributions and any propagation simulation are + **simulation** (evidence level L0 per ADR-282), labelled `SYNTHETIC`. A delta + computed against them is a model-relative statement. +- A change/anomaly detection *claim* (e.g. "detects furniture-scale changes") + requires real-silicon measurement against a leakage-free protocol with a + reproducer before it is tagged `MEASURED` (CLAUDE.md hardware rule). The twin + never presents a modelled expected distribution as evidence that a physical + state *is* the case; it presents a *delta and its significance*. This ADR + asserts **no** detection-accuracy number. + +## Consequences + +- RuView gains a persistent per-deployment baseline, turning "a different + measurement" into "a measurable, attributable, versioned change" — the bridge + from a sensing runtime to facility management, security, robotics, and + industrial monitoring. +- The twin is the shared forward model for ADR-308/310/311, so those primitives + speak one propagation model rather than three inconsistent ones — a + deliberate reason to build the twin before its consumers mature. +- Quality is bounded by the coarseness of the worldgraph scene and the fidelity + of the forward model; the twin reports deltas *with significance against its + own variance* rather than asserting confident change detection on a coarse + model. The optional high-fidelity backend is where higher accuracy lives. +- Hard dependency on ADR-306 (scene), ADR-301 (calibration state), and ADR-312 + (persistence); it reuses `worldgraph` and `wifi-densepose-calibration` rather + than rebuilding geometry or calibration. +- Being phase 3, this is design intent; it is expected to be revised as the + phase-1 spine, ADR-311 fusion, and ADR-312 memory land. + +## Validation + +- Unit tests: the twin's expected distribution is a deterministic function of + scene + calibration + propagation history; delta computation and its + significance against stored variance are correct on synthetic distributions; + versioning advances on calibration/geometry/accepted-change events and history + is retained. +- Integration test: on a synthetic deployment, an injected physical change (a + wall attenuation shift) produces a significant delta against the twin while + ordinary noise does not; the delta surfaces as a governed ADR-306 event with + provenance (ADR-305/292). +- Field validation (deferred, real-silicon): change detection on an instrumented + real deployment with a controlled physical-change protocol, reported as + `MEASURED` with a reproducer. Until then all twin distributions and deltas are + `SYNTHETIC`/L0. No detection-accuracy number is asserted by this ADR. diff --git a/api-docs/adr/ADR-316-fleet-control-plane.md b/api-docs/adr/ADR-316-fleet-control-plane.md new file mode 100644 index 00000000..32ebb93b --- /dev/null +++ b/api-docs/adr/ADR-316-fleet-control-plane.md @@ -0,0 +1,156 @@ +# ADR-316: Fleet control plane — provisioning to audit trails + +- **Status**: Proposed (ADR-300 phase 2) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: fleet, operations, provisioning, firmware, updates, audit, identity, phase-2 + +## Context + +This ADR is a child of **ADR-300** (perception substrate program) and owns +primitive #16, *fleet control plane*. In the ADR-300 DAG it is a phase-2 +integration-and-operations primitive that sits on the phase-1 spine: it +**consumes ADR-305** (authenticated sensor identity) for per-device identity and +enrollment, and **ADR-318** (capability certificate) for the signed models, +calibration validity, and capability envelopes a device is allowed to run. It is +authored as Proposed and is not implemented by the phase-1 swarm. + +The external and internal reviews both named the same operational gap: RuView +has strong per-device primitives but no **release identity** and no **bill of +materials** binding a fielded sensor to the exact firmware, model, and +calibration it is running — and no plane to manage that across many devices. +Without this, a handful of nodes is fine but *hundreds* of sensors become an +operational nightmare: no coherent way to provision, roll certificates, verify +firmware compatibility, distribute signed models, track calibration lifecycle, +watch health, stage updates, roll back, diagnose remotely, enforce data +retention, or produce an audit trail. This ADR addresses that release-identity / +BOM gap directly. + +The scope is deliberately the **control plane**, not the data plane. The +authenticated measurement path is **ADR-296** (bind + allowlist) plus **ADR-305** +(signed envelope); this ADR governs the *devices and artifacts*, not the +per-frame stream. + +Relevant existing assets to build on rather than duplicate: + +- **ADR-305** already defines per-device keypairs, the `DeviceId → public key → + capabilities` enrollment record, key rotation and revocation *semantics* — and + explicitly deferred their **fleet distribution** to this ADR. The control + plane is the distribution and lifecycle layer over ADR-305 identity, not a new + identity scheme. +- **ADR-318** (capability certificate) defines the signed, expiring artifact a + device is authorized to run; the fleet plane is what *distributes, stages, and + revokes* those certificates and the signed models they point at. +- **ADR-301** (calibration) owns calibration validity/expiry; the fleet plane + tracks calibration *lifecycle* across the fleet (which nodes are due, which are + stale) rather than redefining calibration. +- **ADR-319** (witness chain) provides the append-only, re-verifiable record; + fleet audit trails are witness-chain entries, not a parallel log format. +- **ADR-320** (RuView sensor HAL, phase 2) provides hardware/firmware capability + descriptors used for firmware-compatibility checks before staging an update. +- `wifi-densepose-bfld` `CapabilityAttestation` (ADR-141) is the device-side + attestation the plane checks against declared cohort capabilities. + +## Options considered + +1. **Manual per-device operations (SSH/flash by hand).** Rejected: does not + scale past a handful of nodes, produces no release identity, no audit trail, + and no safe rollback — exactly the operational nightmare the reviews named. +2. **Adopt a generic third-party IoT device-management platform wholesale.** + Rejected as the core: generic platforms do not understand RuView's signed + capability certificate, calibration validity, or witness chain, and would + fork trust away from the phase-1 spine. A generic transport/agent *may* be a + backend, but identity, certificates, and audit remain RuView's. +3. **A RuView-native control plane layered on ADR-305 identity, ADR-318 + certificates, ADR-301 calibration lifecycle, and ADR-319 audit — covering + provisioning through rollback and retention.** Chosen. + +## Decision + +Define a **fleet control plane** that manages RuView sensors and their signed +artifacts across their lifecycle, built on the phase-1 identity/certificate +spine. + +### 1. Release identity and bill of materials + +- Each fielded device has a **BOM record** binding `DeviceId` (ADR-305) → exact + firmware version → signed model set → active capability certificate (ADR-318) + → current calibration record (ADR-301) → HAL/hardware descriptor (ADR-320). + This *is* the release identity the reviews found missing: given a device you + can state precisely what it is running and prove it is signed. + +### 2. Provisioning, certificates, firmware compatibility + +- **Provisioning** is the authorized ADR-305 enrollment step at fleet scale: + minting a keypair, registering the public key and capabilities, and issuing + the initial ADR-318 certificate. A device is untrusted until provisioned. +- **Certificate lifecycle**: issue, rotate, expire, and **revoke** ADR-318 + certificates and the ADR-305 keys behind them; revocation lists are + distributed here (the distribution ADR-305 deferred). +- **Firmware compatibility**: before staging a firmware or model, check the + target's ADR-320 HAL descriptor and ADR-141 capability attestation so an + incompatible or under-capable device is never sent an artifact it cannot + honestly run. + +### 3. Cohorts, staged updates, rollback + +- Devices group into **cohorts** (by site, hardware, capability). Updates — + signed models and firmware — roll out **staged** (canary → cohort → fleet) + with health gates between stages, and **roll back** to the previously recorded + BOM on a failed health check. Only signed artifacts are ever staged. + +### 4. Health telemetry, remote diagnostics, retention, audit + +- **Health telemetry** and **remote diagnostics** report device liveness, + calibration staleness (ADR-301), certificate expiry (ADR-318), and error + state — read-only diagnostics by default, mutations authorized explicitly. +- **Data retention** policy is enforced per cohort, and P0/CSI/person data never + leaves the edge except under the ADR-277/280 governance already in force + (CLAUDE.md: never commit or exfiltrate CSI/person data). +- Every lifecycle action — provision, rotate, revoke, stage, roll back — is + written as an **ADR-319 witness-chain** entry, giving a re-verifiable **audit + trail** rather than a mutable log. + +### Authority and least privilege + +- The control plane is default-deny (CLAUDE.md: default to least authority). + Provisioning, key rotation, revocation, staging, and rollback are each + separately authorized operations; no fleet action is implied by another. + Credentials and private keys are never logged or committed. + +## Consequences + +- Hundreds of sensors become operable: coherent release identity, signed-artifact + distribution, staged updates with rollback, and a re-verifiable audit trail — + closing the release-identity / BOM gap the reviews raised. +- The plane concentrates operational authority; that is mitigated by + default-deny, per-action authorization, signed-only artifacts, and + witness-chained audit. A compromised plane must still forge signatures the + phase-1 spine verifies. +- Hard dependency on ADR-305 (identity), ADR-318 (certificate), ADR-301 + (calibration lifecycle), ADR-319 (audit), and ADR-320 (firmware/HAL + compatibility). This ADR distributes and sequences those artifacts; it does + not redefine identity, certificates, calibration, or the witness format. +- Being phase 2, this is design intent depending on the spine; it is expected to + be revised as ADR-318, ADR-319, and ADR-320 land. +- **No fielded fleet-operation claim is MEASURED without real-silicon evidence** + (CLAUDE.md hardware rule): staged update and rollback on real nodes require a + captured runtime log. A passing simulation is not fleet evidence. + +## Validation + +- Unit tests: BOM records bind identity/firmware/model/certificate/calibration + consistently and reject inconsistent bindings; certificate issue/rotate/revoke + transitions are correct; a firmware-incompatible target is refused staging; + every lifecycle action emits a well-formed ADR-319 witness entry. +- Integration test: a synthetic cohort undergoes a canary→cohort→fleet staged + update; an injected health failure triggers rollback to the prior BOM; the + full sequence is re-verifiable from the witness chain offline; a revoked + certificate is rejected fleet-wide. +- Security test (`npm run test:security` analogue for the plane): default-deny + is enforced; unauthorized provision/rotate/revoke/stage is rejected and + counted; no credential or P0 data appears in telemetry or audit output. +- Field validation (deferred, real-silicon): a real multi-node staged update and + rollback with a captured boot/runtime log, reported as `MEASURED` with a + reproducer. Until then all fleet-operation results are simulator-level. No + fielded reliability number is asserted by this ADR. diff --git a/api-docs/adr/ADR-317-benchmark-multi-domain-scorecard.md b/api-docs/adr/ADR-317-benchmark-multi-domain-scorecard.md new file mode 100644 index 00000000..ab7e64a6 --- /dev/null +++ b/api-docs/adr/ADR-317-benchmark-multi-domain-scorecard.md @@ -0,0 +1,140 @@ +# ADR-317: Multi-domain benchmark scorecard — regressions cannot hide behind pooled accuracy + +- **Status**: Accepted — initial implementation planned (ADR-300 phase 1) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: benchmark, aetherarena, ci-gate, evidence, honesty, domain-generalization, substrate + +## Context + +This ADR is primitive 17 of the perception-substrate program (ADR-300) and the +per-PR enforcement edge of the phase-1 certificate spine. In the ADR-300 +dependency DAG it reads accuracy from the evidence engine (ADR-304), consumes +the domain state produced by out-of-distribution detection (ADR-302), scores +against calibration certificates (ADR-301), and is anchored in the witness chain +(ADR-319). It is the surface that makes the rest of the spine testable on every +change to sensing code. + +A single pooled accuracy number is the classic way a domain-generalization +regression hides. A model can raise mean PCK or mean presence accuracy while +quietly collapsing on unseen rooms, unseen devices, or stationary subjects — +exactly the conditions WiFi sensing fails in and exactly the conditions a +pooled average washes out. The strategic assessment (ADR-300) named this: what +distinguishes infrastructure from a demo is that a regression on *any* operating +domain is caught before merge, not discovered in the field. + +RuView does not need a new benchmark to do this. AetherArena is already +**v0-complete infrastructure** (ADR-149): a deterministic scoring engine +reusing `wifi-densepose-train` (`src/ruview_metrics.rs`, `src/ablation.rs`, +`src/eval.rs`, `src/proof.rs`), a `PROOF_SEED=42` determinism substrate that +SHA-256-hashes outputs against an expected hash, an append-only witness ledger, +and a live Hugging Face Space. ADR-145's ablation harness already computes +presence accuracy, localization error, FP/FN, latency percentiles, a +privacy-leakage score, and **cross-room degradation**. The board is +intentionally empty (benchmark-first). What is missing is not a scorer but a +**scorecard format** that reports per-domain rather than pooled, and a +**sensing-crate CI gate** that runs it on every PR. + +## Options considered + +1. **Keep the single pooled score / `RuViewTier`.** Rejected: it is exactly the + surface a per-domain regression hides behind; a Gold tier can coexist with a + broken unseen-room slice. +2. **Add a new benchmark repo/harness for domains.** Rejected: AetherArena's + scorer, determinism binding, and witness ledger already exist and are the + right engine; a parallel harness would fork the scoring substrate and its + anti-gaming/leakage discipline. +3. **Extend the AetherArena scorer with a per-domain scorecard and wire it as a + per-PR sensing-crate gate.** Chosen. + +## Decision + +Reuse the AetherArena scorer and witness ledger (ADR-149) and add two things: a +**multi-domain scorecard** format and a **sensing-crate PR gate** that produces +it. + +### 1. The multi-domain scorecard + +The scorecard reports each capability broken out by operating domain, never +pooled into one figure. The v0 domain axes: + +- **Presence**: `room-known`, `room-unseen`, `device-unseen`, `stationary-10m` + (a stationary subject at range — the canonical WiFi failure case). +- **Pose**: `matched`, `subject-unseen`, `room-unseen`. +- **OOD rejection**: the rate at which genuinely out-of-distribution input is + correctly returned as UNKNOWN by ADR-302 (a capability, not a failure) and + the false-UNKNOWN rate on in-distribution input. +- **Calibration drift**: fingerprint-distance trajectory against the ADR-301 + certificate over the scored window, and the fraction of inferences in each + ADR-302 `DomainState` (KNOWN / DEGRADED / UNKNOWN). + +Each cell carries exactly one `EvidenceLevel` (L0–L5, ADR-282). A slice scored +on synthetic input is L0/`Synthetic` by construction; a slice on a leakage-free +held-out real split is graded higher and only then may a per-domain number be +labelled MEASURED. Pose PCK cells additionally require the mean-pose baseline +and a leakage-free held-out split (CLAUDE.md) or they are not reported as pose +accuracy at all. + +### 2. Per-domain regression gate + +- The gate compares each scorecard cell against the merged-baseline scorecard + stored in the AetherArena witness ledger. A regression **in any single + domain** beyond its configured threshold fails the PR, even if the pooled + average improved. Improvement on `room-known` cannot buy a regression on + `room-unseen`. +- Thresholds are per-domain and per-capability; the unseen/stationary/OOD + domains carry the strictest budgets because they are the ones a pooled score + hides. The baseline is append-only and witness-anchored — a new baseline is a + new signed ledger entry, never an in-place overwrite (ADR-149 ledger pattern, + ADR-319 anchoring). + +### 3. Sensing-crate CI wiring + +- Every PR that touches a sensing crate runs the scorecard across all domains + under the ADR-011/ADR-149 determinism binding (`PROOF_SEED=42`), so the run + is reproducible and tamper-evident. The gate is added to + `.github/workflows/` as an authoritative check. +- The held-out real split remains private and is never accessible to synthetic + generation, augmentation, or calibration (ADR-149 leakage constraint, ADR-282 + rule d). Submitters/PRs provide a model, not predictions on data they hold. + +### Provenance and honesty discipline + +- No benchmark numbers are invented by this ADR. It delivers the scorecard + format, the per-domain gate, and the CI wiring; the numbers come from the + ADR-304 evidence ledger and the AetherArena scorer on real data, labelled at + the honest evidence level. Empty domains report "no evidence," which the gate + treats as no coverage — never as a pass. + +## Consequences + +- A domain-generalization regression can no longer merge behind a flattering + pooled average; the failure mode that most distinguishes fielded sensing from + a demo is caught at PR time. +- Every PR touching sensing pays a per-domain scoring cost. Bounded by reusing + the existing deterministic scorer and by tiered compute (CPU smoke vs full + score, ADR-149), but it is a deliberate cost for per-domain safety. +- The empty AetherArena board fills with honest, per-domain, evidence-labelled + results rather than a single headline tier — consistent with the + benchmark-first posture and with ADR-282's ecosystem positioning. +- Some domains will show weak or absent coverage. Surfacing that per-domain is + the point; the scorecard must never paper over a thin domain with a pooled + number. +- The program-level acceptance test (ADR-300) is encoded here as an AetherArena + scenario, closing the loop once the phase-1 spine lands. + +## Validation + +- `cargo test` on the AetherArena scorer extension — per-domain slicing math + against fixtures; per-domain regression gate fails on a single-domain + regression while pooled improves, and passes when all domains hold; empty + domains report "no evidence," not a pass; every cell carries exactly one + `EvidenceLevel`; synthetic slices are L0 by construction. +- Determinism: a scored run reproduces its SHA-256 hash under `PROOF_SEED=42` + (ADR-011/ADR-149 binding); the baseline scorecard is append-only and + witness-anchored (ADR-319), never mutated in place. +- CI: the sensing-crate gate runs on a PR touching a sensing crate and blocks a + planted single-domain regression. +- Real-data scorecards (a leakage-free held-out split with ADR-303 references) + are the maturity milestone; a synthetic scorecard is L0 and no per-domain + number is MEASURED without a reproducer per CLAUDE.md. diff --git a/api-docs/adr/ADR-318-capability-certificates.md b/api-docs/adr/ADR-318-capability-certificates.md new file mode 100644 index 00000000..62783709 --- /dev/null +++ b/api-docs/adr/ADR-318-capability-certificates.md @@ -0,0 +1,138 @@ +# ADR-318: Capability certificates — validated-for-this-environment claims + +- **Status**: Accepted — initial implementation planned (ADR-300 phase 1) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: capability, certificate, evidence, provenance, signature, honesty, substrate + +## Context + +This ADR is primitive 18 of the perception-substrate program (ADR-300) and, +per the strategic assessment, among the strongest ideas in the program: it is +where the whole certificate spine becomes a consumable contract. In the ADR-300 +dependency DAG it **consumes the evidence engine (ADR-304)** — a capability +certificate is a signed attestation minted over a slice of that ledger — the +**calibration certificate (ADR-301)** for the environment it is validated +against, and the **RuField signature types (ADR-305 / ADR-260/262/277/279)** to +sign it. It reports domain state via ADR-302 and is anchored in the witness +chain (ADR-319). + +RuView must stop making unconditional capability claims. "Supports presence" is +not a true statement — presence detection works in some rooms, on some hardware, +for some subject dynamics, and fails on a stationary subject at range in an +uncalibrated room. A capability is only ever *validated for a specific +environment*, and the honest unit of that claim is a signed, expiring +certificate, not a feature flag in a README. + +The ingredients now exist across the phase-1 spine: ADR-304 accumulates +per-`(room, device, subject)` accuracy, false-positive rate, drift, and domain +state; ADR-301 produces the signed room fingerprint the environment is keyed to; +ADR-305 provides the authenticated device identity and `CapabilityAttestation` +(BFLD, ADR-141) that bounds *what a device is even attested to sense*; ADR-282 +provides the mandatory `EvidenceLevel`. What is missing is the artifact that +binds them into a single, verifiable "validated here, until then" claim and the +consumer-side rule that refuses capabilities lacking one. + +## Options considered + +1. **Static capability flags / a `supports_presence` boolean.** Rejected: it is + the exact dishonest claim — environment-independent, unsigned, non-expiring, + and false the moment the room, device, or subject dynamics differ. +2. **Report raw ledger accuracy to consumers directly.** Rejected: the ledger + (ADR-304) is the source of truth but not a portable, signed, bounded contract; + handing consumers raw records pushes evidence-weighting and expiry logic into + every consumer and drops the single verifiable object. +3. **Mint a signed, expiring `CapabilityCertificate` over an ADR-304 ledger + slice, and make consumers refuse capabilities without a valid one.** Chosen. + +## Decision + +Introduce a signed **`CapabilityCertificate`**: a bounded attestation that a +specific capability has been validated for a specific environment, for a bounded +time. + +### 1. The certificate + +A serializable `CapabilityCertificate` binding: + +- `capability` — the phenomenon (e.g. `presence`, `pose`), which must be within + the device's ADR-305/ADR-141 `CapabilityAttestation` (a device cannot be + certified for something it is not even attested to sense). +- `room` — the ADR-306 space identifier, tied to the ADR-301 calibration + certificate version the validation was performed against. +- `hardware` — the ADR-305 authenticated `DeviceId` (and, in phase 2, the + ADR-320 HAL descriptor of the sensor). +- `model` — the model version scored. +- `calibrated_date` — the calibration certificate age at validation time. +- `moving_recall`, `stationary_recall`, `false_presence_per_24h` — the measured + operating metrics, sliced from the ADR-304 ledger for this exact context (not + a global average), each honestly labelled. These are per-capability; a pose + certificate carries pose metrics with the mean-pose baseline and a + leakage-free split (CLAUDE.md) or it is not issued. +- `valid_until` — an explicit expiry; a certificate is never open-ended. +- `evidence_level` — exactly one L0–L5 (ADR-282). A certificate minted from a + synthetic ledger slice is L0/`Synthetic`; a MEASURED metric requires an + ADR-303 reference and a reproducer. The certificate cannot upgrade the level + of the ledger it is minted from (ADR-304 honesty rule). +- `signature` — a RuField `SignatureBlock` (ADR-305 / ADR-260/262/277/279) over + the canonical serialization; an unsigned certificate is not a valid + certificate. The certificate is anchored in the witness chain (ADR-319). + +### 2. Minting + +- A certificate is minted from a slice of the ADR-304 evidence ledger for one + `(room, device, subject-class, model)` context. If the ledger reports "no + evidence" for that context, **no certificate is issued** — absence of evidence + is never a capability. Minting is a pure function over the append-only ledger + at mint time; the metrics are frozen into the signed object. +- Expiry (`valid_until`) is derived from calibration validity (ADR-301) and an + evidence-freshness policy: a certificate cannot outlive the calibration it was + validated against, and drift beyond the ADR-301 envelope invalidates both. + +### 3. Consumer refusal rule + +- Applications and surfaces **refuse to consume a capability that lacks a valid + certificate for the current environment**. "Valid" means: signature verifies, + `room`/`hardware`/`model` match the running context, `valid_until` is in the + future, and the referenced calibration certificate is itself still valid + (ADR-301 not invalidated). A failed check yields UNKNOWN via ADR-302, not a + best-effort guess. +- This makes the ADR-300 acceptance clause "quantify whether it can reliably + sense the requested phenomenon → generate a signed capability certificate" + a hard gate rather than a hope. + +## Consequences + +- RuView can no longer claim a capability it has not validated for the caller's + environment; the honest failure — "not certified here" → UNKNOWN — is + surfaced by construction rather than by discipline. +- OEM/integrator diligence gets a single verifiable artifact ("presence, + validated in *this* room, on *this* device, with *these* recall/false-alarm + numbers, until *this* date, at *this* evidence level, signed") — the strongest + commercial output of the spine. +- Certificates expire and get refused; some environments will have no + certificate and therefore no capability until validated. That refusal is the + intended honest behavior, not a regression. +- Key management and expiry policy are operational responsibilities, reusing the + ADR-305 enrollment/rotation and ADR-301 validity machinery rather than new + infrastructure; fleet distribution of certificates is owned by ADR-316. +- No capability number is invented here; every metric on a certificate is sliced + from the ADR-304 ledger at its honest evidence level. + +## Validation + +- `cargo test` on the certificate crate — mint from a ledger slice produces the + frozen metrics; "no evidence" context yields no certificate; signature + round-trip and tamper rejection; `valid_until` and calibration-linked expiry + enforced; consumer refusal on room/hardware/model mismatch, expiry, or + invalidated calibration resolves to UNKNOWN (ADR-302), not a guess; evidence + level is inherited from the ledger and cannot be upgraded; a certificate + cannot be issued for a capability outside the device's ADR-305/ADR-141 + attestation. +- Cross-ADR: an ADR-304 ledger fixture mints a certificate; an ADR-302 test + asserts an expired/mismatched certificate gates to UNKNOWN; the ADR-300 + acceptance test consumes a minted certificate end-to-end. +- Real-deployment certificates (minted from a populated ledger with ADR-303 + references on live ESP32 captures) are the maturity milestone and require + hardware evidence per CLAUDE.md; a certificate minted from a synthetic ledger + is L0 by construction. diff --git a/api-docs/adr/ADR-319-witness-chain.md b/api-docs/adr/ADR-319-witness-chain.md new file mode 100644 index 00000000..cff5bbc3 --- /dev/null +++ b/api-docs/adr/ADR-319-witness-chain.md @@ -0,0 +1,146 @@ +# ADR-319: Witness chain — epistemic infrastructure for physical AI + +- **Status**: Accepted — initial implementation planned (ADR-300 phase 1) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: provenance, witness, evidence, signature, epistemics, ontology, substrate + +## Context + +This ADR is primitive 19 of the perception-substrate program (ADR-300) and a +spine root of its phase-1 certificate stack. In the ADR-300 dependency DAG it +**extends the source-provenance state machine (ADR-295)** and the RuField +provenance types, **ties to the signature machinery (ADR-305 / +ADR-260/262/277/279)**, and anchors the artifacts produced by ADR-301 +(calibration certificates), ADR-304 (evidence records), ADR-317 (benchmark +scorecards), and ADR-318 (capability certificates). In phase 2 it carries the +independent-corroboration link from ADR-303. + +The strategic assessment (ADR-300) framed RuView's real product as **epistemic +infrastructure for physical AI**: the value is not the claim "a person is +present" but the *auditable reasoning* behind it. A bare boolean output discards +everything a downstream system needs to trust or contest it — which radio +observed it, what DSP evidence supported it, which model inferred it, whether an +independent sensor agreed, what spatial state it updated, and what policy acted +on it. Once the answer is a boolean, "why do you believe that?" has no answer. + +RuView already has the pieces of a chain but not the chain itself. ADR-295 +defines a canonical `SourceState` (`Synthetic` / `LiveVerified` / +`LiveUnverified` / `Stale` / `Disconnected`) with `Unknown` structurally +forbidden from collapsing to live. ADR-305 defines the signed +`device → measurement → sequence → timestamp → … → signed event` chain of +custody. RuField carries `FrameProvenance`, `SemanticProvenance`, and signature +types; the AetherArena witness ledger (ADR-149) demonstrates an append-only, +witness-anchored ledger. What is missing is a single **staged, signed envelope** +that travels the whole pipeline and records, at each stage, the confidence and +provenance of that stage. + +## Options considered + +1. **Keep provenance as scattered per-stage fields (status quo).** Rejected: + `FrameProvenance`, `SourceState`, calibration state, and model uncertainty + live in different structures and are re-encoded per surface; there is no + single object a consumer can re-verify offline to answer "why." +2. **Log a free-form audit trail alongside the output.** Rejected: mutable, + unsigned, and not structurally tied to the output — the classic + dashboard-that-overwrites-yesterday failure the evidence engine (ADR-304) + already rejects. +3. **A staged, signed witness envelope carried through the pipeline, each stage + appended and signed, anchored in an append-only ledger.** Chosen. + +## Decision + +Define the **witness chain**: a staged, append-only, signed envelope that +accompanies an observation from radio to policy decision. Instead of emitting +"person present," RuView emits a chain whose stages are: + +``` +RF observation ▸ DSP evidence ▸ model inference ▸ independent corroboration + ▸ spatial state ▸ policy decision +``` + +### 1. The staged envelope + +- Each stage is a signed record carrying its **confidence** and its + **provenance**: + - **RF observation** — the ADR-305 authenticated frame envelope + (`DeviceId`, sequence, timestamp, measurement hash) and its ADR-295 + `SourceState`. This is the root link; a `Synthetic` root can never present + as a `LiveVerified` one (ADR-295 invariant). + - **DSP evidence** — the deterministic signal features and the ADR-137 + quality signals that support (or fail to support) an inference. + - **model inference** — the model version, its raw output, and its predictive + uncertainty; the ADR-302 `DomainState` (KNOWN / DEGRADED / UNKNOWN) gate + result, so a low-confidence or out-of-distribution inference is recorded as + such, not silently promoted. + - **independent corroboration** — the phase-2 ADR-303 agreement link + (a reference/second modality that agreed or disagreed); absent in phase 1, + the stage records "no corroboration," never a fabricated one. + - **spatial state** — the ADR-306 ontology `Observation`/`Track`/`Event` the + inference updated, carrying `SemanticProvenance` and its `EvidenceLevel`. + - **policy decision** — the governed action taken (or withheld), with the + certificate (ADR-318) it relied on. +- Each stage carries exactly one `EvidenceLevel` (L0–L5, ADR-282); the envelope's + effective level is the **minimum** across its stages — a synthetic root or an + unreferenced inference caps the whole chain, so the chain cannot claim more + than its weakest link. + +### 2. Signing and anchoring + +- Each stage is signed with RuField signature types (ADR-305 / + ADR-260/262/277/279) over the canonical serialization of that stage plus the + hash of the prior stage, so the chain is tamper-evident end to end and any + broken link is detectable. The completed chain is anchored in an append-only, + witness-anchored ledger following the AetherArena pattern (ADR-149); it is the + same anchoring ADR-301/ADR-304/ADR-317/ADR-318 write into. +- The chain is **append-only**: a correction is a new chain referencing the + prior one, never an in-place edit (mirroring ADR-304 and CLAUDE.md's "source + over summaries"). + +### 3. Offline re-verification + +- A consumer with the enrolled public keys (ADR-305) can re-verify a chain + offline: check each stage signature, check each prior-stage hash, and read the + per-stage confidence and evidence level — answering "why do you believe this?" + without trusting the emitting host. This is the property store-and-forward + channel authentication (rejected in ADR-305) cannot provide. + +### Provenance and honesty discipline + +- The witness chain never manufactures confidence: a stage that lacks evidence + records the absence. A `Synthetic` root, a missing corroboration, or an + UNKNOWN gate is carried faithfully and caps the chain's evidence level. No + accuracy number is invented here; the chain records the numbers the other + primitives produce at their honest level. + +## Consequences + +- Every RuView output becomes contestable and auditable: a downstream physical-AI + system can inspect the reasoning, weight it by per-stage confidence, and reject + a chain whose weakest link is too weak — the defining property of epistemic + infrastructure the strategic assessment asked for. +- The certificate spine (ADR-301/301/314/315) gains a single anchoring substrate; + each of those artifacts is a specialization of a witness record rather than a + bespoke signed blob. +- Carrying and signing a staged envelope adds per-observation size and CPU cost; + bounded by reusing RuField signatures and the existing ledger, and by the + minimum-level rule keeping the object honest rather than exhaustive. +- The chain will frequently reveal weak links (synthetic root, no corroboration, + DEGRADED gate). Surfacing that is the point; the envelope must never smooth a + weak stage into a confident summary. + +## Validation + +- `cargo test` on the witness-chain crate — stage-by-stage signature round-trip + and tamper rejection (a mutated stage or a broken prior-stage hash fails + verification); effective evidence level equals the minimum across stages; a + `Synthetic` root caps the chain and cannot present as `LiveVerified` + (ADR-295 invariant); an UNKNOWN gate (ADR-302) and a "no corroboration" stage + are recorded faithfully; append-only correction produces a new chain + referencing the prior one. +- Cross-ADR: an ADR-305 signed frame lineage serializes into a chain that + re-verifies offline with only the enrolled public keys; ADR-301/301/314/315 + artifacts anchor into the same ledger. +- Real-deployment chains (from live ESP32 captures with ADR-303 corroboration) + are the maturity milestone and require hardware evidence per CLAUDE.md; a + chain rooted in synthetic input is L0 by construction. diff --git a/api-docs/adr/ADR-320-sensor-hal.md b/api-docs/adr/ADR-320-sensor-hal.md new file mode 100644 index 00000000..ae3ea153 --- /dev/null +++ b/api-docs/adr/ADR-320-sensor-hal.md @@ -0,0 +1,150 @@ +# ADR-320: RuView sensor HAL — abstract all sensing hardware to one Observation type + +- **Status**: Accepted — initial implementation (ADR-300 phase 2) +- **Date**: 2026-08-11 +- **Deciders**: ruv +- **Tags**: hal, sensor-abstraction, ontology, fusion, adapters, category, phase-2 + +## Context + +This ADR is primitive 20 of the perception-substrate program (ADR-300) and a +phase-2 integration primitive; it is authored as **Proposed**. In the ADR-300 +DAG it **consumes the canonical spatial ontology (ADR-306)** — its output is an +ontology `Observation` bound to a `Sensor` entity — and **feeds real sensor +fusion (ADR-311)**, which resolves many observations into one world state. It +closes the "identify the hardware" clause of the ADR-300 acceptance test that +phase 1 leaves open. + +RuView's strategic ceiling is set by how tightly it is coupled to WiFi CSI. +Every new modality today lands as a bespoke ingest path with its own frame +shape, its own provenance handling, and its own place in the pipeline. That is +the difference between "a WiFi-DensePose project" and "an open +spatial-intelligence operating layer": the category changes the moment *any* +sensing hardware — {CSI, 802.11bf, BLE, UWB, mmWave, acoustic, camera, lidar, +IMU, custom} — enters through one abstraction and becomes one `Observation` +feeding one world model. + +Crucially this is a *unification*, not a green field. Adapters already exist and +must be reused, not rebuilt: + +- ADR-279's native RF frame contract (`RfFrameV2`) already unifies ESP32, + Intel, Atheros, PicoScenes, Realtek radar, and 320 MHz 802.11bk producers as + `RfFrameV2` producers into a shared latent — "lightweight per-device adapters + into a shared latent, not a shared tensor." The HAL generalizes that lesson + beyond RF. +- Existing CSI adapters (ESP32/Nexmon/FeitCSI paths), the mmWave fusion path + (ADR-063), and the multistatic WiFi path (ADR-029) are concrete producers to + bring under one trait. +- ADR-305 already authenticates a `Sensor`/`DeviceId`; ADR-306 already defines + `Sensor`, `Observation`, `Track`, and `Event` as first-class node types. The + HAL is the trait that turns a heterogeneous device into that authenticated + `Sensor` emitting those `Observation`s. + +The gap is a single **`SensorHal` trait and one `Observation` type** that every +modality implements, so the world model never sees a modality-specific frame — +only a provenance-bearing, evidence-labelled `Observation`. + +## Options considered + +1. **Continue adding per-modality ingest paths.** Rejected: O(modalities) bespoke + pipelines, each re-encoding provenance and evidence, each a place the ladder + can be dropped — and it keeps RuView categorically a WiFi project. +2. **Force every modality into the ADR-274/279 RF tensor/frame.** Rejected: the + ADR-279 lesson is precisely that premature canonicalization discards + information (bandwidth, antenna structure, phase). A camera, lidar, or IMU + has no meaningful `RfFrameV2` projection; forcing one is the same mistake at a + larger scale. +3. **Define a `SensorHal` trait producing one `Observation` type, with existing + adapters as implementations feeding a shared latent and the ADR-306 + ontology.** Chosen. + +## Decision + +Introduce a **`SensorHal` trait** and a single **`Observation`** type. Every +sensing modality is an implementation of the trait; the world model consumes +only `Observation`s. + +### 1. The `SensorHal` trait + +- A `SensorHal` describes a device's **capabilities** (which phenomena it can + sense — reusing the ADR-305/ADR-141 `CapabilityAttestation`), its **native + frame** (kept native, not canonicalized, per the ADR-279 shared-latent + lesson), and a method that lifts a native frame into an `Observation`. +- Implementations wrap the existing producers: CSI (ESP32/Nexmon/FeitCSI via the + ADR-279 `RfFrameV2` path), 802.11bf (ADR-310, phase 2), BLE, UWB, mmWave + (ADR-063), acoustic, camera, lidar, IMU, and `custom`. RF modalities reuse the + ADR-279 per-device latent adapters wholesale; the HAL adds the non-RF and + ranging modalities under the same trait. +- The trait is the boundary where untrusted hardware input is validated + (CLAUDE.md: validate at every hardware/FFI boundary; default to least + authority). A device is authenticated as an ADR-305 `Sensor` before its + observations are trusted. + +### 2. The `Observation` type + +- One provenance-bearing `Observation`: a measurement plus its `SensorHal` + source descriptor, its ADR-305 authenticated `DeviceId`, its ADR-295 + `SourceState`, its native-frame reference (not a lossy projection), and + exactly one `EvidenceLevel` (L0–L5, ADR-282). A camera-derived `Observation` + and a CSI-derived `Observation` are the same type with different provenance — + and a camera observation never lifts WiFi output to camera-grade; each carries + its own honest evidence level (CLAUDE.md: never present WiFi sensing as + camera-grade). +- The `Observation` maps directly onto the ADR-306 ontology `Observation` node + attached to its `Sensor`, so the ontology is the one representation and the + HAL is its ingest funnel. + +### 3. Feeding fusion + +- Observations from any set of modalities flow into ADR-311 fusion, which + resolves them into one probabilistic world state. The HAL guarantees fusion + never sees a modality-specific frame — only `Observation`s with uniform + provenance and evidence — which is what makes ADR-311's "many observations → + one world state" invariant implementable across heterogeneous hardware. + +### Category and honesty discipline + +- This ADR changes RuView's category from a WiFi-DensePose pipeline to an open + spatial-intelligence operating layer, but it makes **no accuracy claim**: the + HAL delivers a uniform ingest boundary, not a detector. Any capability of a + newly-connected sensor is still gated by ADR-302 and certified by ADR-318 for + its specific environment — connecting a camera does not grant a validated + capability by itself. +- Hardware support for a given modality is CLAIMED until demonstrated on real + silicon with captured evidence per CLAUDE.md; a passing trait test proves the + abstraction, not a fielded device. + +## Consequences + +- New sensing hardware lands as one `SensorHal` implementation instead of a + bespoke pipeline; the translation matrix stays O(modalities), mirroring how + ADR-306 collapsed the surface matrix. +- The ADR-300 acceptance clause "identify the hardware" becomes implementable: + a new sensor type is described by its HAL, authenticated as an ADR-305 + `Sensor`, calibrated (ADR-301), gated (ADR-302), and certified (ADR-318) + through the same phase-1 spine, closing the last open clause. +- A trait boundary and an `Observation` type are added; existing RF adapters + are re-expressed as implementations rather than rewritten, preserving the + ADR-279 native-frame/shared-latent design. +- Non-RF modalities (camera, lidar, acoustic) enter the governed plane with the + same provenance and privacy discipline as RF; a camera is not a privacy-free + shortcut — it inherits the ADR-277 governance and its own evidence level. +- As a phase-2 Proposed ADR, the trait shape may be revised as ADR-311 fusion + and ADR-310 802.11bf land; that revision is expected for a phased program. + +## Validation + +- `cargo test` on the HAL crate (design-time, Proposed) — a fixture `SensorHal` + for each of at least two modalities (CSI via ADR-279, plus one non-RF) + produces uniform `Observation`s; every `Observation` carries a `DeviceId`, + `SourceState`, native-frame reference, and exactly one `EvidenceLevel`; a + synthetic source yields L0/`Synthetic` and cannot alias to measured + (ADR-279 invariant 6); an unauthenticated device's observations are rejected + at the trait boundary (ADR-305). +- Cross-ADR: an `Observation` maps round-trip to an ADR-306 ontology + `Observation` node with no provenance loss, and a set of `Observation`s from + distinct modalities is accepted by an ADR-311 fusion fixture. +- Real-silicon evidence is required before any modality's hardware support is + claimed beyond CLAIMED: a captured boot/runtime log from the real device + emitting `Observation`s. A successful build or simulator run is not hardware + evidence (CLAUDE.md). diff --git a/api-docs/adr/ADR-321-decision-policy-action-authorization.md b/api-docs/adr/ADR-321-decision-policy-action-authorization.md new file mode 100644 index 00000000..8b707723 --- /dev/null +++ b/api-docs/adr/ADR-321-decision-policy-action-authorization.md @@ -0,0 +1,101 @@ +# 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`. diff --git a/api-docs/adr/README.md b/api-docs/adr/README.md index 284888ee..30b240f6 100644 --- a/api-docs/adr/README.md +++ b/api-docs/adr/README.md @@ -148,6 +148,37 @@ Statuses: **Proposed** (under discussion), **Accepted** (approved and/or impleme | [ADR-288](ADR-288-veil-privacy-shield-compliant-waveform.md) | VEIL — compliant-waveform privacy shield against unauthorized WiFi sensing (`wifi-densepose-privshield`) | Proposed (implemented, P1 reference) | | [ADR-289](ADR-289-wifi-densepose-privshield-harness-via-metaharness.md) | `wifi-densepose-privshield-harness` — npm MetaHarness for the VEIL crate (guidance/router/flywheel) | Proposed (implemented, P1) | | [ADR-290](ADR-290-veil-e2e-hardware-implementation-program.md) | VEIL end-to-end hardware implementation program — portable C core + multi-provider firmware scaffolds (openwifi/openwrt/nexmon/esp32) | Proposed (P4 scaffolding; C core host-validated) | +| [ADR-291](ADR-291-public-benchmark-evaluation-harness.md) | Public-benchmark evaluation harness — Widar3.0 ingest, split protocols, leakage guards | Accepted (initial implementation) | +| [ADR-292](ADR-292-wideband-80211ax-csi-ingest.md) | Wideband 802.11ax CSI ingest — FeitCSI/AX210 adapter, subcarrier-agnostic plumbing | Accepted (initial implementation) | +| [ADR-293](ADR-293-vitals-ground-truth-rig.md) | Vitals ground-truth rig — reference ingest, alignment, agreement metrics | Accepted (initial implementation) | +| [ADR-294](ADR-294-wifi-veil-integration.md) | WiFi Veil integration — emission-shaping countermeasure as advisory BFLD dependency | Accepted (initial implementation) | +| [ADR-295](ADR-295-source-provenance-state-machine.md) | Source provenance state machine — synthetic can never present as live | Accepted (initial implementation) | +| [ADR-296](ADR-296-sensor-data-plane-bind-hardening.md) | Sensor data-plane hardening — UDP bind control and source allowlist (step one) | Accepted (initial implementation) | +| [ADR-297](ADR-297-multi-node-semantic-correctness.md) | Multi-node semantic correctness — per-node inference, node-keyed rate limiting, stale state | Accepted (initial implementation) | +| [ADR-298](ADR-298-model-release-sanity-gates.md) | Model release sanity gates — block degenerate and mislabeled model artifacts | Accepted (initial implementation) | +| [ADR-299](ADR-299-csi-data-incident-repo-controls.md) | Repository CSI data-incident controls — ignore rules and pre-commit/CI policy check | Accepted (controls implemented; tree remediation gated) | +| [ADR-300](ADR-300-perception-substrate-program.md) | RuView perception substrate — phased 21-primitive program (calibration, evidence, trust, deployment) | Accepted (program; children ADR-301..317) | +| [ADR-301](ADR-301-automatic-domain-calibration.md) | Automatic domain calibration — signed, versioned, invalidatable room fingerprint | Accepted (phase 1) | +| [ADR-302](ADR-302-out-of-distribution-detection.md) | Out-of-distribution detection — KNOWN / DEGRADED / UNKNOWN gating | Accepted (phase 1) | +| [ADR-303](ADR-303-ground-truth-synchronization.md) | Ground-truth synchronization — reference sensors as a formal validation plane | Proposed (phase 2) | +| [ADR-304](ADR-304-evidence-engine.md) | Evidence engine — per-(room,device,subject) accuracy ledger | Accepted (phase 1) | +| [ADR-305](ADR-305-authenticated-sensor-identity.md) | Authenticated sensor identity — RF chain of custody | Accepted (phase 1) | +| [ADR-306](ADR-306-canonical-spatial-ontology.md) | Canonical spatial ontology — one Site→…→Event model for every surface | Accepted (phase 1) | +| [ADR-307](ADR-307-persistent-identity-tracking.md) | Persistent identity & tracking — privacy-preserving probabilistic tracks | Proposed (phase 2) | +| [ADR-308](ADR-308-sensor-placement-optimizer.md) | Sensor placement optimizer — floorplan + inventory → recommended positions | Proposed (phase 3) | +| [ADR-309](ADR-309-active-sensing.md) | Active sensing — closed-loop RF experiment control | Proposed (phase 3) | +| [ADR-310](ADR-310-80211bf-native-architecture.md) | 802.11bf-native architecture — standardized WLAN sensing as native measurement types | Proposed (phase 2) | +| [ADR-311](ADR-311-real-sensor-fusion.md) | Real sensor fusion — uncertainty-aware, multiple observations → one world state | Proposed (phase 2) | +| [ADR-312](ADR-312-long-term-spatial-memory.md) | Long-term spatial memory — learn the normal physics of a location | Proposed (phase 3) | +| [ADR-313](ADR-313-counterfactual-inference.md) | Counterfactual inference — generative spatial reasoning | Proposed (phase 3) | +| [ADR-314](ADR-314-information-gain-scheduler.md) | Information-gain scheduler — sample the most informative radios | Proposed (phase 3) | +| [ADR-315](ADR-315-digital-rf-twin.md) | Digital RF twin — persistent per-deployment RF model | Proposed (phase 3) | +| [ADR-316](ADR-316-fleet-control-plane.md) | Fleet control plane — provisioning to audit trails | Proposed (phase 2) | +| [ADR-317](ADR-317-benchmark-multi-domain-scorecard.md) | Multi-domain benchmark scorecard — regressions cannot hide behind pooled accuracy | Accepted (phase 1) | +| [ADR-318](ADR-318-capability-certificates.md) | Capability certificates — validated-for-this-environment claims | Accepted (phase 1) | +| [ADR-319](ADR-319-witness-chain.md) | Witness chain — staged, signed epistemic envelope | Accepted (phase 1) | +| [ADR-320](ADR-320-sensor-hal.md) | RuView sensor HAL — abstract all sensing hardware to one Observation type | Proposed (phase 2) | +| [ADR-321](ADR-321-decision-policy-action-authorization.md) | Decision policy — action authorization conditioned on certificate class, freshness, uncertainty, evidence | Accepted (phase 1) | --- diff --git a/api-docs/user-guide.md b/api-docs/user-guide.md index 61d27411..de364df2 100644 --- a/api-docs/user-guide.md +++ b/api-docs/user-guide.md @@ -38,6 +38,7 @@ WiFi DensePose turns commodity WiFi signals into real-time human pose estimation 14. [Training a Model](#training-a-model) - [CRV Signal-Line Protocol](#crv-signal-line-protocol) 14. [RVF Model Containers](#rvf-model-containers) +14. [Perception Certificate Spine (Developer Preview, ADR-300)](#perception-certificate-spine-developer-preview-adr-297) 14. [Hardware Setup](#hardware-setup) - [ESP32-S3 Mesh](#esp32-s3-mesh) - [Intel 5300 / Atheros NIC](#intel-5300--atheros-nic) @@ -1493,6 +1494,78 @@ An RVF file contains: model weights, HNSW vector index, quantization codebooks, --- +## Perception Certificate Spine (Developer Preview, ADR-300) + +RuView's perception substrate program (ADR-300) is building a `signal → observation → +calibration → inference → uncertainty → evidence → certificate → policy → governed +action` pipeline, where a downstream consumer either gets a calibrated, provenance-backed +answer or an explicit `UNKNOWN` — never a confident-looking guess outside the sensor's +proven operating envelope. + +**Status: developer preview.** Phase 1 shipped nine new crates with their own test +suites, and each one works correctly in isolation. **They are not yet wired together or +into the live `sensing-server` request path** — there is currently no code path where a +real drift signal from a running sensor flows through calibration → certificate +invalidation → policy denial. Treat everything below as a library you can compose +yourself today, not a safety guarantee the server enforces for you yet. + +### The crates + +| Crate | Role | +|---|---| +| `ruview-ontology` | Canonical `Site → … → Event` types | +| `ruview-attest` | Signed measurement / RF chain-of-custody | +| `ruview-evidence` | Append-only per-context ledger (no pooling, no evidence upgrade) | +| `wifi-densepose-calibration` | Signed, drift-invalidatable calibration certificate | +| `ruview-ood` | `Known` / `Degraded` / `Unknown` staleness-guard domain gating | +| `ruview-witness` | Hash-linked staged provenance chain | +| `ruview-certify` | Capability certificate, conditional on a live domain signature | +| `ruview-scorecard` | Multi-domain scorecard, worst-domain promotion gate | +| `ruview-policy` | Fail-closed action authorization gate | + +### Minting and checking a certificate + +```rust +use ruview_certify::{mint, CapabilityCertificate, DomainState}; + +// `signer`, `request`, and `evidence_slice` come from your own calibration run — +// see each crate's README for how to build them. +let cert = mint(&signer, request, &evidence_slice)?; + +// A certificate is only valid at a given instant AND domain state — the same +// signed certificate is rejected the moment the live domain degrades: +assert!(cert.is_valid(now_ms, DomainState::Known)); +assert!(!cert.is_valid(now_ms, DomainState::Degraded)); +assert!(!cert.is_valid(now_ms, DomainState::Unknown)); +``` + +### Gating an action + +```rust +use ruview_policy::{authorize, ActionClass, DomainState}; + +let decision = authorize(ActionClass::SafetyCritical, &inputs); +// Deny with a named FailedCondition (e.g. `domain_not_known`) rather than a +// silent false-positive, whenever the domain isn't KNOWN. +``` + +**Important:** `ruview_certify::DomainState` and `ruview_policy::DomainState` (and +`ruview_ood`'s) are currently three separate enum types — `ruview-ood`'s `Degraded` +variant even carries different data. There is no automatic conversion between them. +If you compose these crates yourself today, you own writing that bridge; don't assume +one crate's domain read automatically reaches another's gate. + +### What's genuinely enforced today, for comparison + +Not every ADR-295–296 remediation item is preview-only. Two are live now: + +- **UDP data-plane bind hardening (ADR-296)** — `sensing-server`'s `UdpSourceAllowlist` + is checked on every incoming packet (`main.rs`), not just defined. +- **CSI data-incident repo controls (ADR-299)** — `scripts/csi-data-policy-check.sh` + runs in CI on every push/PR and fails the build on a policy violation. + +--- + ## Hardware Setup ### Supported targets