This commit is contained in:
ruvnet
2026-08-11 17:41:30 +00:00
parent cc140650da
commit b5633f9865
33 changed files with 3900 additions and 0 deletions
@@ -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 20242025: 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.
@@ -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
20160 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 (20160 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.
@@ -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.10.5 Hz) and heart
rate (0.82.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), BlandAltman
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.
@@ -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 (I1I3 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.
@@ -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`.
@@ -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.
@@ -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`.
@@ -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.
@@ -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.
@@ -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` L0L5 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).
@@ -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` (L0L5, 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.
@@ -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.
@@ -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/
BlandAltman/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/BlandAltman/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.
+117
View File
@@ -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` L0L5 (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` (L0L5, 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.
@@ -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.
@@ -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` (L0L5, 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.
@@ -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.
@@ -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.
+152
View File
@@ -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`
P0P5 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 P0P5 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.
@@ -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` (L0L5, 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.
+140
View File
@@ -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.
@@ -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.
@@ -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` L0L5 (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.
@@ -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.
+159
View File
@@ -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.
+156
View File
@@ -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.
@@ -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` (L0L5, 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.
@@ -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 L0L5 (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.
+146
View File
@@ -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` (L0L5, 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.
+150
View File
@@ -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` (L0L5, 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).
@@ -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 L0L5 floor (ADR-282/ADR-304); e.g. a safety
action may require ≥ L3 (held-out room+subject validation).
Reference action classes (illustrative, configurable):
| Class | Example | Typical floor |
|---|---|---|
| `Convenience` | lighting, scenes | tolerant: L1+, higher uncertainty ok |
| `Security` | alerts, arming | stricter: valid cert, L2+, bounded uncertainty |
| `SafetyCritical` | door lock, machine stop | strict: fresh cert, L3+, low uncertainty, KNOWN domain only |
### 2. The authorization decision
`authorize(action, capability_certificate, live_state) -> Authorization` where
`live_state` carries the current `SourceState` (ADR-295), OOD/domain state
(ADR-302), and inference uncertainty. Rules:
- **Fail-closed.** Any unmet condition → `Deny { failed_condition }`. The denial
names the *specific* condition (expired cert, domain DEGRADED, uncertainty
over ceiling, evidence below floor, certificate class too low).
- **UNKNOWN denies high-assurance actions.** A domain in UNKNOWN (ADR-302)
cannot authorize `Security`/`SafetyCritical` actions; it may still authorize
`Convenience` if that class's policy permits, but the authorization records
that it proceeded under UNKNOWN.
- The decision is a **pure function** of (action class, certificate, live
state) — deterministic and unit-testable without a clock or actuator.
- Every authorization (allow or deny) is emitted as the terminal stage of the
witness chain (ADR-319), so "why was this actuator allowed/denied" is
auditable end-to-end.
### 3. No silent optimism
A missing certificate, an expired certificate, or an unrecognized action class
all deny by default. Absence of a policy is not permission.
## Consequences
- Action authorization becomes uniform and centrally reasoned instead of
per-consumer and optimistic; this is the "RuView Certify → constrains action"
boundary that is hard to commoditize.
- A behavior change for existing automations that acted directly on presence:
they now pass through the gate. Convenience-class defaults keep low-stakes
automations working; high-stakes actions must opt into stricter classes.
- Depends on ADR-318 (certificate), ADR-302 (domain/OOD state), ADR-295
(source state), ADR-304 (evidence). Built in the phase-1 dependent wave after
those types land.
## Validation
- Unit tests: each action class authorizes/denies correctly across the matrix
of (valid/expired/degraded cert × KNOWN/DEGRADED/UNKNOWN domain × uncertainty
above/below ceiling × evidence above/below floor); UNKNOWN denies
safety-critical; every deny names its failed condition; absence-of-policy
denies; determinism.
- Integration: the acceptance-test scenario (ADR-300) — post-certification room
change drives domain to DEGRADED→UNKNOWN, and a `SafetyCritical` authorization
is denied with `failed_condition = domain_not_known` *before* the inference
reaches the actuator, witness chain preserved.
- `cargo test -p ruview-policy`.
+31
View File
@@ -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) |
---
+73
View File
@@ -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-295296 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