bench+test: engine per-cycle benchmark + ADR-142 acceptance path

- engine: criterion benchmark engine_cycle — full process_cycle (4 nodes / 56
  subcarriers) measured at ~6.35 us/cycle, ~7800x under the 50ms (20Hz) budget.
- signal: ADR-142 acceptance test — 3 links drift 30 frames -> ChangePoint ->
  VoxelMap accumulates -> low-confidence voxels suppressed -> VoxelGate
  Restricted emits histogram only -> ADR-137 contradiction recorded.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
ruv 2026-05-29 08:42:46 -04:00
parent 020aa08049
commit 95bdd37e76
4 changed files with 108 additions and 0 deletions

1
v2/Cargo.lock generated
View File

@ -10657,6 +10657,7 @@ name = "wifi-densepose-engine"
version = "0.3.0"
dependencies = [
"blake3",
"criterion",
"wifi-densepose-bfld",
"wifi-densepose-core",
"wifi-densepose-geo",

View File

@ -20,6 +20,13 @@ wifi-densepose-geo = { path = "../wifi-densepose-geo" }
# Deterministic witness over the trust decision (ADR-137 §2.7 / ADR-028).
blake3 = { version = "1.5", default-features = false }
[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
[[bench]]
name = "engine_cycle"
harness = false
[lints.rust]
unsafe_code = "forbid"
missing_docs = "warn"

View File

@ -0,0 +1,52 @@
//! Criterion benchmark for the RuView streaming-engine hot path.
//!
//! The live system runs at 20 Hz → a **50 ms** wall-clock budget per cycle.
//! This measures one full [`StreamingEngine::process_cycle`] (fuse + quality
//! scoring + calibration provenance + privacy gate + WorldGraph semantic node)
//! for a 4-node / 56-subcarrier mesh — the realistic ESP32-S3 HT20 case.
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use wifi_densepose_bfld::PrivacyMode;
use wifi_densepose_engine::StreamingEngine;
use wifi_densepose_geo::types::GeoRegistration;
use wifi_densepose_signal::hardware_norm::{CanonicalCsiFrame, HardwareType};
use wifi_densepose_signal::ruvsense::fusion_quality::CalibrationId;
use wifi_densepose_signal::ruvsense::MultiBandCsiFrame;
fn node_frame(node_id: u8, ts_us: u64, n_sub: usize) -> MultiBandCsiFrame {
MultiBandCsiFrame {
node_id,
timestamp_us: ts_us,
channel_frames: vec![CanonicalCsiFrame {
amplitude: (0..n_sub).map(|i| 1.0 + 0.1 * i as f32).collect(),
phase: (0..n_sub).map(|i| i as f32 * 0.05).collect(),
hardware_type: HardwareType::Esp32S3,
}],
frequencies_mhz: vec![2412],
coherence: 0.9,
}
}
fn bench_cycle(c: &mut Criterion) {
let frames: Vec<MultiBandCsiFrame> =
(0..4).map(|i| node_frame(i, 1000 + u64::from(i), 56)).collect();
c.bench_function("process_cycle_4nodes_56sc", |b| {
b.iter_batched(
|| {
let mut e =
StreamingEngine::new(PrivacyMode::PrivateHome, 1, GeoRegistration::default());
let room = e.add_room("living_room", "Living Room");
e.add_sensor("esp32-com9", room);
(e, room)
},
|(mut e, room)| {
e.process_cycle(&frames, CalibrationId(1), room, 0).unwrap()
},
BatchSize::SmallInput,
);
});
}
criterion_group!(benches, bench_cycle);
criterion_main!(benches);

View File

@ -339,6 +339,54 @@ mod tests {
assert!(m.occupancies().iter().all(|&o| o == 0.0));
}
/// ADR-142 acceptance (the environmental-nervous-system path):
/// `three links drift for 30 frames -> ChangePoint fires -> VoxelMap
/// accumulates evidence -> low-confidence voxels suppressed -> VoxelGate
/// Restricted emits histogram only -> ADR-137 contradiction recorded`.
#[test]
fn acceptance_drift_to_histogram_with_contradiction() {
use crate::ruvsense::fusion_quality::ContradictionFlag;
// Three links, change-point requires all three to diverge at once.
let mut tracker = EvolutionTracker::new(3, 2.0, 3);
// 30 jittered baseline frames (non-zero std so divergence is defined).
for i in 0..30u32 {
let j = if i % 2 == 0 { 0.99 } else { 1.01 };
assert!(tracker.observe_window(&[j, j, j]).is_none(), "baseline is quiet");
}
// Three links drift simultaneously → ChangePoint fires.
let cp = tracker
.observe_window(&[5.0, 5.0, 5.0])
.expect("simultaneous drift on 3 links must fire a change-point");
assert_eq!(cp.diverging_links, 3);
// VoxelMap accumulates evidence over repeated observations.
let mut map = TemporalVoxelMap::new(vec![[0.0; 3], [1.0; 3], [2.0; 3]]);
for ns in 0..6 {
map.observe(0, 0.95, Some(0.4), ns);
map.observe(1, 0.90, None, ns);
// voxel 2 deliberately under-observed.
}
assert!(map.voxel(0).unwrap().occupancy > 0.9, "evidence accumulated");
// Low-confidence voxels (under 5 frames) are suppressed from output.
let low = map.low_confidence_indices();
assert!(low.contains(&2) && !low.contains(&0), "voxel 2 suppressed, voxel 0 kept");
// ADR-137 contradiction recorded from the change-point (drift conflict).
let contradictions = vec![ContradictionFlag::DriftProfileConflict {
node_idx: 0,
drift_score: cp.diverging_links as f32,
}];
assert!(!contradictions.is_empty(), "change-point recorded as an ADR-137 contradiction");
// VoxelGate Restricted → histogram only; the raw map never leaves the node.
let hist = VoxelGate::demote(&mut map, VoxelPrivacy::Restricted, 4)
.expect("Restricted yields an occupancy histogram");
assert_eq!(hist.iter().sum::<u32>(), 3, "all voxels binned");
assert!(map.occupancies().iter().all(|&o| o == 0.0), "raw occupancy cleared");
}
#[test]
fn evolution_tracker_detects_cross_link_change_point() {
let mut t = EvolutionTracker::with_defaults(4);