# RuvSense Domain Model ## Domain-Driven Design Specification ### Ubiquitous Language | Term | Definition | |------|------------| | **Sensing Cycle** | One complete TDMA round (all nodes TX once): 50ms at 20 Hz | | **Link** | A single TX-RX pair; with N nodes there are N×(N-1) directed links | | **Multi-Band Frame** | Fused CSI from one node hopping across multiple channels in one dwell cycle | | **Fused Sensing Frame** | Aggregated observation from all nodes at one sensing cycle, ready for inference | | **Coherence Score** | 0.0-1.0 metric quantifying consistency of current CSI with reference template | | **Coherence Gate** | Decision rule that accepts, inflates noise, rejects, or triggers recalibration | | **Pose Track** | A temporally persistent per-person 17-keypoint trajectory with Kalman state | | **Track Lifecycle** | State machine: Tentative → Active → Lost → Terminated | | **Re-ID Embedding** | 128-dim AETHER contrastive vector encoding body identity | | **Node** | An ESP32-S3 device acting as both TX and RX in the multistatic mesh | | **Aggregator** | Central device (ESP32/RPi/x86) that collects CSI from all nodes and runs fusion | | **Sensing Schedule** | TDMA slot assignment: which node transmits when | | **Channel Hop** | Switching the ESP32 radio to a different WiFi channel for multi-band sensing | | **Person Cluster** | A subset of links whose CSI variations are correlated (attributed to one person) | --- ## Bounded Contexts ### 1. Multistatic Sensing Context **Responsibility:** Collect, normalize, and fuse CSI from multiple ESP32 nodes across multiple channels into a single coherent sensing frame per cycle. ``` ┌──────────────────────────────────────────────────────────┐ │ Multistatic Sensing Context │ ├──────────────────────────────────────────────────────────┤ │ │ │ ┌───────────────┐ ┌───────────────┐ │ │ │ Link Buffer │ │ Multi-Band │ │ │ │ Collector │ │ Fuser │ │ │ │ (per-link │ │ (per-node │ │ │ │ ring buf) │ │ channel │ │ │ └───────┬───────┘ │ fusion) │ │ │ │ └───────┬───────┘ │ │ │ │ │ │ └────────┬───────────┘ │ │ ▼ │ │ ┌────────────────┐ │ │ │ Phase Aligner │ │ │ │ (cross-chan │ │ │ │ correction) │ │ │ └────────┬───────┘ │ │ ▼ │ │ ┌────────────────┐ │ │ │ Multistatic │ │ │ │ Fuser │──▶ FusedSensingFrame │ │ │ (cross-node │ │ │ │ attention) │ │ │ └────────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘ ``` **Aggregates:** - `FusedSensingFrame` (Aggregate Root) **Value Objects:** - `MultiBandCsiFrame` - `LinkGeometry` (tx_pos, rx_pos, distance, angle) - `SensingSchedule` - `ChannelHopConfig` **Domain Services:** - `PhaseAlignmentService` — Corrects LO-induced phase rotation between channels - `MultiBandFusionService` — Merges per-channel CSI into wideband virtual frame - `MultistaticFusionService` — Attention-based fusion of N nodes into one frame **RuVector Integration:** - `ruvector-solver` → Phase alignment (NeumannSolver) - `ruvector-attention` → Cross-channel feature weighting - `ruvector-attn-mincut` → Cross-node spectrogram attention gating - `ruvector-temporal-tensor` → Per-link compressed ring buffers --- ### 2. Coherence Context **Responsibility:** Monitor temporal consistency of CSI observations and gate downstream updates to reject drift, transient interference, and environmental changes. ``` ┌──────────────────────────────────────────────────────────┐ │ Coherence Context │ ├──────────────────────────────────────────────────────────┤ │ │ │ ┌───────────────┐ ┌───────────────┐ │ │ │ Reference │ │ Coherence │ │ │ │ Template │ │ Calculator │ │ │ │ (EMA of │ │ (z-score per │ │ │ │ static CSI) │ │ subcarrier) │ │ │ └───────┬───────┘ └───────┬───────┘ │ │ │ │ │ │ └────────┬───────────┘ │ │ ▼ │ │ ┌────────────────┐ │ │ │ Static/Dynamic│ │ │ │ Decomposer │ │ │ │ (separate env │ │ │ │ vs. body) │ │ │ └────────┬───────┘ │ │ ▼ │ │ ┌────────────────┐ │ │ │ Gate Policy │──▶ GateDecision │ │ │ (Accept / │ (Accept / PredictOnly / │ │ │ Reject / │ Reject / Recalibrate) │ │ │ Recalibrate) │ │ │ └────────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘ ``` **Aggregates:** - `CoherenceState` (Aggregate Root) — Maintains reference template and gate state **Value Objects:** - `CoherenceScore` (0.0-1.0) - `GateDecision` (Accept / PredictOnly / Reject / Recalibrate) - `ReferenceTemplate` (EMA of static-period CSI) - `DriftProfile` (Stable / Linear / StepChange) **Domain Services:** - `CoherenceCalculatorService` — Computes per-subcarrier z-score coherence - `StaticDynamicDecomposerService` — Separates environmental drift from body motion - `GatePolicyService` — Applies threshold-based gating rules **RuVector Integration:** - `ruvector-solver` → Coherence matrix decomposition (static vs. dynamic) - `ruvector-attn-mincut` → Gate which subcarriers contribute to template update --- ### 3. Pose Tracking Context **Responsibility:** Track multiple people as persistent 17-keypoint skeletons across time, with Kalman-smoothed trajectories, lifecycle management, and identity preservation via re-ID. ``` ┌──────────────────────────────────────────────────────────┐ │ Pose Tracking Context │ ├──────────────────────────────────────────────────────────┤ │ │ │ ┌───────────────┐ ┌───────────────┐ │ │ │ Person │ │ Detection │ │ │ │ Separator │ │ -to-Track │ │ │ │ (min-cut on │ │ Assigner │ │ │ │ link corr) │ │ (Hungarian+ │ │ │ └───────┬───────┘ │ embedding) │ │ │ │ └───────┬───────┘ │ │ │ │ │ │ └────────┬───────────┘ │ │ ▼ │ │ ┌────────────────┐ │ │ │ Kalman Filter │ │ │ │ (17-keypoint │ │ │ │ 6D state ×17)│ │ │ └────────┬───────┘ │ │ ▼ │ │ ┌────────────────┐ │ │ │ Lifecycle │ │ │ │ Manager │──▶ TrackedPose │ │ │ (Tentative → │ │ │ │ Active → │ │ │ │ Lost) │ │ │ └────────┬───────┘ │ │ │ │ │ ┌────────▼───────┐ │ │ │ Embedding │ │ │ │ Identifier │ │ │ │ (AETHER re-ID)│ │ │ └────────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘ ``` **Aggregates:** - `PoseTrack` (Aggregate Root) **Entities:** - `KeypointState` — Per-keypoint Kalman state (x,y,z,vx,vy,vz) with covariance **Value Objects:** - `TrackedPose` — Immutable snapshot: 17 keypoints + confidence + track_id + lifecycle - `PersonCluster` — Subset of links attributed to one person - `AssignmentCost` — Combined Mahalanobis + embedding distance - `TrackLifecycleState` (Tentative / Active / Lost / Terminated) **Domain Services:** - `PersonSeparationService` — Min-cut partitioning of cross-link correlation graph - `TrackAssignmentService` — Bipartite matching of detections to existing tracks - `KalmanPredictionService` — Predict step at 20 Hz (decoupled from measurement rate) - `KalmanUpdateService` — Gated measurement update (subject to coherence gate) - `EmbeddingIdentifierService` — AETHER cosine similarity for re-ID **RuVector Integration:** - `ruvector-mincut` → Person separation (DynamicMinCut on correlation graph) - `ruvector-mincut` → Detection-to-track assignment (DynamicPersonMatcher) - `ruvector-attention` → Embedding similarity via ScaledDotProductAttention --- ## Core Domain Entities ### FusedSensingFrame (Value Object) ```rust pub struct FusedSensingFrame { /// Timestamp of this sensing cycle pub timestamp_us: u64, /// Fused multi-band spectrogram from all nodes /// Shape: [n_velocity_bins x n_time_frames] pub fused_bvp: Vec, pub n_velocity_bins: usize, pub n_time_frames: usize, /// Per-node multi-band frames (preserved for geometry) pub node_frames: Vec, /// Node positions (from deployment config) pub node_positions: Vec<[f32; 3]>, /// Number of active nodes contributing pub active_nodes: usize, /// Cross-node coherence (higher = more agreement) pub cross_node_coherence: f32, } ``` ### PoseTrack (Aggregate Root) ```rust pub struct PoseTrack { /// Unique track identifier pub id: TrackId, /// Per-keypoint Kalman state pub keypoints: [KeypointState; 17], /// Track lifecycle state pub lifecycle: TrackLifecycleState, /// Running-average AETHER embedding for re-ID pub embedding: Vec, // [128] /// Frames since creation pub age: u64, /// Frames since last successful measurement update pub time_since_update: u64, /// Creation timestamp pub created_at: u64, /// Last update timestamp pub updated_at: u64, } ``` ### KeypointState (Entity) ```rust pub struct KeypointState { /// State vector [x, y, z, vx, vy, vz] pub state: [f32; 6], /// 6x6 covariance matrix (upper triangle, row-major) pub covariance: [f32; 21], /// Confidence (0.0-1.0) from DensePose model pub confidence: f32, } ``` ### CoherenceState (Aggregate Root) ```rust pub struct CoherenceState { /// Per-subcarrier reference amplitude (EMA) pub reference: Vec, /// Per-subcarrier variance over recent window pub variance: Vec, /// EMA decay rate for reference update pub decay: f32, /// Current coherence score pub score: f32, /// Frames since last accepted update pub stale_count: u64, /// Current drift profile classification pub drift_profile: DriftProfile, } ``` --- ## Domain Events ### Sensing Events ```rust pub enum SensingEvent { /// New fused sensing frame available FrameFused { timestamp_us: u64, active_nodes: usize, cross_node_coherence: f32, }, /// Node joined or left the mesh MeshTopologyChanged { node_id: u8, change: TopologyChange, // Joined / Left / Degraded active_nodes: usize, }, /// Channel hop completed on a node ChannelHopCompleted { node_id: u8, from_channel: u8, to_channel: u8, gap_us: u32, }, } ``` ### Coherence Events ```rust pub enum CoherenceEvent { /// Coherence dropped below accept threshold CoherenceLost { score: f32, threshold: f32, timestamp_us: u64, }, /// Coherence recovered above accept threshold CoherenceRestored { score: f32, stale_duration_ms: u64, timestamp_us: u64, }, /// Recalibration triggered (>10s low coherence) RecalibrationTriggered { stale_duration_ms: u64, timestamp_us: u64, }, /// Recalibration completed via SONA TTT RecalibrationCompleted { adaptation_loss: f32, timestamp_us: u64, }, /// Environmental drift detected DriftDetected { drift_type: DriftProfile, magnitude: f32, timestamp_us: u64, }, } ``` ### Tracking Events ```rust pub enum TrackingEvent { /// New person detected (track born) PersonDetected { track_id: TrackId, position: [f32; 3], // centroid confidence: f32, timestamp_us: u64, }, /// Person pose updated PoseUpdated { track_id: TrackId, keypoints: [[f32; 4]; 17], // [x, y, z, conf] per keypoint jitter_mm: f32, // RMS jitter at torso timestamp_us: u64, }, /// Person lost (signal dropout) PersonLost { track_id: TrackId, last_position: [f32; 3], last_embedding: Vec, timestamp_us: u64, }, /// Person re-identified after loss PersonReidentified { track_id: TrackId, previous_track_id: TrackId, similarity: f32, gap_duration_ms: u64, timestamp_us: u64, }, /// Track terminated (exceeded max lost duration) TrackTerminated { track_id: TrackId, reason: TerminationReason, total_duration_ms: u64, timestamp_us: u64, }, } pub enum TerminationReason { /// Exceeded max_lost_frames without re-acquisition SignalTimeout, /// Confidence below minimum for too long LowConfidence, /// Determined to be false positive FalsePositive, /// System shutdown SystemShutdown, } ``` --- ## Context Map ``` ┌──────────────────────────────────────────────────────────────────┐ │ RuvSense System │ ├──────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────┐ FusedFrame ┌──────────────────┐ │ │ │ Multistatic │──────────────▶│ Pose Tracking │ │ │ │ Sensing │ │ Context │ │ │ │ Context │ │ │ │ │ └────────┬─────────┘ └────────┬───────────┘ │ │ │ │ │ │ │ Publishes │ Publishes │ │ │ SensingEvent │ TrackingEvent │ │ ▼ ▼ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ Event Bus (Domain Events) │ │ │ └────────────────────┬───────────────────────────────┘ │ │ │ │ │ ┌───────────▼───────────┐ │ │ │ Coherence Context │ │ │ │ (subscribes to │ │ │ │ SensingEvent; │ │ │ │ publishes │ │ │ │ CoherenceEvent; │ │ │ │ gates Tracking │ │ │ │ updates) │ │ │ └───────────────────────┘ │ │ │ ├──────────────────────────────────────────────────────────────────┤ │ UPSTREAM (Conformist) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │wifi-densepose│ │wifi-densepose│ │wifi-densepose│ │ │ │ -hardware │ │ -nn │ │ -signal │ │ │ │ (CsiFrame │ │ (DensePose │ │ (SOTA algs │ │ │ │ parser) │ │ model) │ │ per link) │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ ├──────────────────────────────────────────────────────────────────┤ │ SIBLING (Partnership) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ AETHER │ │ MERIDIAN │ │ MAT │ │ │ │ (ADR-024) │ │ (ADR-027) │ │ (ADR-001) │ │ │ │ embeddings │ │ geometry │ │ triage │ │ │ │ for re-ID │ │ encoding │ │ lifecycle │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └──────────────────────────────────────────────────────────────────┘ ``` **Relationship Types:** - Multistatic Sensing → Pose Tracking: **Customer/Supplier** (Sensing produces FusedFrames; Tracking consumes) - Coherence → Multistatic Sensing: **Subscriber** (monitors frame quality) - Coherence → Pose Tracking: **Gate/Interceptor** (controls measurement updates) - RuvSense → Upstream crates: **Conformist** (adapts to their types) - RuvSense → AETHER/MERIDIAN/MAT: **Partnership** (shared embedding/geometry/tracking abstractions) --- ## Anti-Corruption Layer ### Hardware Adapter (Multistatic Sensing → wifi-densepose-hardware) ```rust /// Adapts raw ESP32 CsiFrame to RuvSense MultiBandCsiFrame pub struct MultiBandAdapter { /// Group frames by (node_id, channel) within time window window_ms: u32, /// Hardware normalizer (from MERIDIAN, ADR-027) normalizer: HardwareNormalizer, } impl MultiBandAdapter { /// Collect raw CsiFrames from one TDMA cycle and produce /// one MultiBandCsiFrame per node. pub fn adapt_cycle( &self, raw_frames: &[CsiFrame], ) -> Vec; } ``` ### Model Adapter (Pose Tracking → wifi-densepose-nn) ```rust /// Adapts DensePose model output to tracking-compatible detections pub struct PoseDetectionAdapter; impl PoseDetectionAdapter { /// Convert model output (heatmaps + offsets) to detected poses /// with keypoint positions and AETHER embeddings. pub fn adapt( &self, model_output: &ModelOutput, fused_frame: &FusedSensingFrame, ) -> Vec; } pub struct PoseDetection { pub keypoints: [[f32; 4]; 17], // [x, y, z, confidence] pub embedding: Vec, // [128] AETHER embedding pub person_cluster: PersonCluster, } ``` ### MAT Adapter (Pose Tracking → wifi-densepose-mat) ```rust /// Adapts RuvSense TrackedPose to MAT Survivor entity /// for disaster response scenarios. pub struct SurvivorAdapter; impl SurvivorAdapter { /// Convert a RuvSense TrackedPose to a MAT Survivor /// with vital signs extracted from small-motion analysis. pub fn to_survivor( &self, track: &PoseTrack, vital_signs: Option<&VitalSignsReading>, ) -> Survivor; } ``` --- ## Repository Interfaces ```rust /// Persists and retrieves pose tracks pub trait PoseTrackRepository { fn save(&self, track: &PoseTrack); fn find_by_id(&self, id: &TrackId) -> Option; fn find_active(&self) -> Vec; fn find_lost(&self) -> Vec; fn remove(&self, id: &TrackId); } /// Persists coherence state for long-term analysis pub trait CoherenceRepository { fn save_snapshot(&self, state: &CoherenceState, timestamp_us: u64); fn load_latest(&self) -> Option; fn load_history(&self, duration_ms: u64) -> Vec<(u64, f32)>; } /// Persists mesh topology and node health pub trait MeshRepository { fn save_node(&self, node_id: u8, position: [f32; 3], health: NodeHealth); fn load_topology(&self) -> Vec<(u8, [f32; 3], NodeHealth)>; fn save_schedule(&self, schedule: &SensingSchedule); fn load_schedule(&self) -> Option; } ``` --- ## Invariants ### Multistatic Sensing - At least 2 nodes must be active for multistatic fusion (fallback to single-node mode otherwise) - Channel hop sequence must contain at least 1 non-overlapping channel - TDMA cycle period must be ≤50ms for 20 Hz output - Guard interval must be ≥2× clock drift budget (≥1ms for 50ms cycle) ### Coherence - Reference template must be recalculated every 10 minutes during quiet periods - Gate threshold must be calibrated per-environment (initial defaults: accept=0.85, drift=0.5) - Stale count must not exceed max_stale (200 frames = 10s) without triggering recalibration - Static/dynamic decomposition must preserve energy: ||S|| + ||D|| ≈ ||C|| ### Pose Tracking - Exactly one Kalman predict step per output frame (20 Hz, regardless of measurement availability) - Birth gate: track not promoted to Active until 2 consecutive measurement updates - Loss threshold: track marked Lost after 5 consecutive missed measurements - Re-ID window: Lost tracks eligible for re-identification for 5 seconds - Embedding EMA decay: 0.95 (slow adaptation preserves identity across environmental changes) - Joint assignment cost must use both position (60%) and embedding (40%) terms --- ## Part II: Persistent Field Model Bounded Contexts (ADR-030) ### Ubiquitous Language (Extended) | Term | Definition | |------|------------| | **Field Normal Mode** | The room's electromagnetic eigenstructure — stable propagation baseline when unoccupied | | **Body Perturbation** | Structured change to field caused by a person, after environmental drift is removed | | **Environmental Mode** | Principal component of baseline variation due to temperature, humidity, time-of-day | | **Personal Baseline** | Per-person rolling statistical profile of biophysical proxies over days/weeks | | **Drift Event** | Statistically significant deviation from personal baseline (>2sigma for >3 days) | | **Drift Report** | Traceable evidence package: z-score, direction, window, supporting embeddings | | **Risk Signal** | Actionable observation about biophysical change — not a diagnosis | | **Intention Lead Signal** | Pre-movement dynamics (lean, weight shift) detected 200-500ms before visible motion | | **Occupancy Volume** | Low-resolution 3D probabilistic density field from RF tomography | | **Room Fingerprint** | HNSW-indexed embedding characterizing a room's electromagnetic identity | | **Transition Event** | Person exiting one room and entering another, matched by embedding similarity | --- ### 4. Field Model Context **Responsibility:** Learn and maintain the room's electromagnetic baseline. Decompose all CSI observations into environmental drift, body perturbation, and anomalies. Provide the foundation for all downstream exotic capabilities. ``` ┌──────────────────────────────────────────────────────────┐ │ Field Model Context │ ├──────────────────────────────────────────────────────────┤ │ │ │ ┌───────────────┐ ┌───────────────┐ │ │ │ Calibration │ │ Mode │ │ │ │ Collector │ │ Extractor │ │ │ │ (empty-room │ │ (SVD on │ │ │ │ CSI frames) │ │ baseline) │ │ │ └───────┬───────┘ └───────┬───────┘ │ │ │ │ │ │ └────────┬───────────┘ │ │ ▼ │ │ ┌────────────────┐ │ │ │ Perturbation │ │ │ │ Extractor │ │ │ │ (subtract │ │ │ │ baseline + │──▶ BodyPerturbation │ │ │ project out │ │ │ │ env modes) │ │ │ └────────┬───────┘ │ │ │ │ │ ┌────────▼───────┐ │ │ │ RF Tomographer│ │ │ │ (sparse 3D │──▶ OccupancyVolume │ │ │ inversion) │ │ │ └────────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘ ``` **Aggregates:** - `FieldNormalMode` (Aggregate Root) **Value Objects:** - `BodyPerturbation` — Per-link CSI residual after baseline + environmental mode removal - `EnvironmentalMode` — One principal component of baseline variation - `OccupancyVolume` — 3D voxel grid of estimated mass density - `CalibrationStatus` — Fresh / Stale / Expired (based on time since last empty-room) **Domain Services:** - `CalibrationService` — Detects empty-room windows, collects calibration data - `ModeExtractionService` — SVD computation for environmental modes - `PerturbationService` — Baseline subtraction + mode projection - `TomographyService` — Sparse L1 inversion for occupancy volume **RuVector Integration:** - `ruvector-solver` → SVD for mode extraction; L1 for tomographic inversion - `ruvector-temporal-tensor` → Baseline history compression - `ruvector-attn-mincut` → Mode-subcarrier assignment partitioning --- ### 5. Longitudinal Monitoring Context **Responsibility:** Maintain per-person biophysical baselines over days/weeks. Detect meaningful drift. Produce traceable evidence reports. Enforce the signals-not-diagnoses boundary. ``` ┌──────────────────────────────────────────────────────────┐ │ Longitudinal Monitoring Context │ ├──────────────────────────────────────────────────────────┤ │ │ │ ┌───────────────┐ ┌───────────────┐ │ │ │ Metric │ │ Baseline │ │ │ │ Extractor │ │ Updater │ │ │ │ (pose → gait,│ │ (Welford │ │ │ │ stability, │ │ online │ │ │ │ breathing) │ │ statistics) │ │ │ └───────┬───────┘ └───────┬───────┘ │ │ │ │ │ │ └────────┬───────────┘ │ │ ▼ │ │ ┌────────────────┐ │ │ │ Drift Detector│ │ │ │ (z-score vs │ │ │ │ personal │ │ │ │ baseline) │ │ │ └────────┬───────┘ │ │ ▼ │ │ ┌────────────────┐ │ │ │ Evidence │ │ │ │ Assembler │──▶ DriftReport │ │ │ (embeddings + │ │ │ │ timestamps + │ │ │ │ graph links) │ │ │ └────────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘ ``` **Aggregates:** - `PersonalBaseline` (Aggregate Root) **Entities:** - `DailyMetricSummary` — One day's worth of compressed metric statistics per person **Value Objects:** - `DriftReport` — Evidence package with z-score, direction, window, embeddings - `DriftMetric` — GaitSymmetry / StabilityIndex / BreathingRegularity / MicroTremor / ActivityLevel - `DriftDirection` — Increasing / Decreasing - `MonitoringLevel` — Physiological (Level 1) / Drift (Level 2) / RiskCorrelation (Level 3) - `WelfordStats` — Online mean/variance accumulator (count, mean, M2) **Domain Services:** - `MetricExtractionService` — Extract biomechanical proxies from pose tracks - `BaselineUpdateService` — Update Welford statistics with daily observations - `DriftDetectionService` — Compute z-scores, identify significant deviations - `EvidenceAssemblyService` — Package supporting embeddings and graph constraints **RuVector Integration:** - `ruvector-temporal-tensor` → Compressed daily summary storage - `ruvector-attention` → Weight metric significance in drift score - `ruvector-mincut` → Temporal changepoint detection in metric series - HNSW → Similarity search across longitudinal embedding record **Invariants:** - Baseline requires 7+ observation days before drift detection activates - Drift alert requires >2sigma deviation sustained for >3 consecutive days - Evidence chain must include start/end embeddings bracketing the drift window - System never outputs diagnostic language — only metric values and deviations - Personal baseline decay: Welford stats use full history (no windowing) for stability --- ### 6. Spatial Identity Context **Responsibility:** Maintain cross-room identity continuity via environment fingerprinting and transition graphs. Track who is where across spaces without storing images. ``` ┌──────────────────────────────────────────────────────────┐ │ Spatial Identity Context │ ├──────────────────────────────────────────────────────────┤ │ │ │ ┌───────────────┐ ┌───────────────┐ │ │ │ Room │ │ Transition │ │ │ │ Fingerprint │ │ Detector │ │ │ │ Index (HNSW) │ │ (exit/entry │ │ │ └───────┬───────┘ │ events) │ │ │ │ └───────┬───────┘ │ │ │ │ │ │ └────────┬───────────┘ │ │ ▼ │ │ ┌────────────────┐ │ │ │ Cross-Room │ │ │ │ Matcher │ │ │ │ (exit embed ↔ │──▶ TransitionEvent │ │ │ entry embed) │ │ │ └────────┬───────┘ │ │ │ │ │ ┌────────▼───────┐ │ │ │ Transition │ │ │ │ Graph │ │ │ │ (rooms, │ │ │ │ persons, │ │ │ │ timestamps) │ │ │ └────────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘ ``` **Aggregates:** - `SpatialIdentityGraph` (Aggregate Root) **Entities:** - `RoomProfile` — HNSW-indexed electromagnetic fingerprint of a room - `PersonSpatialRecord` — Which rooms a person has visited, in order **Value Objects:** - `TransitionEvent` — Person, from_room, to_room, timestamps, embedding similarity - `RoomFingerprint` — 128-dim AETHER embedding of the room's CSI profile - `SpatialContinuity` — Confidence score for cross-room identity chain **Domain Services:** - `RoomFingerprintService` — Compute and index room electromagnetic profiles - `TransitionDetectionService` — Detect exits (track lost near boundary) and entries (new track) - `CrossRoomMatchingService` — HNSW similarity between exit and entry embeddings - `TransitionGraphService` — Build and query the room-person-time graph **RuVector Integration:** - HNSW → Room and person fingerprint similarity search - `ruvector-mincut` → Transition graph partitioning for occupancy analysis **Invariants:** - Cross-room match requires >0.80 cosine similarity AND <60s temporal gap - Room fingerprint must be recalculated if mesh topology changes - Transition graph edges are immutable once created (append-only audit trail) - No image data stored — only 128-dim embeddings and structural events --- ### Domain Events (Extended) #### Field Model Events ```rust pub enum FieldModelEvent { /// Baseline calibration completed (empty room detected and measured) BaselineCalibrated { room_id: RoomId, n_modes: usize, variance_explained: f32, // fraction of total variance captured timestamp_us: u64, }, /// Environmental drift detected (baseline shift without body cause) EnvironmentalDriftDetected { room_id: RoomId, magnitude: f32, drift_type: DriftProfile, timestamp_us: u64, }, /// Anomalous perturbation detected (does not match body or environment) AnomalousPerturbation { room_id: RoomId, anomaly_score: f32, affected_links: Vec, timestamp_us: u64, }, /// Occupancy volume updated OccupancyUpdated { room_id: RoomId, occupied_voxels: usize, total_voxels: usize, timestamp_us: u64, }, } ``` #### Longitudinal Monitoring Events ```rust pub enum LongitudinalEvent { /// Personal baseline established (7-day calibration complete) BaselineEstablished { person_id: PersonId, observation_days: u32, metrics_tracked: Vec, timestamp_us: u64, }, /// Drift detected — biophysical metric significantly changed DriftDetected { person_id: PersonId, report: DriftReport, timestamp_us: u64, }, /// Drift resolved — metric returned to baseline range DriftResolved { person_id: PersonId, metric: DriftMetric, resolution_days: u32, timestamp_us: u64, }, /// Daily summary computed for a person DailySummaryComputed { person_id: PersonId, date: u64, // day timestamp metrics: Vec<(DriftMetric, f32)>, // metric, today's value timestamp_us: u64, }, } ``` #### Spatial Identity Events ```rust pub enum SpatialEvent { /// New room fingerprinted RoomFingerprinted { room_id: RoomId, fingerprint_dims: usize, timestamp_us: u64, }, /// Person transitioned between rooms PersonTransitioned { person_id: PersonId, from_room: RoomId, to_room: RoomId, similarity: f32, gap_duration_ms: u64, timestamp_us: u64, }, /// Cross-room match failed (new person in destination room) CrossRoomMatchFailed { entry_room: RoomId, entry_embedding: Vec, candidates_checked: usize, best_similarity: f32, timestamp_us: u64, }, } ``` --- ### Extended Context Map ``` ┌──────────────────────────────────────────────────────────────────────┐ │ RuvSense Full System (ADR-029 + ADR-030) │ ├──────────────────────────────────────────────────────────────────────┤ │ │ │ ┌───────────────┐ FusedFrame ┌──────────────┐ │ │ │ Multistatic │────────────▶│ Pose Tracking │ │ │ │ Sensing │ │ Context │ │ │ └───────┬───────┘ └───────┬───────┘ │ │ │ │ │ │ │ │ TrackedPose │ │ │ │ │ │ ▼ ┌───────▼───────┐ │ │ ┌───────────────┐ │ Longitudinal │ │ │ │ Coherence │ │ Monitoring │ │ │ │ Context │ │ Context │ │ │ └───────┬───────┘ └───────┬───────┘ │ │ │ Gates │ DriftReport │ │ │ │ │ │ ▼ ▼ │ │ ┌───────────────┐ ┌───────────────┐ │ │ │ Field Model │ │ Spatial │ │ │ │ Context │ │ Identity │ │ │ │ (baseline, │ │ Context │ │ │ │ modes, │ │ (cross-room, │ │ │ │ tomography) │ │ transitions)│ │ │ └───────────────┘ └───────────────┘ │ │ │ │ ──────────────── Event Bus ────────────────── │ │ SensingEvent | CoherenceEvent | TrackingEvent | │ │ FieldModelEvent | LongitudinalEvent | SpatialEvent │ │ │ ├──────────────────────────────────────────────────────────────────────┤ │ UPSTREAM: wifi-densepose-{hardware, nn, signal} │ │ SIBLINGS: AETHER (embeddings) | MERIDIAN (geometry) | MAT (triage) │ └──────────────────────────────────────────────────────────────────────┘ ``` **New Relationship Types:** - Multistatic Sensing → Field Model: **Partnership** (sensing provides raw CSI; field model provides perturbation extraction) - Pose Tracking → Longitudinal Monitoring: **Customer/Supplier** (tracking provides daily pose metrics; monitoring builds baselines) - Pose Tracking → Spatial Identity: **Customer/Supplier** (tracking provides track exit/entry events; spatial maintains transition graph) - Coherence → Field Model: **Subscriber** (coherence events inform baseline recalibration) - Longitudinal Monitoring → Spatial Identity: **Partnership** (person profiles shared for cross-room matching) --- ### Extended Repository Interfaces ```rust /// Persists field normal modes and calibration history pub trait FieldModelRepository { fn save_baseline(&self, room_id: RoomId, mode: &FieldNormalMode); fn load_baseline(&self, room_id: RoomId) -> Option; fn list_rooms(&self) -> Vec; fn save_occupancy_snapshot(&self, room_id: RoomId, volume: &OccupancyVolume, timestamp_us: u64); } /// Persists personal baselines and drift history pub trait LongitudinalRepository { fn save_baseline(&self, baseline: &PersonalBaseline); fn load_baseline(&self, person_id: &PersonId) -> Option; fn save_daily_summary(&self, person_id: &PersonId, summary: &DailyMetricSummary); fn load_summaries(&self, person_id: &PersonId, days: u32) -> Vec; fn save_drift_report(&self, report: &DriftReport); fn load_drift_history(&self, person_id: &PersonId) -> Vec; } /// Persists room fingerprints and transition graphs pub trait SpatialIdentityRepository { fn save_room_fingerprint(&self, room_id: RoomId, fingerprint: &RoomFingerprint); fn load_room_fingerprint(&self, room_id: RoomId) -> Option; fn save_transition(&self, transition: &TransitionEvent); fn load_transitions(&self, person_id: &PersonId, window_ms: u64) -> Vec; fn load_room_occupancy(&self, room_id: RoomId) -> Vec; } ``` --- ### Extended Invariants #### Field Model - Baseline calibration requires ≥10 minutes of empty-room CSI (≥12,000 frames at 20 Hz) - Environmental modes capped at K=5 (more modes overfit to noise) - Tomographic inversion only valid with ≥8 links (4 nodes minimum) - Baseline expires after 24 hours if not refreshed during quiet period - Perturbation energy must be non-negative (enforced by magnitude computation) #### Longitudinal Monitoring - Personal baseline requires ≥7 observation days before drift detection activates - Drift alert requires >2sigma deviation sustained for ≥3 consecutive days - Evidence chain must include embedding pairs bracketing the drift window - Output must never use diagnostic language — only metric values and statistical deviations - Daily summaries stored for ≥90 days (rolling retention policy) - Welford statistics use full history (no windowing) for maximum stability #### Spatial Identity - Cross-room match requires >0.80 cosine similarity AND <60s temporal gap - Room fingerprint recalculated when mesh topology changes (node added/removed/moved) - Transition graph is append-only (immutable audit trail) - No image data stored — only 128-dim embeddings and structural events - Maximum 100 rooms indexed per deployment (HNSW scaling constraint)