29 KiB
ADR-151: RuView Per-Room Calibration & Specialized Model Training System
| Field | Value |
|---|---|
| Status | Accepted — Stages 1–5 implemented (statistical specialists); HF-backbone distillation pending |
| Date | 2026-06-09 |
| Deciders | ruv |
| Codebase target | New wifi-densepose-calibration crate (orchestration); wifi-densepose-train (rapid_adapt.rs, signal_features.rs, trainer.rs); wifi-densepose-ruvector (RVF specialist storage); wifi-densepose-signal/ruvsense/* (feature extractors); wifi-densepose-cli (enroll, train-room, room-status subcommands) |
| Relates to | ADR-135 (Empty-Room Baseline Calibration), ADR-030 (Persistent Field Model), ADR-134 (CIR), ADR-024 (Contrastive CSI Embedding / AETHER), ADR-027 (Cross-Environment Domain Generalization / MERIDIAN), ADR-070 (Self-Supervised Pretraining), ADR-105 (Federated CSI Training), ADR-149 (AetherArena / Hugging Face), ADR-150 (RF Foundation Encoder) |
1. Context
1.1 The thesis — teach the room before you teach the model
RuView's deployment frontier is not a better generic model. ADR-150 documents the wall directly: an MM-Fi pose head scores 81.63% torso-PCK@20 in-domain but ~11.6% leakage-free cross-subject, and bigger capacity hurts cross-subject (transformer 24.8% < conv 27.3%). A single oversized model that "understands the world" overfits the rooms and bodies it has seen. The lever is the opposite of scale: a small model that understands one room and one person, calibrated in minutes, run locally, and specialised per biological signal.
This positions RuView between the two incumbents in ambient sensing:
- Wearables — high fidelity, but people forget to wear them, and they only measure the wearer.
- Cameras — powerful, but invasive, store identifiable video, and fail in the dark / under covers.
RuView sits in the middle: it learns the space, learns the person, and tracks biological rhythm (breathing, heartbeat, restlessness, posture, presence) without seeing skin or storing video. Heartbeat and breathing are not visual problems — they are tiny, repeating disturbances in the RF field. Capturing them well is a calibration problem, not a model-size problem.
1.2 What already exists (and what is missing)
The pieces of a calibration→training pipeline exist as disconnected modules. There is no system that runs them end to end and emits a per-room model bank.
| Capability | Status today | Gap |
|---|---|---|
| Empty-room baseline (environmental fingerprint) | ADR-135 BaselineCalibration (Proposed): per-subcarrier amplitude + circular-phase stats, ruvcal NVS namespace |
Captures the room, but there is no step that captures guided human anchors on top of it |
| Field eigenstructure | ADR-030 field_model.rs (SVD room eigenmodes) |
Consumes calibration; not wired to a training trigger |
| Shared invariant backbone | ADR-150 RF Foundation Encoder (pose-preserving, subject/room/device-invariant) | Defined as a foundation embedding; nothing distills it into per-room specialists |
| Few-shot adaptation | train/src/rapid_adapt.rs — test-time training → LoRA weight deltas (MERIDIAN P5) |
Produces a single pose-adaptation delta, not a bank of per-modality specialists |
| Feature extractors | ruvsense/{bvp,longitudinal,intention,gesture,pose_tracker,adversarial}.rs, train/src/signal_features.rs |
Each emits a signal; none is packaged as a labelled training source for enrollment |
| Small-model storage | wifi-densepose-ruvector (RVF cognitive containers, HNSW, sketch) |
No schema for "a bank of specialist models scoped to a room_id" |
| HF publishing | ADR-149 AetherArena (Hugging Face Space + signed scorer), sensing-server from_pretrained path |
Publishes/評価s a global model; no notion of a published base + private local heads |
The missing system is the connective tissue: a guided enrollment protocol, a feature-extraction-to-label bridge, a specialist-bank trainer that reuses the frozen HF backbone, and a runtime that fuses the specialists with confidence gating. This ADR defines that system.
1.3 The four-step user model (and where each step lands)
The system is deliberately presented to operators as four plain steps. Each maps to existing or new code:
- Capture a quiet baseline — no people, just room/router/reflections/noise/drift → the environmental fingerprint. → Reuse ADR-135
BaselineCalibration+ ADR-030 field eigenmodes. No new capture code; the calibration crate calls it. - Capture guided samples — stand, sit, lie down, slow vs normal breathing, small movement, sleep posture. Clean anchors, not hours of data. → NEW
EnrollmentProtocol(Section 2.2). - Extract the useful signal — CSI phase, amplitude, Doppler shift, micro-motion, periodicity, variance, timing. → Reuse
signal_features.rs+ ruvsense extractors, packaged as labelledAnchorFeaturerecords (Section 2.3). - Compress patterns into small ruVector models — specialised per signal: breathing, heartbeat, sleep restlessness, posture, presence, anomaly. → NEW
SpecialistBanktrained viarapid_adaptLoRA heads over the frozen ADR-150 backbone, stored as RVF (Section 2.4).
2. Decision
Build the RuView Per-Room Calibration & Specialized Model Training System: a four-stage, local-first pipeline (baseline → enroll → extract → train) that produces a versioned bank of small specialised ruVector models scoped to one room_id, each a lightweight head distilled/adapted from the frozen, Hugging-Face-published RF Foundation Encoder (ADR-150). Big model understands the world; small ruVector models understand your room.
Two invariants govern every design choice below:
(A) Specialisation over scale. One small model per biological signal, not one large model for all of them. Each specialist is faster, cheaper, more private, and — because it is calibrated to the room's actual fingerprint — often more accurate than a general model.
(B) Local-first, base-shared. The frozen room/subject/device-invariant backbone is the only artifact published to Hugging Face. Per-room baselines and per-specialist heads never leave the device unless the operator opts into federation (ADR-105).
2.1 System architecture
HUGGING FACE HUB (public, room-agnostic)
┌───────────────────────────────────────┐
│ RF Foundation Encoder (ADR-150) │
│ pose-preserving · subject/room/device │
│ -invariant · frozen · safetensors │
└───────────────┬───────────────────────┘
│ from_pretrained() once, cached on device
▼
STAGE 1 baseline STAGE 2 enroll STAGE 3 extract STAGE 4 train (per room_id)
┌──────────────┐ ┌──────────────┐ ┌────────────────┐ ┌─────────────────────────┐
│ ADR-135 │ │ Enrollment │ │ signal_features│ │ SpecialistBank │
│ Baseline- │──fp──► │ Protocol │─clip►│ + ruvsense │─AF──►│ frozen backbone │
│ Calibration │ │ guided │ │ extractors │ │ │ ┌────────────────┐ │
│ (env finger- │ │ anchors: │ │ → AnchorFeature│ │ ├─►│ breathing head │ │
│ print) │ │ stand/sit/ │ │ (phase, amp, │ │ ├─►│ heartbeat head │ │
│ ADR-030 │ │ lie/breathe/ │ │ doppler, │ │ ├─►│ restless head │ │
│ field eigen │ │ move/sleep │ │ micromotion, │ │ ├─►│ posture head │ │
└──────────────┘ └──────────────┘ │ periodicity, │ │ ├─►│ presence head │ │
│ │ variance, │ │ └─►│ anomaly head │ │
│ baseline drift > τ → invalidate bank │ timing) │ │ (LoRA / ruVector │
└───────────────────────────────────────┴────────────────┴──────┤ small models) │
└───────────┬─────────────┘
│ RVF container
▼
RUNTIME: Mixture-of-Specialists
each head emits {value, confidence};
coherence_gate (ADR-135) + anomaly
head veto → fused RoomState
The shared backbone is loaded once per device and frozen. Every specialist is a small head over its embedding — so the marginal cost of a sixth specialist is kilobytes of LoRA weights, not another full model.
2.2 Stage 2 — the guided enrollment protocol (NEW)
EnrollmentProtocol is a CLI-driven state machine that walks the operator through a fixed sequence of labelled anchors. The design rule from the user vision is explicit: clean anchors, not hours of data. Each anchor is a short (default 20 s @ 20 Hz = 400 frames) labelled clip captured against the already-recorded baseline.
| Anchor | Label | Duration | Primary signal taught | Feature emphasis |
|---|---|---|---|---|
empty |
presence=0 | (reuse ADR-135 baseline) | absence reference | amplitude variance floor |
stand_still |
posture=standing, presence=1 | 20 s | static human load | amplitude mean shift, eigenmode delta |
sit |
posture=sitting | 20 s | lower static load | amplitude profile |
lie_down |
posture=lying | 20 s | sleep-position load | amplitude profile, low Doppler |
breathe_slow |
resp≈0.1–0.15 Hz | 30 s | slow respiration | periodicity, micro-Doppler |
breathe_normal |
resp≈0.2–0.3 Hz | 30 s | normal respiration | periodicity, BVP phase |
small_move |
motion=1 | 20 s | limb micro-motion | Doppler spread, variance |
sleep_posture |
posture=lying, restless=0 | 30 s | quiescent sleep baseline | long-window variance, timing |
The protocol is adaptive: an anchor is only accepted when its captured features pass a quality gate (coherence ≥ threshold from coherence_gate.rs, sufficient SNR vs baseline, no saturation). A failed anchor is re-prompted rather than silently kept — bad anchors poison small models far more than large ones. Total guided enrollment is ~4 minutes of wall-clock, producing 8 clean anchors. This is intentionally far below the "hours of data" that a from-scratch model needs, because the backbone already carries world knowledge; enrollment only teaches this room's offsets.
Anchors are persisted as an append-only EnrollmentSession (event-sourced, per CLAUDE.md state rules) under room_id, so re-enrollment is incremental and auditable.
2.3 Stage 3 — feature extraction to labelled records (REUSE + bridge)
Each accepted anchor clip is run through the existing extractor stack, baseline-subtracted per ADR-135, and packaged into an AnchorFeature record. No new DSP is invented — this stage is a bridge, not a new algorithm.
| Feature group | Source module | Used by specialists |
|---|---|---|
| CSI amplitude mean/variance | ADR-135 baseline subtraction + signal_features.rs |
presence, posture |
| CSI phase (sanitised, LO-aligned) | phase_sanitizer → phase_align |
posture, heartbeat |
| Doppler shift / micro-Doppler | ruvsense/bvp.rs, breathing path |
breathing, small-move |
| Micro-motion / intention lead | ruvsense/intention.rs |
restlessness, anomaly |
| Periodicity / spectral peaks | bvp.rs autocorrelation + FFT |
breathing, heartbeat |
| Long-window variance / drift | ruvsense/longitudinal.rs (Welford) |
restlessness, presence |
| Timing / inter-frame epoch | c6_timesync epoch, frame Δt |
all (rhythm alignment) |
| Field eigenmode coefficients | ADR-030 field_model.rs |
posture, presence |
AnchorFeature = { room_id, anchor_label, t_epoch_us, embedding: [f32; D] (backbone output), aux: { resp_hz?, doppler_spread, variance, periodicity_score, eigen_coeffs } }. The backbone embedding is the shared representation; aux carries the cheap hand-features that let small heads specialise without re-learning DSP.
2.4 Stage 4 — the specialist bank (NEW, the core contribution)
A SpecialistBank is a versioned collection of small models scoped to one room_id, persisted as a single RVF cognitive container (wifi-densepose-ruvector). Each specialist is a head over the frozen backbone embedding, trained from the labelled AnchorFeature records via the existing rapid_adapt.rs LoRA machinery (test-time/few-shot training, contrastive + entropy losses), not a from-scratch network.
| Specialist | Model type | Params (typ.) | Label source | Output |
|---|---|---|---|---|
| breathing | 1-D temporal head + periodicity regressor | ~8 KB LoRA + aux | breathe_slow/breathe_normal |
resp rate (Hz) + confidence |
| heartbeat | narrowband phase head (harmonic-aware) | ~12 KB | quiescent anchors + periodicity | HR (bpm) + confidence |
| sleep restlessness | variance/drift classifier | ~4 KB | sleep_posture vs small_move |
restlessness score [0,1] |
| posture | k-way prototype classifier (HNSW NN) | prototypes only | stand/sit/lie anchors |
posture class + margin |
| presence | binary energy/eigenmode gate | ~2 KB | empty vs occupied anchors |
presence prob |
| anomaly | one-class / physically-impossible detector (adversarial.rs) |
~6 KB | baseline + all anchors (novelty) | anomaly score + veto flag |
Design properties that follow from invariant (A):
- Independently versioned & swappable. Re-enrolling breathing does not retrain posture. A specialist carries its own
{trained_at, anchor_set_hash, baseline_hash, backbone_rev}. - HNSW prototype storage for the classifiers. Posture and presence are nearest-prototype lookups in the RVF index — no inference engine, microsecond latency, and new postures are added by inserting a prototype, not retraining.
- SONA online adaptation. Each specialist may carry a SONA/MicroLoRA online-adaptation slot (
ruvllm_sona_*/microloraprimitives) so it tracks slow drift (furniture moved, seasonal RF change) between full re-enrollments, gated by ADR-135 baseline drift. - Teacher–student distillation (optional, offline). Where a labelled public corpus exists (MM-Fi, Wi-Pose), the ADR-150 backbone acts as teacher to pre-shape a head before per-room fine-tuning, improving cold-start. The teacher is global/HF; the student head is local.
Invalidation contract. The bank stores the baseline_id (the baseline UUID) it was trained against. As implemented, the runtime marks the bank STALE whenever the current baseline id differs from the trained one — a conservative trigger that catches re-calibration (room rearranged, AP moved, band changed) because any of those produces a new baseline. A finer drift-threshold trigger (mark STALE when ADR-135's per-subcarrier deviation exceeds τ without a full re-baseline) is a planned refinement (P6). Either way the runtime prompts re-enrollment rather than emitting silently wrong vitals — the calibration analogue of the #954 DEGRADED honesty rule: never report confident numbers from an invalid model.
2.5 Runtime — mixture of specialists with confidence gating
At inference, the frozen backbone embeds each CSI window once; every specialist consumes that shared embedding and emits {value, confidence}. Fusion rules:
- The anomaly specialist holds a veto: a high anomaly score (physically-impossible signal per
adversarial.rs, or a coherence-gateReject) suppresses positive vitals/posture output and raises a flag, rather than propagating a hallucinated reading. - presence=0 short-circuits breathing/heartbeat/posture to
null(you cannot have a respiration rate in an empty room). - Each emitted reading is tagged with the specialist's confidence and the
baseline_hash/backbone_revprovenance, so downstream consumers (sensing-server, MQTT, Home Assistant) can gate on quality — consistent with ADR-135 coherence-gate semantics.
2.6 Crate & module layout
New bounded-context crate wifi-densepose-calibration (orchestration only; files < 500 lines, typed public APIs, event-sourced sessions — per CLAUDE.md):
wifi-densepose-calibration/
src/
lib.rs # public API: CalibrationSystem facade
enrollment.rs # EnrollmentProtocol state machine (Stage 2)
anchor.rs # Anchor, EnrollmentSession (event-sourced)
extract.rs # AnchorFeature bridge over signal_features + ruvsense (Stage 3)
specialist.rs # Specialist trait, SpecialistKind enum
bank.rs # SpecialistBank (RVF container, versioning, invalidation)
runtime.rs # MixtureOfSpecialists fusion + veto (Stage 5)
backbone.rs # frozen ADR-150 encoder loader (hf_hub from_pretrained, cached)
error.rs
Dependencies (no duplication — orchestrates existing crates): wifi-densepose-signal (ruvsense extractors, ADR-135 baseline), wifi-densepose-train (rapid_adapt, signal_features, trainer), wifi-densepose-ruvector (RVF, HNSW), wifi-densepose-nn (backbone inference). The wifi-densepose-cli gains enroll, train-room, and room-status subcommands, sequenced after the existing ADR-135 calibrate.
2.7 CLI flow (operator-facing)
# Stage 1 — environmental fingerprint (ADR-135, existing)
wifi-densepose calibrate --room living-room --duration 60s # empty room
# Stage 2+3 — guided enrollment (NEW); prompts through 8 anchors, ~4 min
wifi-densepose enroll --room living-room
# → "Stand still in view of the sensor…" [✓ anchor accepted: coherence 0.91]
# → "Sit down…" [✗ low SNR, retrying]
# ...
# Stage 4 — train the specialist bank (NEW); reuses cached HF backbone
wifi-densepose train-room --room living-room \
--specialists breathing,heartbeat,restlessness,posture,presence,anomaly
# Status / invalidation
wifi-densepose room-status --room living-room
# baseline: fresh (drift 0.04 < 0.20) · backbone: rf-foundation@1.2.0
# breathing ✓ trained 2026-06-09 conf p50 0.88
# heartbeat ✓ trained 2026-06-09 conf p50 0.71
# posture ✓ 3 prototypes (stand/sit/lie)
# anomaly ✓ · presence ✓ · restlessness ✓
3. Consequences
3.1 Positive
- Fidelity through specialisation. Six small calibrated heads beat one oversized general model on the cross-room/cross-subject frontier that ADR-150 quantified — and each runs in microseconds-to-milliseconds, on-device.
- Privacy by construction. Only the room-agnostic backbone is public (HF). The environmental fingerprint and the person-specific heads stay local; no video, no skin, no cloud round-trip. This is the core differentiator vs cameras and the convenience differentiator vs wearables.
- Minutes, not hours. Because the backbone carries world knowledge, ~4 minutes of clean anchors calibrates a room. Re-enrollment is incremental.
- Honest degradation. The
baseline_hashinvalidation + anomaly veto mean an out-of-calibration room reportsSTALE/flagged rather than confidently wrong — the same honesty principle as the firmwareDEGRADEDflag. - Composable & cheap to extend. A new biological signal = a new small head over the same embedding, not a new model.
3.2 Negative / risks
- Backbone dependency. Every specialist rides on ADR-150's encoder; its quality and revision compatibility (
backbone_rev) are a single point of leverage. Mitigation: pinbackbone_revin each specialist; distillation cold-start reduces sensitivity. - Enrollment burden. 4 minutes is small but non-zero, and anchor quality depends on the operator following prompts. Mitigation: adaptive re-prompting + quality gates; ship sane defaults so a partial bank (presence+posture) works after just the static anchors.
- Heartbeat is hard. Sub-mm chest displacement at HR frequencies is near the ESP32-S3 noise floor; the heartbeat specialist will have lower and more variable confidence than breathing. The confidence-gated runtime surfaces this rather than faking it.
- Per-room storage proliferation. A bank per room per person; needs a clear RVF lifecycle (list/prune/export) — handled by
bank.rsversioning and theroom-statusCLI.
3.3 Alternatives considered
| Alternative | Verdict | Reason |
|---|---|---|
| One large general model for all signals | Rejected | The ADR-150 evidence: scale overfits rooms/subjects and collapses cross-domain; also slower, costlier, less private. Directly contradicts invariant (A). |
| Cloud training of per-room models | Rejected | Violates invariant (B): would ship raw CSI of a person's home/sleep to a server. Local-first is the privacy promise. Federation (ADR-105) is the opt-in path for shared improvement, exchanging gradients/deltas, never raw CSI. |
| Skip the backbone; train each specialist from scratch | Rejected | Reintroduces the "hours of data" requirement the user vision explicitly rejects, and loses cross-room priors. |
| Fold this into ADR-135 | Rejected | ADR-135 is room calibration (no humans). This ADR is human-anchor enrollment + model training on top of it. Distinct lifecycles, distinct invalidation; kept as separate bounded contexts. |
4. Implementation phases
| Phase | Scope | Exit criterion | Status |
|---|---|---|---|
| P1 | Scaffold wifi-densepose-calibration crate; AnchorFeature schema; (backbone via hf_hub deferred) |
Crate + schema; unit tests | ✅ Done (crate + Stage-1 baseline via calibrate/calibrate-serve; HF backbone deferred) |
| P2 | EnrollmentProtocol + anchor.rs (event-sourced sessions) + CLI enroll with quality gates |
8-anchor enrollment; bad anchors re-prompt | ✅ Done (anchor.rs, enrollment.rs, CLI enroll) |
| P3 | extract.rs bridge → labelled records; baseline subtraction (ADR-135) |
AnchorFeature records persisted per room_id |
✅ Done (extract.rs; autocorr periodicity + variance/motion) |
| P4 | SpecialistBank + presence/posture (prototype) + breathing (periodicity); persistence + versioning |
train-room produces a bank; room-status reads it back |
✅ Done (specialist.rs, bank.rs, CLI train-room/room-status; JSON persistence — RVF/HNSW = future) |
| P5 | heartbeat + restlessness + anomaly specialists; runtime.rs mixture + veto + confidence gating |
End-to-end RoomState on hardware; anomaly veto verified | ✅ Done (runtime.rs, CLI room-watch; breathing read live on COM8 ESP32) |
| P6 | Baseline-drift STALE invalidation; SONA online adaptation; optional ADR-105 federation; HF teacher–student distillation |
Drift marks bank STALE; AetherArena entry | ◐ Partial (STALE done; SONA/federation/HF-backbone = follow-ups) |
Current status (2026-06-10): Stages 1–5 implemented with statistical specialists (threshold/prototype/autocorrelation). 55 tests (35 unit incl. multistatic + 1 full-loop integration + 19 CLI), all passing under qemu-aarch64. Validation scope is precise: baseline capture + HTTP API + auth are proven on real CSI (Pi-5 nexmon, 6,813 frames; and an ESP32-S3). The complete baseline → enroll → train-room → infer loop is now proven in-process on deterministic synthetic CSI (tests/full_loop.rs: clean baseline with zero motion flags, 8/8 anchors through the quality gate, 6 specialists trained, JSON bank round-trip, trained-bank inference 18±2 BPM positive / absent negative / foreign-baseline STALE; seed-robust). The one live runtime signal (breathing ~16–31 BPM via room-watch) used the stateless breathing head, not a trained bank; the clean empty-room loop has not yet run on-target — the remaining gap is strictly the hardware session (empty room + operator anchors). The four behavioral findings from the full-loop test (z-band squeeze, variance-only presence, ungated hz embedding, heart-band lag-floor leakage) are FIXED and regression-guarded — see the integration doc §7. SOTA-intake decisions affecting this system (geometry conditioning, checkerboard alignment) are recorded in ADR-152. Open refinements: --source-format adr018v6 (drive from the Pi's own nexmon), phase-based breathing carrier, RVF/HNSW storage, and the ADR-150 frozen HF backbone the specialists would distill from.
Validation per CLAUDE.md: cargo test --workspace --no-default-features green; hardware verification on the ESP32-S3 (currently COM8) before any release; witness bundle regenerated if the proof surface changes.
6. Review notes
6.1 Correctness + security review (2026-06-14)
Beyond-SOTA correctness+security review of wifi-densepose-calibration (this
ADR's pipeline), un-covered by the ADR-154–159 sweep.
Finding (FIXED) — NaN-poisoning of the feature path (numerical / fail-closed).
Features::from_series — the carrier for both live inference and training-anchor
extraction — computed mean/variance/motion over the raw scalar series with
no non-finite guard. A single NaN/±inf sample (corrupt CSI frame) yielded
mean=NaN, variance=NaN and an all-NaN prototype embedding. Persisted into a
PresenceSpecialist::threshold/empty_mean at train time, the NaN silently
disabled presence detection for the bank's lifetime (every > / |·|
comparison against NaN is false → always reads absent, confidence 0), with no
error — and an asymmetry against the rigorously NaN-guarded geometry_embedding.
Fixed at the production boundary: non-finite samples are dropped (a corrupt frame
counts as no frame), an all-non-finite series degrades to Features::ZERO like
the empty series. Value-identical for all-finite input (full-loop + extract tests
unchanged); pinned by non_finite_samples_do_not_poison_features and
all_non_finite_series_is_zero (both fail on the old code).
Clean dimensions (evidence, no invented issues).
- File/path handling: the crate performs zero file/path I/O (no
std::fs/Path/File/read/writeinsrc/; only in-memoryserde_json). Path-traversal / unbounded-read / artifact-path handling live entirely in thewifi-densepose-cliconsumer (room.rs), outside this crate's boundary. - Untrusted-load:
SpecialistBank::from_jsonshape-validates via serde (malformed →CalibrationError::Serde); banks are local-first (invariant B), never network-received. A well-formed bank with adversarial numerics is trusted as-is — acceptable under the local-first threat model; a validate-on-load defense-in-depth pass is a possible future hardening, not a present bug. - Receipt/hash integrity: the crate emits no hash/receipt/witness/signature, so
the unframed-concatenation bug class (cf. the engine
witness_offix) is structurally absent. - Other numerical paths:
geometry_embeddingsanitizes every input and sweeps to finite; presence/restlessness/anomaly divisions are.max(1e-3)-guarded;autocorr_dominantguardsr0, short signals, and empty bands;trainrejects empty anchors; anomaly requires ≥2 anchors.
De-magicked the bare specialist threshold literals (breathing/heartbeat default min-scores, anomaly outlier-spread multiple + label cutoff) into named documented consts, value-identical, pinned by const-equality tests. Tests 58→62 unit + 1 integration, 0 failed; Python deterministic proof unchanged (off the signal proof path).
5. Summary
Big models understand the world. Small ruVector models understand your room.
ADR-151 makes that operational: a local-first baseline → enroll → extract → train pipeline that turns ~4 minutes of clean human anchors — layered on ADR-135's empty-room fingerprint and ADR-150's Hugging-Face-published invariant backbone — into a versioned bank of tiny, specialised, privacy-preserving models for breathing, heartbeat, restlessness, posture, presence, and anomaly. Specialisation over scale; local heads over a shared base; honest STALE degradation over confident error.