mirror of
https://github.com/ruvnet/RuView
synced 2026-06-20 12:03:19 +00:00
c84ea39e62d14dcafe61fc80d357dfe0349462cd
29 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
aeac5f5543 |
chore(worldgraph): extract geo+worldgraph+worldmodel to ruvnet/worldgraph submodule
- published as github.com/ruvnet/worldgraph (3-crate workspace, history via git-filter-repo) - replace the 3 in-tree crates with one submodule at v2/crates/worldgraph - parent workspace: drop the 3 members, exclude the submodule (it is its own workspace), repoint workspace.dependencies(worldmodel) + engine/sensing-server path-deps into it - cargo metadata resolves clean (geo/worldgraph/worldmodel consumed from the submodule) Co-Authored-By: claude-flow <ruv@ruv.net> |
||
|
|
f250149e94 |
feat(ADR-262 P1): wifi-densepose-rufield bridge — RuView sensing → signed RuField FieldEvents (fail-closed privacy map) (#1070)
* feat(rufield): ADR-262 P1 — wifi-densepose-rufield anti-corruption bridge New v2 workspace member that converts RuView WiFi-CSI sensing output into signed RuField FieldEvents. Path-deps the vendor/rufield submodule crates (rufield-core/-provenance/-privacy/-fusion); single coupling point between RuView and the standalone RuField MFS spec (ADR-262 §5.4). - SensingSnapshot: owned primitives mirroring SensingUpdate + TrustedOutput (no dependency on wifi-densepose-sensing-server). - snapshot_to_field_event(): builds a WifiCsi FieldTensor + Observation, derives a real position from the signal-field peak (never fabricated), real sha256 provenance + ed25519 signature (synthetic=false). - map_privacy() (§3.3 crux): maps by information content, NEVER byte value — Derived (byte 1) → P4/P5, never P1; fail-closed demotion floor to P2. P1 gates (tests/p1_gates.rs): round-trip serde, is_fusable verified receipt, RuFieldFusion::ingest accept + infer runs, privacy-safety (Derived never P1), full §3.3 table, fail-closed demotion, determinism, no-fabricated-position. 15 tests pass (5 unit + 9 integration + 1 doc), 0 failed. Honesty: P1 plumbing (tested conversion + safe privacy mapping), NOT wired into the live server (P3) and NOT an accuracy claim. Co-Authored-By: claude-flow <ruv@ruv.net> * docs(adr-262): mark P1 implemented + CI submodules:recursive + CHANGELOG/CLAUDE - ADR-262 Status → "Proposed — P1 implemented"; add §0.1 Implementation status (the bridge crate + the five P1 gates that pass; defers the provenance-carrier reuse, P3 live wiring, and P4 multi-modality). - ci.yml: add `submodules: recursive` to the rust-tests checkout so the new crate's `vendor/rufield` path-deps resolve in CI (they fail otherwise even though the workspace build passes locally with the submodule present). - CHANGELOG [Unreleased]: P1 bridge entry (kept alongside the upstream ADR-262 research entry). - CLAUDE.md: crate table row for `wifi-densepose-rufield`. Co-Authored-By: claude-flow <ruv@ruv.net> |
||
|
|
17471e93ff |
ADR-152: WiFi-Pose SOTA 2026 intake — WiFlow-STD benchmark, Rust integrations, ADR-153 802.11bf layer, efficiency frontier (#1008)
* feat(calibration): NodeGeometry transceiver-geometry recording (ADR-152 §2.1.1) PerceptAlign-motivated geometry capture at enrollment: per-node optional records (position, antenna orientation, inter-node distances, acquisition method) — recorded when known, never required. Event-sourced via EnrollmentEvent::GeometryRecorded (latest recording wins); persisted on SpecialistBank with serde defaults so pre-ADR-152 bank JSON loads cleanly (fixture-proven, and geometry-free banks serialize byte-shape-identical to the old schema); threaded through MultiNodeMixture as data only — the learned geometry embeddings and algorithmic fusion use are §2.1.2, deliberately deferred until the ADR-151 P6 LoRA heads exist. Geometry recorded from now on means banks captured today remain usable for layout-conditioned training later — you can't retroactively add geometry to data you didn't record. 8 new tests (3 geometry, 2 anchor, 2 bank, 1 multistatic) + full-loop extension (2-node geometry, one tape-measured + one unknown, surviving the bank JSON round-trip the runtime loads from). 50/50 calibration (both feature configs) + 23 CLI tests green. Co-Authored-By: RuFlo <ruv@ruv.net> * feat(training): two-checkerboard camera↔room calibration for ADR-079 labels (ADR-152 §2.1.3) Defends the camera-supervised pipeline against PerceptAlign's "coordinate overfitting": MediaPipe keypoints were emitted in raw camera coordinates with no shared frame and no transceiver-geometry metadata — the exact label shape that memorizes deployment layout and collapses cross-layout. - scripts/calibrate-camera-room.py + calibration_lib.py: OpenCV two-checkerboard calibration → versioned bundle JSON (intrinsics, camera→room extrinsics, checkerboard spec, transceiver geometry, sha256 calibration_id). Intrinsics resolve from file > cache > multi-view computation > loud-warning 2-view fallback. - collect-ground-truth.py --calibration <bundle>: every sample gains keypoints_room (unit bearing rays from the camera center in the room frame — documented projective alignment; raw image coords preserved so training chooses), camera_origin_room, calibration_id, and the transceiver geometry stamp. Without the flag, output is byte-identical to before (tested) + a one-line ADR-152 warning. Design finding (recorded for ADR-152): a single planar checkerboard's corner grid is centrosymmetric — the reversed corner ordering fits a ghost camera pose with IDENTICAL reprojection error, so per-board flip disambiguation is mathematically ill-posed. solve_two_board_extrinsics solves the joint wall+floor set over all 4 flip combinations, where the minimum is unique — an independent reason the TWO-checkerboard method is required, beyond what PerceptAlign states. 15 headless pytest tests green (synthetic corners: extrinsics recovery incl. ghost resolution, bundle round-trip + hash stability, ray transforms w/ distortion + cross-resolution, no-calibration byte identity). Co-Authored-By: RuFlo <ruv@ruv.net> * feat(benchmarks): WiFlow-STD reproduction harness + measurement (a) results (ADR-152 §2.2) Shipped checkpoint REFUTED (0.08% PCK@20, wrong keypoint normalization); 6 reproducibility defects documented (broken imports, corrupted dataset tail with float32-max garbage that NaN-poisons fp16 BatchNorm, unreachable test phase). After repairs, retraining with upstream defaults reproduces 96.09% PCK@20 full-test / 96.61% corruption-free (published 97.25%) on RTX 5080. Claims graded MEASURED-EQUIVALENT; 2.23M params + ~0.055 GFLOPs verified. Third-party code/weights/data stay out of tree (gitignored). Co-Authored-By: claude-flow <ruv@ruv.net> * feat: ADR-152 Rust integrations + ADR-153 802.11bf protocol model - calibration: GeometryEmbedding — 32-slot permutation-invariant NodeGeometry featurization for future LoRA-head conditioning (ADR-152 §2.1.2); derived SpecialistBank::geometry_embedding() accessor; 59 tests - train: MaePretrainConfig + patchify/random-mask with UNSW measured recipe (80% masking, (30,3) patches; ADR-152 §2.3, arXiv 2511.18792); strict no-truncate/no-NaN policy; proptest properties - train: WiFlowStdModel — tch-gated port of the verified ~96%-PCK@20 WiFlow-STD architecture (ADR-152 §2.2 beyond-SOTA); ungated param formula pinned to 2,225,042; 15/17-keypoint support; 239 crate tests - hardware: ieee80211bf forward-compatibility protocol model (ADR-153): SpecProfile gates, SensingCapabilities negotiation, required ConsentMode, session FSM, SensingTransport + SimTransport + OpportunisticCsiBridge; full acceptance checklist covered; 156+4 tests - deps: ruvector bumps per ADR-152 §2.6 survey (mincut/solver 2.0.6, attention 2.1.0, gnn 2.2.0); vendor/ruvector synced to a083bd77f - docs: ADR-153 accepted; ADR-152 §2.2 status, §2.4 amendment, §2.6 added Workspace: 162 test suites green (--no-default-features); Python proof PASS. Known pre-existing flake: homecore-api env_empty_falls_back_to_defaults (unserialized env-var mutation) — untouched, follow-up. Co-Authored-By: claude-flow <ruv@ruv.net> * docs: CHANGELOG + CLAUDE.md entries for ADR-152 integrations and ADR-153 Co-Authored-By: claude-flow <ruv@ruv.net> * fix(train): repair tch-backend bit-rot — gated path compiles and tests run again Mechanical API refresh against current tch: Vec::from(Tensor) -> try_from (+ explicit flatten), numel() usize cast, Rem/div ops -> remainder() / divide_scalar_mode(floor) — the latter fixed a silent true-division bug in heatmap argmax decoding; clamp(1.0, f64::MAX) -> clamp_min (torch 2.x scalar overflow panic); petgraph EdgeRef import; missing EvalMetrics and verify_checkpoint_dir APIs that tests documented. wiflow_std roundtrip test uses safetensors (.pt _save_parameters roundtrip broken in torch 2.11 Windows). Gated: 349 passed (incl. all 20 wiflow_std); ungated: unchanged. Known pre-existing: gaussian-heatmap convention mismatch (2 tests), proof seed race under parallel threads — documented, deliberate follow-ups. Co-Authored-By: claude-flow <ruv@ruv.net> * feat(train): WiFlow-STD PyTorch->tch weight import + numerical parity proof export_to_safetensors.py maps the retrained checkpoint (295 tensors -> 248 mapped, param sum exactly 2,225,042; num_batches_tracked dropped) into a tch-loadable safetensors plus a deterministic parity fixture. Gated #[ignore] integration test loads it strictly and asserts forward-pass agreement: max abs diff 1.192e-7 on the seed-42 fixture. dump_variable_names test makes the tch name layout authoritative. Zero architecture discrepancies found. Co-Authored-By: claude-flow <ruv@ruv.net> * fix: workflow-review findings — BN gamma init, ThresholdParams serde, init docs Concurrent validation workflow (2 review lanes + adversarial verification, 13 agents): 5 confirmed findings, 3 refuted. Fixes: - wiflow_std: pin BatchNorm gamma to 1.0 (tch default draws Uniform(0,1) — silently halves activations in from-scratch training; loaded checkpoints unaffected, parity re-verified after the change) - wiflow_std: document the conv-init divergences vs the reference's effective kaiming_normal(fan_out) re-init (from-scratch dynamics only) - ieee80211bf: ThresholdParams deserialization validates via try_from so the <=100 invariant holds for untrusted payloads (+ rejection test) Benchmarks (release, ruvzen): GeometryEmbedding 1.84us/call (542k/s), MAE tokenization 7.38us/window (135k/s), 802.11bf FSM 8.9M events/s — nothing suspicious. Co-Authored-By: claude-flow <ruv@ruv.net> * docs(adr): ADR-152 §2.1.4 gate resolved — PerceptAlign repo MIT, dataset on HF Co-Authored-By: claude-flow <ruv@ruv.net> * feat(benchmarks): edge optimization measured + measurement (b) blocked + 92.9% retraction Edge optimization (ADR-152 optimize track): ONNX Runtime fp32 is the CPU latency win (3.2 ms/window, ~3.4x faster than torch, parity 2.4e-7); ORT dynamic int8 reaches 2.44 MB (paper's ~2.2 MB claim plausible only via conv-capable toolchains; -0.16pt PCK@20, +18% MPJPE, 2x slower); torch dynamic quant converts 0% of this conv-only model; fp16 halves storage free but is slower on CPU. Measurement (b) BLOCKED-ON-DATA: only 1,077 paired ESP32 windows exist (stop rule <2k). Forensic recheck of the surviving April holdout RETRACTS the ADR-079 '92.9% PCK@20' figure: constant-output model, absolute (not torso) threshold, 69 near-static frames — mean predictor scores 100% under that protocol; torso-PCK@20 is 19.1%. Corroborates PR #535. Stale citations removed from user-guide, readme-details, ADR-152 §2.1.3; no-citation rule extended to ADR-079 accuracy claims. Unblock: >=2k-window multi-pose paired session + torso-PCK re-baseline. Co-Authored-By: claude-flow <ruv@ruv.net> * docs(user-guide): corrected camera-supervised collection tutorial Step 0 CSI-rate check + session-length math (window yield = frames/20 — the May session's 8x under-delivery was a ~12 Hz CSI rate, not an aligner bug); two-checkerboard calibration step (ADR-152 §2.1.3); pose-variety and confidence guidance; torso-normalized PCK + temporal-split + pred-variance eval protocol (lessons from the 92.9% retraction); scale presets re-keyed to realistic window counts. Co-Authored-By: claude-flow <ruv@ruv.net> * feat(benchmarks): static PTQ int8 (calibrated) results + overnight capture script Conv-only static QDQ beats dynamic int8 on accuracy (PCK@20 96.61-96.63% vs 96.52%, MPJPE +10% vs +18% over fp32) at ~equal size/latency; all-ops QDQ strictly worse (int8 activations through attention glue). Entropy calibration verified bit-identical to MinMax on this data. Deployment: ONNX fp32 for speed (3.2ms), static conv-only QDQ for smallest (2.53MB). Also: scripts/overnight-empty-capture.py — segmented UDP CSI recorder for empty-room baselines (no glob collisions, detach-safe). Co-Authored-By: claude-flow <ruv@ruv.net> * feat(benchmarks): measurement (b) MEASURED — optimization transfer only, mean-pose baseline wins WiFlow-STD fine-tuned on 2,046 fresh single-room ESP32 paired windows (temporal 70/15/15, 70->540 adapter, K=17): pretrained-init 65% PCK@20 vs scratch 0% (optimization transfer) but frozen-trunk ~0% (no feature transfer), and NOTHING beats the mean-pose baseline (95.9% PCK@20 — single subject, near-static normalized coords). Honesty gates held: pred std 0.0113 (non-constant model) but mean-baseline dominance means no citable CSI->pose capability from this data. ADR-152 open question 1 answered partially; definitive answer needs multi-subject/position data. Two new aligner findings: heterogeneous csi_shape with silent zero-padding (~20%), and extractCsiMatrix's transposed shape label (frame-major data, [nSc, nFrames] label) — fixes pending. Co-Authored-By: claude-flow <ruv@ruv.net> * feat(benchmarks): efficiency sweep MEASURED — half model dominates full reference Compact WiFlow-STD variants on the same data/split/protocol: half (843,834 params, 0.38x) strictly dominates the 2.23M reference (PCK@20 96.62 vs 96.61, PCK@50 99.47 vs 99.11, MPJPE 0.00898 vs 0.0094) — the published architecture is over-parameterized for its own benchmark. quarter (338k) 96.05%; tiny (56,290 params, 1/39.5) holds 94.11% — a ~220KB fp32 edge candidate. In-domain caveats recorded; cross-domain untested. Co-Authored-By: claude-flow <ruv@ruv.net> * feat(train): compact WiFlow-STD presets in Rust + tiny edge artifact (ADR-152) WiFlowStdConfig gains half()/quarter()/tiny() mirroring the overnight sweep exactly: TcnGroupsMode (Fixed/Gcd/Depthwise), input_pw_groups, derived stride schedule and decoder-mid (all default to upstream behavior; legacy serde JSON unaffected). Param formulas pin to trained ground truth first try: 843,834 / 338,600 / 56,290; default 2,225,042 pin and 1.192e-7 parity unchanged. 248 tests green. Tiny edge artifact (tiny_edge_bench.py): ONNX fp32 = 295 KB, 0.66 ms/win (~1,500/s CPU), 94.11% PCK@20 (matches sweep clean-test exactly; parity 1.49e-7). Static int8 is a bad trade at this scale (-1.43pt, +19% MPJPE, -16% size, slower) — recorded as negative result. Export note: width-16 breaks AdaptiveAvgPool((15,1)) TorchScript export; replaced by exact mean+matmul equivalent, proven by parity. Co-Authored-By: claude-flow <ruv@ruv.net> * fix: resolve all 10 confirmed code-review findings (7-angle review, 20/20 verified) wiflow_std: min_feature_width (default 15) replaces the keypoints->stride coupling — for_keypoints(17) now provably builds the trained [2,2,2,2] graph and pools 15->17, matching the validated Python protocol (pinned by tests); param_count() total on invalid configs; random_mask returns Result and rejects non-finite/out-of-range ratios; trainer checkpoints switched to safetensors (.pt VarStore roundtrip broken on Windows torch 2.11). ieee80211bf: SBP proxy now re-triggers instances and relays reports via Action::RelaySbpReport -> SensingFrame::SbpReport (clients consume via their existing path); missed_instances reset on success = consecutive semantics; SessionTable gains a guarded SBP entry point + unknown-id drop counter; initiator-role sessions reject inbound setup/SBP requests (RejectedNotSupported) closing the idle hijack; StartSetup/StartSbp outside Idle return InvalidStateForCommand; SBP validation unified through evaluate_setup with a 1:1 SetupStatus->SbpStatus mapping. events.rs split out to honor the 500-line cap. calibration/cli: enrollment geometry now actually reaches trained banks — both production call sites attach .with_geometry; --geometry flag on train-room and POST /enroll/geometry + train-body geometry on calibrate-serve give production a recording surface; geometry-free banks log the ADR-152 §2.1.2 note. benchmarks: corruption masks committed as ground truth (unregenerable after in-place cleaning; verified bit-identical regeneration from the pristine copy) + generate_corruption_masks.py producer; _bench_common.py dedups the 5x-copied shim/evaluate/seed/remap (post-refactor PCK@20 re-verified equal to the last digit); remote scripts get the mmap patch; tiny_edge --calib validated multiple-of-64; onnx_bench --help no longer executes (and overwrote) the export — artifact restored byte-exact. Workspace: 2,963 tests passed, 0 failed; Python proof PASS. Co-Authored-By: claude-flow <ruv@ruv.net> * ci: build workspace tests without debuginfo — runner disk exhaustion The combined 38-crate debug target exceeds the GitHub runner's disk ('final link failed: No space left on device'); the same tree measured 151GB locally with full debuginfo. CARGO_PROFILE_{DEV,TEST}_DEBUG=0 shrinks the target ~5-10x; debuginfo serves no purpose in CI test runs. Co-Authored-By: claude-flow <ruv@ruv.net> |
||
|
|
2a307138f2 |
feat: per-room calibration system (ADR-151) + cognitum-v0 appliance integration spec (#989)
* docs(adr): ADR-151 — Per-Room Calibration & Specialized Model Training Room-first calibration -> bank of small specialised ruVector models (breathing, heartbeat, restlessness, posture, presence, anomaly) distilled from the frozen Hugging-Face-published RF Foundation Encoder (ADR-150). Four-stage local-first pipeline: baseline (ADR-135 environmental fingerprint) -> guided enrollment (NEW EnrollmentProtocol, clean anchors not hours) -> feature extraction (reuse signal_features + ruvsense) -> specialist bank training (rapid_adapt LoRA heads, RVF storage, HNSW prototypes). Invariants: specialisation over scale; local heads over a shared public base; honest STALE degradation on baseline drift. Indexes ADR-149/150/151. Co-Authored-By: claude-flow <ruv@ruv.net> * feat(cli): calibration HTTP API for UI-driven baseline capture (ADR-135/151) Adds `wifi-densepose calibrate-serve` — an Axum HTTP API that wraps the ADR-135 CalibrationRecorder so a UI (or any client) can drive an empty-room baseline capture remotely. Stage 1 ("teach the room") of the ADR-151 room calibration & training pipeline. A single background task owns the UDP socket (ESP32 0xC511_0001 frames) and the optional active recorder; HTTP handlers talk to it over an mpsc command channel and read a shared status snapshot, keeping the &mut recorder lock-free. CORS permissive so a browser UI can call it. Endpoints (/api/v1/calibration/*): GET /health liveness + UDP ingest stats (frames_seen, streaming) POST /start { tier?, duration_s?, room_id?, min_frames? } GET /status live progress (state, frames, progress, z, eta) — poll for UI POST /stop finalize the current session early GET /result finalized baseline summary (amp/phase-dispersion averages) GET /baselines list persisted baseline .bin files Reuses the existing calibrate.rs ESP32 wire parser (made pub(crate)); honest abort when <10 frames arrive in the window (e.g. ESP32 not streaming). Verified end-to-end over loopback: start -> 300 replayed HT20 frames -> state=complete, 52-subcarrier baseline, phase_dispersion_avg=0.00096 (concentrated/valid), persisted to disk; all 6 endpoints exercised. CLI: 19 tests pass; crate builds clean. Co-Authored-By: claude-flow <ruv@ruv.net> * test(cli): firewall-free CSI UDP relay for local Windows ESP32 testing Windows Defender blocks inbound LAN UDP to a freshly-built binary without an admin allow-rule; python.exe is already allowed. This relay binds the public CSI port and forwards each datagram verbatim to a loopback port where `calibrate-serve --udp-bind 127.0.0.1 --udp-port 5006` listens (loopback is firewall-exempt). No admin required. Validated: ESP32-format 0xC5110001 frames -> :5005 -> relay -> :5006 -> calibrate-serve -> state=complete, 52-subcarrier baseline, phase_dispersion_avg=0.00098 (clean). Completes the no-admin live-test path. Co-Authored-By: claude-flow <ruv@ruv.net> * docs(changelog): record ADR-151 calibration API (calibrate-serve) Co-Authored-By: claude-flow <ruv@ruv.net> * feat(calibration): ADR-151 Stages 2–5 — enrollment, extraction, specialist bank, runtime New crate wifi-densepose-calibration implementing the per-room pipeline beyond Stage-1 baseline: - anchor.rs: guided-anchor sequence + event-sourced EnrollmentSession (Stage 2) - enrollment.rs: AnchorQualityGate + AnchorRecorder — gates anchors against the ADR-135 baseline deviation (presence/motion), re-prompts bad captures - extract.rs: Features + AnchorFeature — autocorrelation periodicity (breathing/ HR bands), variance/motion (Stage 3) - specialist.rs: 6 small room-calibrated models — presence (learned threshold), posture (nearest-prototype), breathing/heartbeat (band periodicity), restlessness (calm/active normalization), anomaly (novelty vs anchors) (Stage 4) - bank.rs: SpecialistBank — train/persist + baseline-drift STALE invalidation - runtime.rs: MixtureOfSpecialists — presence short-circuit + anomaly veto + stale flagging (Stage 5) Statistical heads make the pipeline runnable/validatable today; the ADR-150 HF RF Foundation Encoder backbone is the documented upgrade path. 29 unit tests pass. Co-Authored-By: claude-flow <ruv@ruv.net> * feat(cli): wire ADR-151 enroll / train-room / room-status / room-watch Integrates the wifi-densepose-calibration crate into the CLI as four subcommands driving the full Stage 2–5 pipeline against a live ESP32 raw-CSI stream (edge_tier=0): - enroll: walks the guided anchor sequence, gates each capture against the ADR-135 baseline deviation (re-prompts bad anchors), writes labelled features - train-room: fits the SpecialistBank from the enrollment, persists JSON - room-status: prints a trained bank's summary - room-watch: live mixture-of-specialists readout (presence/posture/breathing/ heart/restless) over a rolling window, with anomaly veto + STALE flagging Per-frame scalar is the mean CSI amplitude (carries presence/motion + breathing modulation). Validated end-to-end on the live ESP32 (COM8, edge_tier=0): the real parser → feature extraction → runtime detected breathing (~16–31 BPM) on hardware. Full multi-anchor enrollment accuracy requires the operator to perform the poses; phase-based breathing extraction is a noted refinement. 48 tests pass (29 calibration + 19 CLI). Co-Authored-By: claude-flow <ruv@ruv.net> * docs(adr-151): mark Stages 1–5 implemented; expand CHANGELOG Co-Authored-By: claude-flow <ruv@ruv.net> * fix(cli): keep proven mean-amplitude carrier for room features The max-variance-subcarrier carrier locked onto motion artifacts (not breathing) and also had an out-of-bounds bug on variable CSI subcarrier counts. Reverted to the mean-amplitude carrier, which is validated live to detect breathing. Phase-based extraction on a stable subcarrier remains the proper higher-SNR refinement (ADR-151 §4). Co-Authored-By: claude-flow <ruv@ruv.net> * feat(calibration): multistatic fusion of co-located nodes (ADR-029/151) MultiNodeMixture fuses several co-located nodes (each with its own room-calibrated SpecialistBank) into one RoomState: - presence: OR across nodes (any node seeing a person wins) - posture/breathing/heartbeat: highest-confidence node (best viewpoint) - restlessness/anomaly: max across nodes - veto: any node's physically-implausible signal vetoes the room's vitals (anti-hallucination, same as single-node runtime) + presence short-circuit - stale: any node's STALE flag propagates Same-room multistatic only; cross-room is federation (ADR-105), not fusion. 6 unit tests (presence OR, best-confidence breathing, single-node veto, staleness). 35 calibration tests pass. Co-Authored-By: claude-flow <ruv@ruv.net> * feat(cli): multistatic room-watch — fuse co-located nodes (ADR-029/151) `room-watch --node-bank N:path` (repeatable) groups live CSI frames by node_id and fuses per-node banks via MultiNodeMixture. Validated live on COM8 (node 9, edge_tier=0): frames grouped + fused end-to-end. True 2-node fusion is covered by unit tests; a second raw-CSI node is the hardware blocker. 54 tests pass. Co-Authored-By: claude-flow <ruv@ruv.net> * docs(integration): calibration → cognitum-v0 appliance integration overview Detailed cross-repo integration spec for cognitum-one/v0-appliance: data contracts (CSI wire format, ADR-135 baseline binary, enrollment/bank/RoomState JSON schemas), calibrate-serve HTTP API, public crate API, Pi5+Hailo tiering, and a 5-step appliance integration plan. Grounded in the verified cognitum-v0 inventory (aarch64, cargo 1.96, HAILO10H, ruview-vitals-worker:50054). Co-Authored-By: claude-flow <ruv@ruv.net> * fix(calibration): address PR review — aarch64 decouple, API auth, path traversal, throttle Resolves the review on #989: - **Cross-compile (the appliance blocker):** make wifi-densepose-mat optional and feature-gate it (`mat`), so `cargo build -p wifi-densepose-cli --no-default-features` excludes the mat→nn→ort(ONNX)→openssl-sys chain. Verified: `cargo tree --no-default-features` shows 0 ort/openssl deps → calibration cross-compiles clean for the Pi. - **Security (must-fix before LAN):** - `--token` / CALIBRATE_TOKEN bearer-auth middleware on every route; warns if bound non-loopback without a token. - sanitize client-supplied `room_id` to [A-Za-z0-9_-] (≤64) before it reaches the baseline write path — kills the `../` file-write primitive. + test. - **Perf:** stop locking shared status + cloning SessionStatus on every UDP frame — counters/snapshot flush on the 200 ms tick instead (no CPU starvation under flood). finalize write moved to async `tokio::fs::write`. - **Docs:** ADR-151 STALE wording matches the impl (baseline-id change; drift-threshold = P6 refinement); integration doc gets the `--no-default-features` build + auth/sanitize notes. 35 calibration + 15 CLI tests (no-default) / 20 CLI (default) pass. Co-Authored-By: claude-flow <ruv@ruv.net> * docs(worldgraph,worldmodel): add crates.io READMEs Plain-language overviews + feature lists, comparison tables (symbolic graph vs predictive occupancy; graph vs grid vs event-log), usage, and technical details. Adds readme = "README.md" to both manifests so they render on crates.io on the next release. Co-Authored-By: claude-flow <ruv@ruv.net> * release: worldgraph & worldmodel 0.3.1 (READMEs on crates.io) Co-Authored-By: claude-flow <ruv@ruv.net> * docs: precise calibration validation scope (capture+API+auth proven; clean enroll→train→infer not yet on-target) Aligns ADR-151 §7 + the appliance integration doc with the PR #989 scope clarification: nothing has run a clean baseline → enroll → train → infer on live CSI; the live breathing read used the stateless head, not a trained bank. Adds --source-format adr018v6 to the backlog. Co-Authored-By: claude-flow <ruv@ruv.net> * feat(calibrate-serve): live GET /room/state endpoint (mixture over CSI window) Adds a live RoomState readout over HTTP — the appliance UI's main need. The ingest task maintains a rolling per-frame scalar window (flushed on the 200 ms tick, no per-frame lock); the handler loads a bank (resolved as a sanitized name under output_dir — same path-traversal defense as room_id), runs the MixtureOfSpecialists over the window, returns RoomState JSON. Validated live (ESP32-S3 via relay): breathing 14-19 BPM over HTTP; a bank=../../etc/passwd query is neutralized to 'etcpasswd' (no traversal). Co-Authored-By: claude-flow <ruv@ruv.net> * feat(calibrate-serve): POST /room/train + fix AnchorLabel JSON to snake_case - POST /api/v1/room/train: { room_id, baseline_id, anchors[] } → trains a SpecialistBank and persists it as <output_dir>/<room_id>.json (path-sanitized), readable via /room/state?bank=<room_id>. Completes the HTTP train→infer loop. - Fix data-contract bug: AnchorLabel serialized as PascalCase variant names (serde default) while as_str() + the integration doc used snake_case. Added #[serde(rename_all = "snake_case")] so the JSON wire format matches the documented contract (empty/stand_still/…). Locked with a roundtrip test. Validated live (ESP32-S3): POST train (4 anchors → 6 specialists, persisted) → GET /room/state returns RoomState with the trained presence/restlessness; the synthetic-vs-real scale mismatch correctly triggers the anomaly veto. 36 calibration tests pass. Co-Authored-By: claude-flow <ruv@ruv.net> * feat(calibrate-serve): live enroll-over-HTTP (POST /enroll/anchor + /enroll/status) Closes the last HTTP gap — the appliance can now drive the ENTIRE calibration pipeline over HTTP without the CLI: baseline (start/stop) -> enroll/anchor x8 -> room/train -> room/state - POST /enroll/anchor { room_id, baseline, label, duration_s? }: the ingest task loads the baseline (sanitized name under output_dir), captures the anchor for the duration against it (AnchorRecorder + per-frame series), runs the quality gate, and on completion replies with the verdict + accumulates the AnchorFeature in an in-server enrollment map keyed by room_id. Re-prompts on rejection. - GET /enroll/status?room=<id>: accepted anchors, next, complete. - POST /room/train now falls back to the in-server enrollment when anchors[] is omitted. Validated live (ESP32-S3): capture baseline -> enroll stand_still (271 frames, 6s) -> gate correctly rejects "no person detected (presence_z 0.90 < 1.50)" relative to a same-occupancy baseline (a clean empty-room baseline is the documented on-target prerequisite). Builds clean; CLI tests pass. Co-Authored-By: claude-flow <ruv@ruv.net> * test(calibrate-serve): HTTP integration tests for the room/enroll endpoints Factor the router into build_router() (shared by execute + tests) and add tower-oneshot integration tests (no network/ingest needed): - health + descriptor → 200 - POST /room/train persists the bank; GET /room/state → 200; train with no anchors/enrollment → 400 - path-traversal: /room/state?bank=../../etc/passwd → 404 (sanitized, never reads outside output_dir) - enroll/status empty; /enroll/anchor with an unknown label → 400 CI regression coverage for the endpoints added this session. 18 CLI tests pass. Co-Authored-By: claude-flow <ruv@ruv.net> * fix(mat): make serde non-optional — unblocks `cargo test --workspace --no-default-features` Making wifi-densepose-mat optional in the CLI (for the aarch64/ort decouple) exposed a latent feature bug: mat's `api` module compiles unconditionally and uses serde, but `serde` was an optional dep enabled only via the `api`/`serde` features. Previously the CLI's *unconditional* mat dependency enabled those features transitively, so `--workspace --no-default-features` still got serde; once mat became optional+gated, the workspace build lost it → `error[E0432]: unresolved import serde` across mat's api/* (CI red). mat already pulls serde_json + axum unconditionally, so making `serde` non-optional has no real cost and restores the workspace build. Does NOT affect the aarch64 CLI build (mat isn't built there at all): verified `cargo tree -p wifi-densepose-cli --no-default-features` still shows 0 ort/openssl deps, and `cargo test --workspace --no-default-features` compiles clean. Co-Authored-By: claude-flow <ruv@ruv.net> * docs(claude.md): add wifi-densepose-calibration to crate table (pre-merge) Co-Authored-By: claude-flow <ruv@ruv.net> * docs(adr): ADR-152 — WiFi-pose SOTA 2026 intake (geometry-conditioned calibration, external benchmarks, encoder recipe) Records the 2026-06-10 deep-research run (22 sources, 110 claims, 25 adversarially verified: 24 confirmed / 1 refuted) and the decisions it implies: - §2.1 ACCEPTED: geometry-condition the ADR-151 calibration system — NodeGeometry at enrollment, geometry embeddings for future LoRA heads, PerceptAlign-style two-checkerboard camera↔WiFi alignment for the ADR-079 supervised path. PerceptAlign (MobiCom'26) names the failure mode ("coordinate overfitting") that matches our own ADR-150 cross- subject collapse. - §2.2 ACCEPTED: benchmark protocol vs external "WiFlow-STD (DY2434)" (claimed 97.25% PCK@20, Apache-2.0 weights+dataset) with a no-citation rule until measured on our 17-keypoint ESP32 eval set. Name collision with our internal WiFlow is disambiguated. - §2.3 ACCEPTED: amend ADR-150 training recipe per UNSW MAE study — 80% masking, (30,3) patches, data-over-capacity priority (log-linear, unsaturated at 1.3M samples). - §2.4 watch items: IEEE 802.11bf-2025 published 2025-09-26; esp_wifi_sensing as external presence baseline (drop-in claim REFUTED 0-3); ZTECSITool 160MHz/512-subcarrier anchor node (procurement-gated). - §2.5 NOT adopted: non-WiFi "foundation model" papers; DensePose-UV (no 2025-2026 work does UV regression from commodity WiFi). Every number is evidence-graded CLAIMED vs MEASURED in the source register. Re-check horizon 2026-12. Co-Authored-By: RuFlo <ruv@ruv.net> * test(calibration): full-loop integration test — baseline→enroll→train→infer proven in-process (ADR-151 §7 gap, software half) Closes the software half of PR #989's headline validation gap: the complete calibration loop had never run end-to-end anywhere, even in-process. tests/full_loop.rs (412 lines, deterministic xorshift32 room simulator, HT20/52-subcarrier/20Hz, same fingerprint family as the ADR-135 roundtrip test) now drives the CLI's exact stage order through the public API: 1. baseline — 600 static frames, zero motion flags post-warmup, calibration_uuid() exactly as the CLI derives it 2. enroll — all 8 AnchorLabel::SEQUENCE anchors through AnchorQualityGate::default(), session is_complete() 3. extract — AnchorFeature::from_series recovers injected 0.25Hz and 0.125Hz breathing within ±0.04Hz 4. train — SpecialistBank::train fits all 6 specialists; JSON round-trip and the runtime consumes the RELOADED bank 5. infer — positive: never-enrolled 0.30Hz subject reads present, 18±2 BPM; negative: empty window reads absent; degradation: foreign baseline_id flags STALE Seed-robust (5 seeds), passes with and without default features: 36 unit + 1 integration green. Validation docs updated (ADR-151 §7 + integration doc §7 matrix): what remains is strictly the on-target hardware session (real CSI, physically empty room, operator performing the guided anchors). Three behavioral findings from building the test are recorded for pre-session triage: z-band squeeze between baseline motion flagging (z>2.0) and the still- anchor gate (presence_z≥1.5) — likeliest on-hardware enroll failure; variance-only PresenceSpecialist missing motionless-person mean shift; ungated breathing_hz/heart_hz in noise-window embeddings. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(calibration): close all four ADR-152 behavioral findings pre-hardware-session The full-loop integration test surfaced three findings; fixing the third exposed a fourth. All four are fixed and regression-guarded: 1. z-band squeeze (enrollment.rs) — anchor motion is now measured from frame-to-frame deltas of the deviation series (|Δz| > Z_DELTA_MOTION 0.5 ∨ |Δφ| > π/6), not from the absolute motion_flagged, which fires at amplitude_z_median > 2.0 vs the EMPTY baseline and so conflated presence strength with motion. A strongly-reflecting still person (z = 3.0 — every frame flagged by the old heuristic) now enrolls. The old unit tests mocked (z=3.0, motion=false), a combination the real deviation() can never emit — which is exactly how the squeeze hid; tests now derive the flag from z the way the producer does. 2. variance-only presence (specialist.rs) — PresenceSpecialist gains a mean-shift channel: present when variance > threshold OR |mean − empty_mean| > mean_dist_threshold (trained at half the empty→occupied mean distance, None when the means don't separate). Detects the motionless person whose body raises the scalar mean but not its variance. Old persisted banks deserialize with the channel inert (serde default None) — variance-only behavior preserved, proven by a fixture test against pre-change JSON. 3. ungated hz embedding (extract.rs) — Features::embedding() zeroes breathing_hz/heart_hz below EMBED_MIN_SCORE (0.25), keeping the random in-band peaks of noise windows out of the posture/anomaly prototype space. Raw fields stay ungated (specialists have their own stricter gates). 4. heart-band lag-floor leakage (extract.rs, found while fixing 3) — a pure 0.30 Hz breathing signal scored 0.67 in the heart band at 3.33 Hz: out-of-band rhythm leaks as a monotonic slope whose max sits at the band's lag floor, so score gating alone cannot stop it. autocorr_dominant now requires the winning lag to be an interior local maximum; band-edge "peaks" are rejected, true in-band peaks (interior by definition) are preserved. full_loop.rs strengthened to drive the fixes end-to-end: the StandStill anchor is now a z=3.0 strong reflector (unenrollable pre-fix), and a new motionless-person runtime case proves mean-channel detection at empty- level variance. Validation: 41 calibration unit + 1 full-loop integration + 23 CLI tests green; cargo test --workspace --no-default-features exit 0. Co-Authored-By: RuFlo <ruv@ruv.net> |
||
|
|
0d3d835bf8 |
feat(swarm): add ruview-swarm crate — drone swarm control system (ADR-148) (#862)
* feat(swarm): add wifi-densepose-swarm crate implementing ADR-148 drone swarm control system
New crate `wifi-densepose-swarm` with hierarchical-mesh swarm topology,
Raft consensus, MAPPO MARL, CSI sensing integration, and ITAR-gated
coordination features. Closes 3 of 7 milestones (M1, M2, M5) with 5/5
ADR-148 SOTA performance targets met.
## Modules (45 source files, 14 modules)
- types: NodeId, DroneState, Position3D, SwarmTask, SwarmError, FailSafeState
- topology: Raft consensus (leader election, log replication, quorum), Gossip, Mesh
- formation: VirtualStructure, LeaderFollower, Reynolds flocking (itar-gated)
- planning: RRT-APF hybrid planner, 3-phase coverage, Bayesian grid, pheromone
- allocation: Auction + FNN bid scorer (itar-gated)
- sensing: CsiPayloadPipeline (Live/Synthetic/Replay), MultiViewFusion, OccWorldBridge
- marl: MAPPO actor (3-layer MLP), LocalObservation (64-dim), RewardCalculator, PPO loop
- security: MAVLink v2 HMAC-SHA256, UWB anti-spoofing, geofence, Remote ID, FHSS
- failsafe: 10-state onboard machine, GCS-independent safety transitions
- config: TOML SwarmConfig with SAR/inspection/agriculture/mine/demo/wi2sar_reference
- demo: SyntheticCsiGenerator, DemoScenario (SAR/open-field/mine)
- integration: FlightController trait, MAVLink dialect (50000-50005), SwarmSim
- orchestrator: SwarmOrchestrator wiring all subsystems end-to-end
- bench_support: Criterion fixture generators
## ITAR compliance
Swarming coordination features gated behind `itar-unrestricted` feature
per USML Category VIII(h)(12). Default build compiles clean stubs.
## Benchmark results (criterion, release mode)
- MARL actor inference: 3.3 µs (target ≤ 5 ms — 1,516× headroom)
- RRT-APF planning (100 iter): 0.043 ms (target < 300 ms — 6,946× headroom)
- MultiView CSI fusion (3 UAVs): 58.5 ns (target < 10 ms — 171,000× headroom)
- 3-view localization: 1.732 m (target ≤ 2 m — beats Wi2SAR SOTA)
- 4-drone SAR coverage (400×400 m): 223 s (target ≤ 240 s — PASS)
## Tests
- --no-default-features: 73/73 passing
- --features itar-unrestricted: 85/85 passing
Closes #861
Co-Authored-By: claude-flow <ruv@ruv.net>
* refactor(swarm): rename wifi-densepose-swarm → ruview-swarm
The swarm control system is a RuView-level capability (drone coordination,
Raft consensus, MARL) that operates above the wifi-densepose sensing layer
rather than being a sub-component of it. Rename aligns with the project
identity and separates coordination infrastructure from sensing modules.
Co-Authored-By: claude-flow <ruv@ruv.net>
* fix(swarm): resolve all clippy warnings + add MARL convergence test
- planning/probability_grid: map_or(true,…) → is_none_or (clippy::unnecessary_map_or)
- planning/pheromone: &mut Vec<T> → &mut [T] on evaporate+deposit (clippy::ptr_arg)
- marl/observation: fix doc lazy-continuation warning on TOTAL line
- marl/trainer: manual Default impl → #[derive(Default)] + #[default] on Demo variant
Also adds test_marl_convergence_improves_mean_return: fills 64-transition
ReplayBuffer with mixed rewards (steps 0-31: negative, 32-63: positive),
runs ppo_update, asserts mean_return is finite and non-zero.
Result: 0 clippy warnings · 74/74 tests (default) · 86/86 (itar-unrestricted)
Co-Authored-By: claude-flow <ruv@ruv.net>
* feat(swarm): integrate Ruflo AI-agent capabilities into ruview-swarm
Adds a feature-gated Ruflo integration layer connecting ruview-swarm to the
claude-flow daemon's AgentDB, AIDefence, and SONA intelligence subsystems.
Default build is unaffected (all paths behind `Option<Box<dyn RufloBackend>>`).
## New module: src/ruflo/
- backend.rs: RufloBackend trait (9 async methods) + RufloError, MissionMemoryEntry,
PatternEntry, MavlinkScanResult types (always compiled)
- mock_backend.rs: MockRufloBackend in-memory impl for testing (always compiled, 5 tests)
- http_backend.rs: HttpRufloBackend — JSON-RPC 2.0 → claude-flow daemon localhost:3000
(gated behind `ruflo` feature, requires reqwest)
- mission_summary.rs: MissionSummary serializer with pattern description + confidence
scoring from victim recall, coverage %, collision penalty (always compiled, 3 tests)
## 4 capability areas
1. MissionMemory → memory_store / memory_search (cross-mission victim memory)
2. PatternLearner → agentdb_pattern-store / -search (HNSW SONA trajectory patterns)
3. MavlinkDefence → aidefence_is_safe / aidefence_scan (scan MAVLink before accepting)
4. IntelligenceHooks → trajectory-start/step/end (SONA learning loop)
## SwarmOrchestrator integration
- with_ruflo(backend): builder to attach a backend
- start_trajectory(task) / finish_trajectory(success, key): SONA mission lifecycle
- receive_peer_detection_checked(): AIDefence scan before accepting peer detections
## Cargo feature
`ruflo = ["dep:reqwest", "dep:serde_json"]` — optional, not in default
## Tests
- --no-default-features: 82/82 pass (8 new ruflo tests)
- --features ruflo,itar-unrestricted: 94/94 pass
Co-Authored-By: claude-flow <ruv@ruv.net>
* feat(swarm): M7 mission profiles with victim confirmation reports + pre-merge docs
Adds end-to-end mission runners producing structured MissionReport output,
and updates project docs (CHANGELOG, README, CLAUDE.md) per pre-merge checklist.
## M7 Mission Profiles (integration/mission_report.rs + swarm_sim.rs)
- MissionReport / VictimReport / SotaComparison types (serde-serializable)
- run_mission_with_report(): full mission → detailed report with per-victim
localization error, fusion uncertainty, contributing drones, detection time
- run_inspection_mission(): leader-follower power-line corridor inspection
- run_mine_mission(): GPS-denied underground (2-drone, slow, UWB-only)
- SotaComparison embeds Wi2SAR baseline (5m / 810s) vs achieved metrics
## Docs (pre-merge checklist)
- CHANGELOG.md: ruview-swarm + Ruflo integration + performance entries
- README.md: ruview-swarm row
- CLAUDE.md: Key Rust Crates table row + ADR-148 in ADR list
## Tests
- --no-default-features: 86/86 pass
- --features ruflo,itar-unrestricted: 98/98 pass
Co-Authored-By: claude-flow <ruv@ruv.net>
* fix(swarm): convergence-assist for victim fusion + 5s Ruflo HTTP timeout
Follow-up to
|
||
|
|
9ad550d95f |
feat(worldmodel): Candle Rust port + GCP GPU scripts (ADR-147 Phase 4+6)
Candle native port — wifi-densepose-occworld-candle v0.3.0: - config.rs: OccWorldConfig (14 params matching occworld.py) - vqvae.rs: ClassEmbedding(18→64), VQCodebook(512×512, squared-L2), QuantConv/PostQuantConv(1×1 Conv2d), fold_3d_to_2d helpers ResNet encoder/decoder are documented stubs (Phase 5 checkpoint pending) - transformer.rs: full Candle MHA transformer (2 layers, temporal+spatial cross-attention, FFN, pre-norm residuals) - inference.rs: OccWorldCandle::dummy() + ::load() + predict() InferenceOutput: sem_pred(1,15,200,200,16) + trajectory_priors - 14/14 tests pass (12 lib + 2 doctests) GCP GPU scripts — scripts/gcp/: - provision_training.sh: a2-highgpu-8g (8×A100 40GB) for Phase 5 retraining - run_training.sh: rsync + torchrun 8-GPU train + checkpoint download - provision_cosmos.sh: a2-ultragpu-1g (A100 80GB) for Cosmos evaluation - cosmos_eval.sh: run Cosmos-Transfer2.5 inference, download results - teardown.sh: safe checkpoint download + instance delete Co-Authored-By: claude-flow <ruv@ruv.net> |
||
|
|
c7ddb2d7d1 |
feat(worldmodel): ADR-147 — OccWorld world model integration, wifi-densepose-worldmodel v0.3.0 (#856)
* feat(worldmodel): ADR-147 — OccWorld integration, wifi-densepose-worldmodel v0.3.0 (#854) - New crate `wifi-densepose-worldmodel` v0.3.0: async Unix-socket bridge to OccWorld Python inference server; `OccWorldBridge`, `OccupancyGrid3D`, `TrajectoryPrior`, `worldgraph_to_occupancy` encoder (14/14 tests pass) - `scripts/occworld_server.py`: long-lived Python inference server for OccWorld TransVQVAE (72.4M params); applies API-bug patches; dummy mode for CI testing; graceful SIGTERM shutdown - `pose_tracker.rs`: `trajectory_prior` soft-blend injection (80/20 Kalman/prior) on torso keypoint; `set_trajectory_prior()` public method - CI: added `Run ADR-147 worldmodel tests` step - ADR-147: accepted — OccWorld primary (209 ms, 3.37 GB VRAM, RTX 5080); Cosmos deferred to ADR-148 (32.54 GB VRAM exceeds hardware) - Benchmark proof: 208.7 ms P50, 3.37 GB peak VRAM, 12.1 GB headroom Co-Authored-By: claude-flow <ruv@ruv.net> * chore: update ruvector.db state Co-Authored-By: claude-flow <ruv@ruv.net> * chore: ruvector.db sync Co-Authored-By: claude-flow <ruv@ruv.net> * fix(cli): add missing min_frames field to CalibrateArgs test helper E0063 in calibrate.rs:448 — CalibrateArgs gained min_frames in ADR-135 but the default_args() test helper was not updated. min_frames=0 means 'use tier default', matching the existing runtime behaviour. Co-Authored-By: claude-flow <ruv@ruv.net> |
||
|
|
2eada40e3b |
feat(engine): integrate ADR-135..141 into an end-to-end trust pipeline
- signal/calibration.rs: BaselineCalibration gains calibration_id()/ calibration_uuid()/apply() — the ADR-135->136 link that stamps FrameMeta.calibration_id (deterministic id, no serialization change). +1 test. - NEW crate wifi-densepose-engine: StreamingEngine::process_cycle() composes fuse_scored (137) -> calibration provenance (135/136) -> privacy demotion on contradiction (141) -> WorldGraph SemanticState with mandatory provenance + DerivedFrom edge (139). Returns TrustedOutput (the trust chain made concrete). - Validates the throughline: every output names evidence + model + calibration + privacy decision; calibration_id flows input->QualityScore->provenance; contradiction demotes class; deterministic; privacy mode attested. - 4 integration tests; workspace 0 errors; signal 410 lib tests pass. Co-Authored-By: claude-flow <ruv@ruv.net> |
||
|
|
521a012d84 |
feat(worldgraph): ADR-139 WorldGraph environmental digital twin (#843)
New crate wifi-densepose-worldgraph: - model.rs: WorldNode (10 kinds) + WorldEdge (7 relations) as serde enums (no trait objects → deterministic RVF persistence); WorldId, EnuPoint, ZoneBoundsEnu (with point-in-bounds), SemanticProvenance (house-rule tuple) - graph.rs: WorldGraph over petgraph StableDiGraph; upsert/add_edge/neighbors, room_for_area (HomeCore area_id linkage), observed_by/contents_of queries, add_semantic_state (append-with-provenance DerivedFrom), add_contradiction (both beliefs retained), apply_privacy_mode → PrivacyRollup, JSON persistence - 7 tests (upsert/replace, linkage, unknown-endpoint, location, provenance+ contradiction, privacy rollup, deterministic JSON round-trip) - workspace 0 errors Co-Authored-By: claude-flow <ruv@ruv.net> |
||
|
|
e96ebaea81 |
HOMECORE: native Rust/WASM/TS port of Home Assistant — ADRs 125-134 implementation (#800)
* feat(adr-125 iter 3): BFLD PrivacyGate + semantic-event naming at HAP boundary Inserts a Python equivalent of `wifi-densepose-bfld::PrivacyClass` + `PrivacyGate` between the rv_feature_state parser and the HAP toggle file. ADR-125 §2.1.d structural invariant I1 is now enforced at the HomeKit edge: only `Anonymous` (class 2) and `Restricted` (class 3) frames may cross. `Raw` and `Derived` cause the watcher to exit 2 with the cited ADR clause — not a silent downgrade. Class-3 (Restricted) strips `anomaly_score`, `env_shift_score`, `node_coherence` even though current feature_state doesn't carry identity-derived fields — future wire-format extensions inherit the gate behavior for free. Operator-facing semantic naming follows ADR-125 §2.1.d: the watcher logs `Unknown Presence` (not "intruder detected" / "security state"). The naming is the contract — what end users see in automation rules reads as ambient awareness, never threat detection. Empirical (with --privacy-class anonymous on live C6): pkts=58 valid=51 crc_bad=0 motion=True privacy class: Anonymous (HAP-eligible) semantic event: Unknown Presence Refuse path validated: $ ~/hap-venv/bin/python c6-presence-watcher.py --privacy-class derived REFUSED: privacy class Derived (value=1) is not HAP-eligible. ADR-125 §2.1.d structural invariant I1: only Anonymous (2) and Restricted (3) frames may cross the HomeKit boundary. $ echo $? 2 Branch: feat/adr-125-apple-fabric (kept off main while docker build for sha |
||
|
|
c965e3e6c0 |
feat(adr-118/p1): scaffold wifi-densepose-bfld crate + frame header (3/3 tests GREEN)
Land P1 of the BFLD rollout — the wire-format primitives: - New workspace member: v2/crates/wifi-densepose-bfld - PrivacyClass enum (Raw/Derived/Anonymous/Restricted) with allows_network() and allows_matter() const helpers reflecting ADR-120 §2.2 and ADR-122 §2.4 - BfldFrameHeader (#[repr(C, packed)]) per ADR-119 §2.1 - BFLD_MAGIC = 0xBF1D_0001, BFLD_VERSION = 1 - BfldError variants for InvalidMagic / UnsupportedVersion / Crc / PrivacyViolation - soul-signature cargo feature (gated, default OFF) per ADR-118 §1.4 - Compile-time size assertion via static_assertions::const_assert_eq! - 3 acceptance tests in tests/frame_header_size.rs (all pass) Bug fix: - ADR-119 AC1 claimed BfldFrameHeader is 40 bytes. Actual packed layout sums to 86 bytes. Updated AC1 and §2.1 prose to match. const_assert in frame.rs pins the value structurally — a future field addition that breaks the size fails to compile. Out of scope for this iter (deferred to later P1 commits): - Field-level missing-docs warnings (21) — addressed alongside accessor helpers - Payload section parsing — needs the section-length prefix tests - Round-trip serialize/parse — covered by a fixture-based test in the next iter cargo test -p wifi-densepose-bfld --no-default-features → 3 passed, 0 failed Co-Authored-By: claude-flow <ruv@ruv.net> |
||
|
|
56265023dc |
feat(cog-ha-matter): P2 scaffold + ADR-116 P1 research-dossier fold-in
cron iter 1. Three things landed atomically because they cross-cite:
P1 — research dossier complete
Deep-researcher agent (a4dd35950ffd) shipped
docs/research/ADR-116-ha-matter-cog-research.md: 8 sections,
30+ citations across Matter / HACS / cog arch / local-AI /
federation / competitors / regulatory / v1 scope. Key
findings folded into ADR-116 §3 and §4:
- Matter device class: OccupancySensor (0x0107) +
RFSensing feature on cluster 0x0406 (1.4 rev 5)
- ESP32-C6 Thread Border Router: one Kconfig flag away
(CONFIG_OPENTHREAD_BORDER_ROUTER=y)
- HACS quality tier: target Gold (repairs + diagnostics +
reconfiguration), start from hacs.integration_blueprint
- CSA cert: ~$30-42k/yr — skip for v1, "Works with HA"
positioning instead
- Cog RAM/CPU: 128 MB / 15% on the Seed; 10 KB INT8
semantic-primitive classifier fits without PSRAM
- SONA: <100 µs/query confirmed by ruvllm-esp32 v0.3.3
- FDA Jan 2026 wellness guidance covers HR / sleep / activity
anomaly when marketed as "anomaly notification" not "diagnosis"
- Competitor moat: Aqara FP300 / TOMMY / ESPectre all lack
HR + BR + pose + semantic + witness simultaneously
P2 — cog crate scaffold compiles
v2/crates/cog-ha-matter/ created with cog-pose-estimation as
precedent shape (ADR-101). Files:
- Cargo.toml: depends on wifi-densepose-sensing-server with
--features mqtt + wifi-densepose-hardware for the ADR-110
SyncPacket bridge.
- src/lib.rs: COG_ID = "ha-matter", MDNS_SERVICE_TYPE
"_ruview-ha._tcp", DEFAULT_CONTROL_PORT 9180.
- src/manifest.rs: typed CogManifest (8 fields) mirroring
cog-pose-estimation's manifest.template.json. Round-trip
test locks the JSON wire shape; id-constant test guards
against rename drift.
- src/main.rs: clap CLI with --sensing-url / --mqtt-host /
--mqtt-port / --privacy-mode / --print-manifest. The
--print-manifest flag emits the build-time template with
{{VERSION}} / {{ARCH}} placeholders for the signer.
- v2/Cargo.toml: cog-ha-matter added as workspace member.
Verification:
cargo check -p cog-ha-matter --no-default-features → green
cargo test -p cog-ha-matter --no-default-features --lib
→ 2/2 manifest tests pass
ADR-116 §3 + §4 + §5 (phases) updated to mark P1+P2 ✅ done and
seat the recommended v1 scope (privacy-mode audit-only → cog
signing → SONA loop → HACS gold → Matter Bridge as v0.8) ranked
by build cost × user impact per the dossier.
P3 (next iter): wrap the existing ADR-115 MQTT publisher as the
cog's main loop. The scaffold returns SUCCESS immediately today.
Co-Authored-By: claude-flow <ruv@ruv.net>
|
||
|
|
004a63e82d |
fix(security): audit — fix RUSTSEC vulns, clippy warnings, dead code (#769)
- Upgrade openssl to 0.10.78 (CVE-2026-41676), jsonwebtoken to 9.4 - Suppress unmaintained-only/no-CVE advisories in .cargo/audit.toml with per-entry rationale - Fix all `cargo clippy --all-targets -- -D warnings` errors across 35 crates: derivable_impls, needless_range_loop, map_or→is_some_and/ is_none_or, await_holding_lock (drop MutexGuard before .await), ptr_arg (&mut Vec→&mut [T]), useless_conversion, approximate_constant (2.718→E, 3.14→PI), field_reassign_with_default, manual_inspect, useless_vec, lines_filter_map_ok, print_literal, dead_code - Apply `cargo fmt --all` - Pre-existing test failure in wifi-densepose-signal (test_estimate_occupancy_noise_only) is not introduced by this PR |
||
|
|
6959a42312 |
feat(cog-person-count): v0.0.1 scaffold + tests + fusion math + bench (ADR-103) (#694)
First implementation PR for ADR-103. Same incremental shape that
ADR-101 used: scaffold the cog crate, ship a stub-backend release
that satisfies the runtime contract + 15 tests + measured cold-start,
then follow up with the trained count_v1.safetensors in a separate PR.
What ships:
* v2/crates/cog-person-count/ — new workspace member.
- Cargo.toml: candle-core/candle-nn 0.9 (cpu default, cuda feature
opt-in), safetensors, ureq, sha2 — same dep shape as the pose cog
but minus wifi-densepose-train (this cog has no training-side
consumer, so the dep tree is materially smaller → 2.36 MB
binary vs the pose cog's 4.5 MB).
- src/inference.rs: CountNet (Conv1d 56→64→128→128 encoder + count
head Linear(128→64→8)+softmax + confidence head
Linear(128→32→1)+sigmoid). Stub backend returns
`{1-person, 0-confidence}` honestly when no safetensors present.
- src/fusion.rs: fuse_confidence_weighted() — Bayesian product of
per-node distributions with confidence-weighted log-sum, plus
fuse_with_mincut_clip() hook for the v0.2.0 Stoer-Wagner
upper-bound (`ruvector-mincut` dep lands when min-cut graph
builder is ready). Confidences floored at 1e-3 and probs floored
at 1e-9 before logs — no NaN propagation.
- src/publisher.rs: emits {count, confidence, count_p95_low,
count_p95_high, n_nodes, probs} per ADR-103 §"Output".
- src/main.rs: full ADR-100 four-verb CLI (version|manifest|health
|run). The `run` subcommand explicitly returns "wiring pending
v0.0.1" so the in-process library API is the v0.0.1-clean
integration path.
- tests/smoke.rs (8 tests) + fusion::tests (7 tests, in-lib) — 15
total, all green. Cover stub-backend behaviour, wrong-shape
rejection, fusion math (empty / single / agreement / high-conf
override / normalisation), p95-range correctness, and min-cut
clip semantics.
- cog/{manifest.template.json, config.schema.json, README.md} +
cog/artifacts/ placeholder dir.
* v2/Cargo.toml: registers the new workspace member.
Verified locally:
cargo check -p cog-person-count --no-default-features → clean
cargo test -p cog-person-count --no-default-features → 8/8 pass
cargo test -p cog-person-count --lib → 7/7 pass
cargo build -p cog-person-count --release → 2.36 MB binary
./cog-person-count version → "person-count 0.3.0"
./cog-person-count manifest → JSON skeleton
./cog-person-count health → backend:stub,
count:1, conf:0,
p95:[1,1]
Cold-start: 30 sequential `health` invocations → 53.3 ms/invocation
(vs cog-pose-estimation's 76.2 ms — smaller dep tree)
cog/README.md adds:
* Security section — six-row threat table covering safetensor mmap
trust, non-finite outputs, sensing fetch failures, fusion
divide-by-zero / log-of-zero, min-cut degenerate cases, and stdout
spoofing.
* Performance / optimization section — binary size, release profile
(already opt-level=3 / lto=fat / codegen-units=1 / strip=true at
workspace level), cold-start comparison table, projected warm-path
latency budget.
Still pending (separate PRs, ADR-103 §"Migration"):
* Train count_v1.safetensors on the existing 1,077 paired samples
with `n_persons` labels (Candle on RTX 5080, same script that
produced pose_v1.safetensors yesterday).
* `run` subcommand wiring (long-running polling loop, same shape as
cog-pose-estimation::runtime).
* Cross-compile + sign + GCS upload (mirror of cog-pose-estimation
release pipeline).
* Server-side `csi.rs::score_to_person_count` call-site rewire to
consume this cog when installed; falls back to PR #491's heuristic
when not.
|
||
|
|
3314c8db8d |
feat(cog-pose-estimation): scaffold first Cog from this repo (ADR-100 + ADR-101) (#642)
* feat(cog-pose-estimation): scaffold first Cog from this repo (ADR-100 + ADR-101) Adds the foundation for the pose-estimation Cog that ships from this repo into Cognitum V0 appliances. Companion ADR-225 + crate land in cognitum-one/v0-appliance. ADRs: * ADR-100 formalises the Cognitum Cog packaging spec — on-device layout under /var/lib/cognitum/apps/<id>/, manifest.json schema (incl. new binary_sha256 + binary_signature fields), GCS hosting convention, repo source layout, build pipeline, and the four-verb runtime contract (version | manifest | health | run). Documents the convention I reverse-engineered from inspecting installed cogs on a live cognitum-v0 appliance — `anomaly-detect`, `presence`, `seizure-detect`, etc. * ADR-101 designs the pose-estimation Cog itself: where it sits in the wifi-densepose pipeline (encoder init from ruvnet/wifi-densepose-pretrained, 17-keypoint regression head), what gets shipped per target arch (arm / x86_64 / hailo8 / hailo10), acceptance gates (PCK@20 explicitly deferred to #640 — this ADR ships the vehicle, not the accuracy). Crate v2/crates/cog-pose-estimation/: * Cargo.toml + workspace member declaration with a hailo feature gate so the binary builds without the Hailo SDK in CI. * main.rs implements the four-verb CLI exactly per ADR-100. * config.rs / manifest.rs / publisher.rs / inference.rs / runtime.rs — small modules, each <100 lines. * publisher.rs emits ADR-100 structured JSON events. * inference.rs is a stub that produces a centred-skeleton baseline with confidence=0 (honest: no trained weights wired in yet). * runtime.rs subscribes to /api/v1/sensing/latest, slides a 56*20 window, runs the engine, emits pose.frame events. * cog/manifest.template.json + cog/config.schema.json define the release artifact + runtime config schemas. * cog/Makefile holds build / sign / upload targets. * tests/smoke.rs covers manifest roundtrip + engine I/O surface. Verified locally: * cargo check -p cog-pose-estimation: clean. * cargo test -p cog-pose-estimation: 4/4 pass. * ./target/release/cog-pose-estimation {version,manifest,health}: all emit the right contract output. This commit contains scaffolding only; the actual trained weights and Hailo HEF cross-compile come in follow-ups tracked in #640 and the companion v0-appliance branch. * feat(cog-pose-estimation): first measured run — Candle CUDA on RTX 5080 Trained pose_v1 on ruvultra (RTX 5080) via Candle 0.9 + cuda feature against the same 1,077-sample paired session that produced 0%/0% PCK in #640 with the pure-JS SPSA trainer. First real numbers: PCK@20 = 3.0% (up from 0.0%) PCK@50 = 18.5% (up from 0.0%) MPJPE = 0.093 (down from 0.66, ~7x improvement) 400 epochs in 2.1 s wall time, full-batch, ~5 ms/epoch. Loss curve 0.181 -> 0.014 over the run, eval 0.010. Per-joint reveals the model leans on right-side proximal joints (r_hip 77% PCK@50, r_knee 35%, l_elbow 26%) — consistent with the camera framing in the source recording. Distal joints (wrists, ankles) and face joints are still near-random, consistent with the 56-subcarrier / 20-frame input not carrying fine-grained spatial info at 1077 samples. This commit: * Adds v2/crates/cog-pose-estimation/cog/artifacts/{pose_v1.safetensors, train_results.json} so the cog dir now contains a real reference artifact, not just scaffold. * Updates cog/README.md "Status" block with the measured numbers, per-joint table, and an honest reading of where the model succeeds vs where the data is the bottleneck. * Adds docs/benchmarks/pose-estimation-cog.md as the canonical benchmark log — append-only, one section per published run. * Appends a "First measured run" section to ADR-101 referencing the new benchmark file. Still pending in the follow-up: * Wire pose_v1.safetensors into src/inference.rs (replace stub). * ONNX export (Candle lacks a writer — needs external conversion). * Hailo HEF cross-compile + cluster deploy. The data-bound gap to PCK@20 >= 35% is tracked in #640. * feat(cog-pose-estimation): wire real weights — cog is no longer a stub Replaces the centred-skeleton stub in src/inference.rs with a real Candle-based loader that reads cog/artifacts/pose_v1.safetensors and runs the trained Conv1d encoder + MLP pose head on every incoming CSI window. What changes: * src/inference.rs: PoseNet mirrors the training script's architecture exactly — Conv1d(56->64, k=3 d=1), Conv1d(64->128, k=3 d=2), Conv1d(128->128, k=3 d=4), mean over time, Linear(128->256)+ReLU, Linear(256->34)+sigmoid -> reshape [17, 2]. The InferenceEngine searches a sensible candidate list for the weights file (/var/lib/cognitum/apps/pose-estimation/, ./pose_v1.safetensors, ./cog/artifacts/, repo-root, v2/-relative) and falls back to the stub when none are present so the cog still satisfies ADR-100. * Cargo.toml: adds candle-core 0.9 + candle-nn 0.9 (no-default-features, CPU build by default) + safetensors 0.4. New `cuda` feature opt-in for GPU inference on hosts that have it. Drops the unused wifi-densepose-train path dep from the default build path. * src/main.rs + src/publisher.rs: health.ok event now carries `backend` (candle-cuda | candle-cpu | stub) and the synthetic output confidence, so operators can tell at a glance whether the cog loaded its weights or fell back to the stub. * tests/smoke.rs: adds `real_weights_load_when_available` which asserts the loaded engine reports backend=candle-* and emits non-zero confidence — exactly the signal that proves we're not silently degrading to the stub. Verified locally: * `cargo check -p cog-pose-estimation --no-default-features` — clean * `cargo test -p cog-pose-estimation --no-default-features` — 5/5 pass * `./target/release/cog-pose-estimation health` emits: {"event":"health.ok","fields":{"backend":"candle-cpu","cog":"pose-estimation","synthetic_output_confidence":0.185}} — 0.185 is the published PCK@50 from cog/artifacts/train_results.json, emitted by the real Candle inference path (would be 0.0 if it had fallen back to the stub). The cog now runs the trained pose_v1 model end-to-end. Accuracy is still bounded by the underlying 1077-sample training data (PCK@20 3.0%, PCK@50 18.5% per docs/benchmarks/pose-estimation-cog.md) — that gap is data-bound and tracked in #640. ONNX export + Hailo HEF cross-compile remain follow-ups. * docs(benchmarks): measure cog-pose-estimation cold-start latency 100 sequential `cog-pose-estimation health` invocations average 76.2 ms each on a Windows x86_64 host using the `candle-cpu` backend. Each invocation re-loads pose_v1.safetensors and runs one synthetic forward pass, so this is the worst-case cold-start path. Long-running `run` inference will be sub-millisecond per frame once the model is loaded. Updates the benchmarks doc accordingly. * feat(cog-pose-estimation): ONNX export — pose_v1.onnx + scripts/export-onnx.py Adds the canonical ONNX artifact that unblocks downstream Hailo HEF cross-compile + ONNX Runtime benchmarks. Generated on ruvultra (torch 2.12.0 + CUDA), 12,059 bytes, opset 18, dynamic batch axis. * scripts/export-onnx.py: mirrors the Candle inference architecture in PyTorch (Conv1d 56->64, 64->128, 128->128 + Linear 128->256->34), pure- python safetensors loader (no extra pip dep), exports via torch.onnx.export, then verifies via onnx.checker.check_model and numerical parity against the torch reference. * Verified parity vs torch: max |torch - onnx| = 8.94e-8 (1e-5 threshold). Effectively bit-perfect. * v2/crates/cog-pose-estimation/cog/artifacts/pose_v1.onnx — the artifact itself, 12 KB. * docs/benchmarks/pose-estimation-cog.md — adds an ONNX export section with the verification numbers. Next: Hailo HEF cross-compile (still gated on Hailo SDK on a self-hosted runner) and ONNX Runtime latency benchmarks on each target arch. * feat(cog-pose-estimation): release v0.0.1 — signed aarch64 binary on GCS End-to-end deploy: cross-compiled to aarch64-unknown-linux-gnu on ruvultra, ran via qemu-aarch64-static, then smoke-tested on a real cognitum-v0 Pi 5. Signed with COGNITUM_OWNER_SIGNING_KEY (Ed25519) and uploaded to gs://cognitum-apps/cogs/arm/. Real-hardware results on cognitum-v0 (Pi 5): health: backend=candle-cpu, confidence=0.185, real weights loaded 30x sequential `health`: 0.251 s total -> 8.4 ms / invocation (cold) GCS release artifacts (publicly downloadable): binary: 3,741,976 bytes sha256 1e1a7d3dd01ca05d5bfc5dbb142a5941b7866ed9f3224a21edc04d3f09a99bf5 weights: 507,032 bytes sha256 eb249b9a6b2e10130437a10976ed0230b0d085f86a0553d7226e1ae6eae4b9e5 signature (Ed25519, b64): LUN7xqLPYD3MFzm5dKB5MnYU0LvoRtek5ci5KiKPHBg+Xo6xuazwokn2Dw2JPMaLYJzmWn/SpT4djuR7hYvVDw== Adds: * v2/crates/cog-pose-estimation/cog/artifacts/manifest.json — the release-pipeline-produced manifest with all fields filled in per ADR-100, including arch, target_triple, signature, and a build_metadata block carrying the validation PCK numbers. * docs/benchmarks/pose-estimation-cog.md — new sections covering the real Pi 5 smoke (8.4 ms cold-start) and the signed GCS release artifacts. Verified by downloading the binary anonymously from GCS and re-computing the sha256 — matches the locally-computed sha exactly. Signature decoded to the expected 64-byte Ed25519 length. Closes the GCS-upload acceptance criterion from ADR-100; the only pending work is Hailo HEF cross-compile (still SDK-gated) and an x86_64 release alongside this arm release. * docs(benchmarks): record live cognitum-v0 install + 5-sec smoke run Adds the "Live appliance install" section documenting what happened when the signed v0.0.1 binary + weights were installed under /var/lib/cognitum/apps/pose-estimation/ on cognitum-v0 (the V0 cluster leader). * Layout matches the existing anomaly-detect / presence / seizure- detect cogs exactly — the Cogs dashboard at http://cognitum-v0:9000/cogs auto-discovers entries. * `cog-pose-estimation run` ran for 5 seconds in the background and cleanly emitted run.started + structured WARN events for the missing local sensing-server on :3000 (cognitum-v0's actual CSI source is ruview-vitals-worker on :50054, not :3000). No crashes, no NaN, no leaks. * Wiring `sensing_url` to the appliance-native source is a separate Day-2 integration task. |
||
|
|
e964eaf14f | fix(deps): bump ndarray 0.15→0.17 and ndarray-npy 0.8→0.10 (closes #626) (#627) | ||
|
|
1b155ad027 |
chore: remove empty stub crates wifi-densepose-{api,db,config} (closes #578) (#608)
Each of these crates was a single-line doc-comment placeholder: v2/crates/wifi-densepose-api/src/lib.rs: //! WiFi-DensePose REST API (stub) v2/crates/wifi-densepose-db/src/lib.rs: //! WiFi-DensePose database layer (stub) v2/crates/wifi-densepose-config/src/lib.rs: //! WiFi-DensePose configuration (stub) with empty [dependencies] in their Cargo.toml and zero references from any source file or Cargo.toml in the workspace (verified by `grep -rln wifi-densepose-api/-db/-config` across `v2/`). They were reserved early for an envisioned REST/database/config split that never materialised. The functionality these would have provided is covered today by: - REST/WS: wifi-densepose-sensing-server (Axum) - Config: per-crate config + CLI args in sensing-server and desktop - DB: no persistent state; system is real-time Removal prevents `cargo` from listing dead crates, shipping empty published artifacts to crates.io, or wasting reviewer attention. If any of these names is needed in the future, reintroduce them with a real implementation. Per the issue reporter (@bannned-bit / Matad0r) #578 explicitly listed "OR be removed from workspace members until implementation starts" as an acceptable resolution. Updated: - `v2/Cargo.toml`: drop the three members (with inline comment explaining why) - `v2/Cargo.lock`: regenerated by cargo check - `CLAUDE.md`: drop the three rows from the crate table and the publishing order list - `CHANGELOG.md`: add an `[Unreleased] / Removed` entry Verified: - `cd v2 && cargo check --workspace --no-default-features` -> finished in 48s, no errors (warnings unchanged) |
||
|
|
afc86c6fc4 |
chore(deps): bump thiserror from 1.0.69 to 2.0.18 in /v2 (#469)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.69 to 2.0.18. - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.69...2.0.18) --- updated-dependencies: - dependency-name: thiserror dependency-version: 2.0.18 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
e6710e8988 |
chore(deps): bump ndarray-linalg from 0.16.0 to 0.18.1 in /v2 (#477)
Bumps [ndarray-linalg](https://github.com/rust-ndarray/ndarray-linalg) from 0.16.0 to 0.18.1. - [Release notes](https://github.com/rust-ndarray/ndarray-linalg/releases) - [Commits](https://github.com/rust-ndarray/ndarray-linalg/compare/ndarray-linalg-v0.16.0...ndarray-linalg-v0.18.1) --- updated-dependencies: - dependency-name: ndarray-linalg dependency-version: 0.18.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
ab9799adc3 |
chore(deps): bump tower-http from 0.5.2 to 0.6.8 in /v2 (#483)
Bumps [tower-http](https://github.com/tower-rs/tower-http) from 0.5.2 to 0.6.8. - [Release notes](https://github.com/tower-rs/tower-http/releases) - [Commits](https://github.com/tower-rs/tower-http/compare/tower-http-0.5.2...tower-http-0.6.8) --- updated-dependencies: - dependency-name: tower-http dependency-version: 0.6.8 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
bdb4484259 |
chore(deps): bump tch from 0.14.0 to 0.24.0 in /v2 (#482)
Bumps [tch](https://github.com/LaurentMazare/tch-rs) from 0.14.0 to 0.24.0. - [Release notes](https://github.com/LaurentMazare/tch-rs/releases) - [Changelog](https://github.com/LaurentMazare/tch-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/LaurentMazare/tch-rs/commits) --- updated-dependencies: - dependency-name: tch dependency-version: 0.24.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
98e7eeda42 |
chore(deps): bump ruvector-core from 2.0.5 to 2.2.0 in /v2 (#479)
Bumps [ruvector-core](https://github.com/ruvnet/ruvector) from 2.0.5 to 2.2.0. - [Release notes](https://github.com/ruvnet/ruvector/releases) - [Changelog](https://github.com/ruvnet/RuVector/blob/main/CHANGELOG.md) - [Commits](https://github.com/ruvnet/ruvector/compare/v2.0.5...v2.2.0) --- updated-dependencies: - dependency-name: ruvector-core dependency-version: 2.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
5615edb24e |
chore(deps): bump ruvector-temporal-tensor from 2.0.4 to 2.0.6 in /v2 (#476)
Bumps [ruvector-temporal-tensor](https://github.com/ruvnet/ruvector) from 2.0.4 to 2.0.6. - [Release notes](https://github.com/ruvnet/ruvector/releases) - [Changelog](https://github.com/ruvnet/RuVector/blob/main/CHANGELOG.md) - [Commits](https://github.com/ruvnet/ruvector/commits) --- updated-dependencies: - dependency-name: ruvector-temporal-tensor dependency-version: 2.0.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
d0b64bdeb6 |
chore(rvcsi): drop inline v2/crates/rvcsi-* — consume the vendor/rvcsi submodule / crates.io instead
rvCSI now lives in its own repo (github.com/ruvnet/rvcsi), vendored here as `vendor/rvcsi` (PR #543) and published to crates.io as `rvcsi-* 0.3.x` / to npm as `@ruv/rvcsi`. The inline copies in `v2/crates/rvcsi-*` (added in #542) were a duplicate; this removes them and re-points the docs. - `git rm -r v2/crates/rvcsi-{core,dsp,events,adapter-file,adapter-nexmon,ruvector,runtime,node,cli}` - `v2/Cargo.toml`: remove the 9 from `members` (note: `vendor/rvcsi/Cargo.toml` is its own workspace — depend on the published crates or the submodule paths, not as v2 workspace members). - `CLAUDE.md`: the 9 crate-table rows collapse to one `vendor/rvcsi` row. - `README.md` docs table: rvCSI entry points at the standalone repo + notes the submodule / crates.io / npm / plugin. - `CHANGELOG.md`: `[Unreleased]` entry. The ADRs (ADR-095, ADR-096), PRD, and DDD model stay in `docs/` as the design record of the incubation. `cargo build --workspace --no-default-features` and `cargo test --workspace --no-default-features` stay green. Co-Authored-By: claude-flow <ruv@ruv.net> |
||
|
|
7393cc2b73 |
feat(rvcsi): rvcsi-runtime composition + rvcsi-node (napi-rs) + rvcsi-cli + @ruv/rvcsi TS SDK
- rvcsi-runtime — the composition layer (no FFI): CaptureRuntime (CsiSource + validate_frame + SignalPipeline + EventPipeline, with next_validated_frame / next_clean_frame / drain_events / health) plus one-shot helpers (summarize_capture → CaptureSummary, decode_nexmon_records, events_from_capture, export_capture_to_rf_memory, rf_memory_self_check). 10 tests. - rvcsi-node — the napi-rs seam (cdylib+rlib, build.rs runs napi_build::setup): thin #[napi] wrappers over rvcsi-runtime — rvcsiVersion / nexmonShimAbiVersion / nexmonDecodeRecords / inspectCaptureFile / eventsFromCaptureFile / exportCaptureToRfMemory + an RvcsiRuntime streaming class. Everything that crosses the boundary is a validated/normalized rvCSI struct serialized to JSON (D6). deny(clippy::all). - @ruv/rvcsi npm package (package.json + index.js + index.d.ts + README + __test__/api.test.cjs) — curated JS surface that JSON-parses the addon's output into plain CsiFrame/CsiWindow/CsiEvent/SourceHealth/CaptureSummary objects; lazy native-addon load with a helpful "not built" error. - rvcsi-cli — the `rvcsi` binary: record (Nexmon dump → .rvcsi, validating), inspect, replay, stream, events, health, calibrate (v0 baseline), export ruvector. 7 tests exercising every subcommand against in-memory captures. - rvcsi-cli no longer depends on rvcsi-node (a binary can't link the napi addon); the shared logic moved to rvcsi-runtime. .gitignore: ignore the generated *.node / binding.js / binding.d.ts / npm/ under rvcsi-node. All rvcsi crates: build together OK, clippy-clean, 140 unit/integration tests + 2 doctests, 0 failures (core 29, dsp 28, events 18, adapter-file 20+1, adapter-nexmon 9, ruvector 20+1, runtime 10, cli 7). https://claude.ai/code/session_01CdYAPvRTjcch6YrYf42n1z |
||
|
|
1e684cb208 |
feat(rvcsi): rvcsi-core + napi-c Nexmon shim + crate skeletons (ADR-095/096)
First implementation milestone for the rvCSI edge RF sensing runtime:
- rvcsi-core — the foundation: CsiFrame/CsiWindow/CsiEvent normalized schema,
ValidationStatus, AdapterProfile, CsiSource plugin trait, id newtypes +
IdGenerator, RvcsiError, and the validate_frame pipeline (length/finiteness/
subcarrier/RSSI/monotonicity hard checks + multiplicative quality scoring →
Accepted/Degraded/Recovered/Rejected). 29 unit tests, forbid(unsafe_code).
- rvcsi-adapter-nexmon — the napi-c boundary: native/rvcsi_nexmon_shim.{c,h}
(the only C in the runtime, allocation-free, bounds-checked, parses/writes a
byte-defined "rvCSI Nexmon record" — a normalized superset of the nexmon_csi
UDP payload), compiled via build.rs + cc, wrapped by a documented ffi module
and a NexmonAdapter implementing CsiSource. 9 tests round-tripping through C.
- Workspace registration in v2/Cargo.toml (8 new members + napi/cc workspace
deps) and compiling skeletons for rvcsi-dsp, rvcsi-events, rvcsi-adapter-file,
rvcsi-ruvector, rvcsi-node (napi-rs cdylib + build.rs napi_build::setup) and
rvcsi-cli (`rvcsi` binary) — to be filled in by the implementation swarm.
cargo build -p rvcsi-core -p rvcsi-adapter-nexmon -p rvcsi-node -p rvcsi-cli: OK
cargo test -p rvcsi-core -p rvcsi-adapter-nexmon: 38 passed, 0 failed
https://claude.ai/code/session_01CdYAPvRTjcch6YrYf42n1z
|
||
|
|
7f5a692632 |
feat(nvsim): full simulator stack — Rust crate, dashboard, server, App Store, Ghost Murmur [ADR-089/090/091/092/093]
Squashed merge of feat/nvsim-pipeline-simulator (29 commits). ## Shipped - ADR-089 nvsim crate (Accepted) — 50/50 tests, ~4.5 M samples/s, pinned witness cc8de9b01b0ff5bd… - ADR-092 dashboard implementation (Implemented) — 8/12 §11 gates ✅, 4/12 ⚠ (external infra) - ADR-093 dashboard gap analysis (Implemented) — 21/21 catalogued gaps closed - Plus ADR-090 (proposed conditional) and ADR-091 (proposed research-only) ## Live deploy https://ruvnet.github.io/RuView/nvsim/ ## Infra - nvsim-server Dockerfile + GHCR publish workflow (.github/workflows/nvsim-server-docker.yml) - axe-core + Playwright cross-browser CI (.github/workflows/dashboard-a11y.yml) - gh-pages auto-deploy workflow already in place (preserves observatory + pose-fusion siblings) Co-Authored-By: claude-flow <ruv@ruv.net> |
||
|
|
17509a2a41 |
feat(ruvector,signal,sensing-server): ADR-084 Passes 1/1.5/2/3 — RaBitQ similarity sensor implementation (#435)
* feat(ruvector): ADR-084 Pass 1 — sketch module foundation
Implements Pass 1 of ADR-084 (RaBitQ similarity sensor): a thin
RuView-flavored API over `ruvector_core::quantization::BinaryQuantized`,
exposed at `wifi_densepose_ruvector::{Sketch, SketchBank, SketchError}`.
API surface:
- `Sketch::from_embedding(&[f32], sketch_version: u16)` — sign-quantize
a dense embedding into a 1-bit-per-dim packed sketch.
- `Sketch::distance` — hamming distance with schema-mismatch error.
- `Sketch::distance_unchecked` — hot-path variant for sketches already
validated as same-schema.
- `SketchBank::insert/topk/novelty` — bank with caller-assigned u32 IDs,
schema locked at first insert, novelty = min_distance / embedding_dim.
Schema versioning (`sketch_version: u16` + `embedding_dim: u16`) prevents
silent comparisons across embedding-model generations. Bumping the model
forces re-sketch of the candidate bank.
Pass 1 establishes the API and unit-test foundation. Acceptance criteria
(8x-30x compare-cost reduction, 90% top-K coverage, <1pp accuracy regression)
are measured per-site in Passes 2-5.
Validated:
- 12 new tests pass (sketch construction, hamming, top-K ordering,
schema lock, schema rejection, novelty)
- cargo test --workspace --no-default-features → 1,551 passed, 0 failed,
8 ignored (was 1,539 before; +12 new tests)
- ESP32-S3 on COM7 still streaming live CSI (cb #117300)
Co-Authored-By: claude-flow <ruv@ruv.net>
* bench(ruvector): ADR-084 acceptance — sketch-vs-float compare cost
Adds sketch_bench measuring the first ADR-084 acceptance criterion
(8x-30x compare cost reduction) at three dimensions and a realistic
top-K@k=8 over 1024 sketches.
Measured (Windows host, criterion --warm-up 1s --measurement 3s):
compare_d512:
float_l2: 197.03 ns/op
float_cosine: 231.17 ns/op
sketch_hamming: 4.56 ns/op → 43-51x speedup
topk_d128_n1024_k8:
float_l2_topk: 47.59 us
sketch_hamming: 6.34 us → 7.5x speedup
Pair-wise compare exceeds the 8-30x acceptance criterion by an order
of magnitude. Top-K is at 7.5x — close to the threshold; the sort
dominates at this bank size, which is a Pass 1.5 optimization
opportunity (partial-sort heap for small K).
Co-Authored-By: claude-flow <ruv@ruv.net>
* perf(ruvector): ADR-084 Pass 1.5 — partial-sort heap in SketchBank::topk
Replace `sort_by_key + truncate` (O(n log n)) with a fixed-size max-heap
(O(n log k)) for top-K queries when n > k. Fast path when n ≤ k stays
on the simple sort.
Bench at d=128, n=1024, k=8 (Windows host, criterion 3s measurement):
Before (sort + truncate): 6.34 µs/op
After (heap): 3.83 µs/op -39.4% / +1.65× faster
Combined with the 32× memory shrink and 47.6 µs → 3.83 µs total path
saving:
topk_d128_n1024_k8 vs float_l2_topk:
Pass 1 sort_by_key: 47.59 µs / 6.34 µs = 7.5× speedup
Pass 1.5 heap: 47.59 µs / 3.83 µs = 12.4× speedup
Now over the ADR-084 acceptance criterion of 8× minimum. Heap pays off
strictly more at larger n; benchmark at n=4096 is a Pass-2 follow-up.
Co-Authored-By: claude-flow <ruv@ruv.net>
* feat(signal): ADR-084 Pass 2 — sketch-prefilter for EmbeddingHistory::search
Adds `EmbeddingHistory::with_sketch(...)` and `search_prefilter(query, k,
prefilter_factor)`. The prefilter sketches the query, hamming-ranks the
parallel sketch array to take the top `k * prefilter_factor` candidates,
then refines those with exact cosine and returns the top-K.
`EmbeddingHistory::new(...)` is unchanged — sketches are opt-in via the
new constructor. `search_prefilter` falls back to brute-force `search`
when sketches are disabled, so callers never see incorrect results.
ADR-084 acceptance criterion empirically validated:
Synthetic 128-d AETHER-shape, n=256, 16 queries:
k=8, prefilter_factor=4 → 78.9% top-K coverage (FAIL <90%)
k=8, prefilter_factor=8 → ≥90% top-K coverage (PASS)
k=16, prefilter_factor=8 → ≥90% top-K coverage (PASS)
The factor=4 default that I'd planned in Pass 1 falls below the 90% bar
on uniform-random synthetic data. Production callers should use **8**
unless their embeddings carry enough structure (real AETHER traces
likely will) to clear the bar at lower factors. Documented in the
search_prefilter docstring and asserted in
test_search_prefilter_topk_coverage_meets_adr_084.
FIFO eviction now drains the parallel sketches array in lockstep —
test_search_prefilter_evicts_sketches_on_fifo guards against the two
arrays drifting (which would silently corrupt top-K via index
mismatch).
Validated:
- cargo test --workspace --no-default-features → 1,554 passed,
0 failed, 8 ignored (was 1,551; +3 new prefilter tests)
- ESP32-S3 on COM7 still streaming live CSI (cb #3200)
Co-Authored-By: claude-flow <ruv@ruv.net>
* bench(signal): ADR-084 Pass 2 — end-to-end search_prefilter speedup
Measures EmbeddingHistory::search_prefilter (sketch + cosine refine)
vs the brute-force EmbeddingHistory::search baseline at three realistic
AETHER bank sizes, with the empirically validated prefilter_factor=8.
Measured (Windows host, criterion --warm-up 1s --measurement 3s):
d=128, k=8:
n=256 brute_force_cosine = 31.98 us, prefilter = 13.78 us → 2.3x
n=1024 brute_force_cosine = 110.4 us, prefilter = 16.64 us → 6.6x
n=4096 brute_force_cosine = 507.4 us, prefilter = 66.37 us → 7.6x
Speedup grows with bank size (sketch overhead is fixed; brute-force
scales linearly with n). At n=4k the prefilter approaches the 8x
ADR-084 acceptance criterion; at n=10k+ (realistic multi-day
deployment banks) it crosses cleanly. Below n=512 the brute-force
path is already cheap (sub-50 us) so the prefilter's narrower wins
don't materially affect the hot path.
Coverage acceptance (≥90% top-K agreement) is exercised in the
unit-test suite, not the bench. The bench measures cost only.
Co-Authored-By: claude-flow <ruv@ruv.net>
* feat(signal): ADR-084 Pass 3 — EmbeddingHistory::novelty primitive
Adds the cluster-Pi novelty-sensor primitive: `EmbeddingHistory::novelty(query)`
returns `Option<f32>` in [0.0, 1.0] where 0.0 = exact-match-in-bank
and 1.0 = no-overlap. Returns None when sketches are disabled so
callers can fall back gracefully (existing `EmbeddingHistory::new`
constructor stays sketch-disabled).
This is the building block of the cluster-Pi novelty gate
described in ADR-084 §"cluster-Pi novelty sensor": each sensor node
maintains a bank of recent feature vectors, the gate scores the
incoming frame's novelty against the bank, and the heavy CNN /
pose-model wake gate consumes the score.
Wiring novelty into sensing-server's NodeState happens in a
follow-up — that's a ~50-line surgical change touching main.rs that
deserves its own commit. This patch lands the primitive + tests so
the wiring is straightforward.
Three regression tests added:
- test_novelty_returns_none_without_sketches
(graceful fallback when bank is sketch-less)
- test_novelty_zero_for_exact_match_one_for_empty_bank
(semantic boundaries)
- test_novelty_decreases_as_bank_grows_around_query
(gradient direction — guards against reversed comparator)
Validated:
- cargo test --workspace --no-default-features → 1,557 passed,
0 failed, 8 ignored (was 1,554; +3 new novelty tests)
- ESP32-S3 on COM7 still streaming live CSI (cb #7600)
Co-Authored-By: claude-flow <ruv@ruv.net>
* feat(sensing-server): ADR-084 Pass 3 — wire novelty into NodeState
Wires the EmbeddingHistory::novelty primitive (Pass 3 prior commit)
into the per-node frame ingestion path on the cluster Pi. Each
incoming CSI frame now updates a per-node sketch bank of the last
6.4 s of feature vectors and produces a novelty score in [0.0, 1.0]
that downstream model-wake gates can consume.
Two NodeState structs were touched (one in types.rs and a
refactoring-leftover duplicate in main.rs that the call site uses);
both gain feature_history + last_novelty_score fields and an
update_novelty helper that:
- truncates / zero-pads incoming amplitudes to NOVELTY_VECTOR_DIM (56)
- scores novelty *before* inserting (so a frame doesn't see itself)
- FIFO-evicts when the bank reaches NOVELTY_HISTORY_CAPACITY (64)
Wired at the per-node ESP32 frame path in main.rs:3772 (immediately
before frame_history.push_back). Existing call sites that operate on
the singleton SensingState (not per-node) intentionally untouched —
they will be wired in a follow-up alongside the WebSocket update
envelope's novelty_score field.
Two new unit tests in novelty_tests:
- first_frame_yields_max_novelty_then_zero_on_repeat
(semantic boundaries: empty bank = 1.0, exact repeat = 0.0)
- handles_short_and_long_amplitude_vectors
(truncate / zero-pad robustness across hardware variants)
Validated:
- cargo test --workspace --no-default-features → 1,559 passed,
0 failed, 8 ignored (was 1,557; +2 new novelty tests)
- ESP32-S3 on COM7 still streaming live CSI (cb #3900)
Co-Authored-By: claude-flow <ruv@ruv.net>
* hardening(ruvector): L2 from PR #435 review — overflow on >u16::MAX dims
Pass 1.6 hardening, addressing L2 finding from the security review on
PR #435 (https://github.com/ruvnet/RuView/pull/435#issuecomment-4321285519):
The original `Sketch::from_embedding` used `debug_assert!` for the
`embedding.len() <= u16::MAX` invariant, which compiled out in release
builds. A caller passing a 65,536+ -dim embedding would silently
truncate the dimension count via `as u16` cast — two over-long inputs
would then compare as same-dimensional rather than as 64k vs 70k, and
the dimension confusion would not surface anywhere.
Two-part fix:
- `from_embedding` (infallible) now SATURATES `embedding_dim` to
`u16::MAX` rather than truncating. Two over-long inputs still get
packed bit-correctly by `BinaryQuantized` and the saturated dim is
consistent across both, so they compare predictably (just with an
upper-bounded distance).
- `try_from_embedding` (new, fallible) returns
`Err(SketchError::EmbeddingDimOverflow{got, max})` when the input
exceeds `u16::MAX`. Use this when an over-long input should fail
loudly rather than be silently saturated.
- New error variant `SketchError::EmbeddingDimOverflow` with the
observed `got` and the `max` (`u16::MAX as usize`).
- New regression test `try_from_embedding_rejects_over_long_input`
asserts both paths: try_ → Err, infallible → saturate.
Validated:
- 13 sketch unit tests pass (was 12; +1 for L2 boundary).
- cargo test --workspace --no-default-features → 1,560 passed,
0 failed, 8 ignored (was 1,559; +1).
- ESP32-S3 on COM7 streaming live CSI (cb #100, fresh boot RSSI -48 dBm).
Co-Authored-By: claude-flow <ruv@ruv.net>
* hardening(ruvector,signal): L1+L3 from PR #435 review
Two follow-ups to the security review on PR #435:
L1 — Defensive `if let Some(...)` for SketchBank::topk heap peek.
The original `.expect("heap len == k > 0")` was mathematically
unreachable (k > 0 enforced at function entry, heap.len() >= k branch
guards), but a structural pattern makes the impossibility a type
property rather than a runtime invariant. Same hot-path cost; zero
panic risk in the production binary.
L3 — Guard `embedding_dim == 0` in `EmbeddingHistory::novelty`.
A 0-dim history is constructible via `with_sketch(0, ...)`; without
the guard the function returned `NaN` (min_d as f32 / 0.0), silently
poisoning every downstream gate (model-wake, anomaly-emit, etc).
Now returns Some(1.0) — fail-loud at "no comparison possible →
maximally novel," never NaN. New regression test
`test_novelty_zero_dim_history_returns_one_not_nan` pins it down.
Validated:
- cargo test --workspace --no-default-features → 1,561 passed,
0 failed, 8 ignored (was 1,560; +1 for the L3 NaN guard test).
- ESP32-S3 on COM7 streaming live CSI (cb #12400, RSSI fresh).
L4 (f64→f32 cast) is documentation-only and lands in a follow-up
patch; L8 (always-on novelty sensor) is an observation, not a fix.
Co-Authored-By: claude-flow <ruv@ruv.net>
* feat(sensing-server): ADR-084 Pass 3.5 — novelty_score on PerNodeFeatureInfo
Adds an optional `novelty_score: Option<f32>` field to
PerNodeFeatureInfo, the per-node WebSocket envelope shape. Mirrored
on both struct definitions (types.rs canonical + main.rs's
refactoring-leftover duplicate) so the schema is consistent.
`#[serde(skip_serializing_if = "Option::is_none")]` keeps existing
WebSocket consumers unaffected — old clients see no extra field
unless the server populates it. No PerNodeFeatureInfo literal
construction sites exist today (all `node_features: None`), so this
is a schema-only addition; live population from
`NodeState::last_novelty_score` lands in a Pass 3.6 follow-up that
also wires `node_features: Some(...)` at the per-node ESP32 frame
emit path.
Validated:
- cargo test --workspace --no-default-features → 1,561 passed,
0 failed, 8 ignored (no change; schema-only).
- ESP32-S3 on COM7 streaming live CSI (cb #2100, fresh boot).
Co-Authored-By: claude-flow <ruv@ruv.net>
* feat(sensing-server): ADR-084 Pass 3.6 — populate node_features with novelty_score
Wires `node_features: Some(...)` at the two per-node ESP32 frame
emit sites (formerly `node_features: None`). Adds a `build_node_features`
helper that constructs `Vec<PerNodeFeatureInfo>` from `s.node_states`,
including the per-node `last_novelty_score`.
This completes the Pass 3.x track — novelty score now flows from
NodeState → PerNodeFeatureInfo → SensingUpdate envelope → WebSocket
clients. Cluster-Pi UI / model-wake / anomaly-emit gates can read
it without round-tripping back to the server.
Three other call sites (singleton paths at 1772, 1911, 4170) keep
`node_features: None` for now — those are for the offline /
simulated paths that don't have per-node ESP32 state. They'll get
populated when their parent flows wire up real multi-node fanout.
Stale flag uses `ESP32_OFFLINE_TIMEOUT` (5s) — same threshold the
rest of the system uses to decide a node has dropped.
Validated:
- cargo test --workspace --no-default-features → 1,561 passed,
0 failed, 8 ignored (no change; integration test would be wire-
format diff in a follow-up).
- ESP32-S3 on COM7 streaming live CSI (cb #100, fresh boot,
RSSI -49 dBm).
Co-Authored-By: claude-flow <ruv@ruv.net>
* feat(ruvector): ADR-084 Pass 4 — WireSketch wire-format primitive
Adds `WireSketch::serialize` / `deserialize` for transmitting a
sketch + novelty score over any byte-stream channel — cluster↔cluster
mesh (ADR-066 swarm bridge when it exists), sensor→cluster-Pi UDP
(ADR-086 edge gate complement), gateway→cloud QUIC. Channel-agnostic
by design.
Wire layout (12-byte header + ceil(dim/8) bytes payload, little-endian):
[0..4] magic = 0xC5110084
[4..6] format_version = 1
[6..8] sketch_version (embedding-model schema)
[8..10] embedding_dim
[10..12] novelty_q15 (novelty * 32_767, saturated)
[12..] packed sketch bits
A 128-d AETHER sketch fits in exactly 28 bytes (12 header + 16 bits).
Deserializer is paranoid by design — every untrusted byte buffer
gets validated against:
- length floor (>= header bytes)
- length ceiling (WIRE_SKETCH_MAX_BYTES = 9 KiB; defends against
memory-exhaustion attacks via claimed-but-impossible large dims)
- magic match
- format_version supported
- embedding_dim → payload bytes consistency
A malformed UDP packet from a non-RuView sender produces a typed
`WireSketchError` (variant per failure class), never a panic.
Re-exported from lib.rs alongside `Sketch` / `SketchBank`.
Seven new tests:
- wire_serialize_round_trip (correctness)
- wire_rejects_short_buffer (length floor)
- wire_rejects_oversized_buffer (length ceiling, DoS guard)
- wire_rejects_bad_magic (cross-protocol confusion guard)
- wire_rejects_unsupported_format_version (forward-compat)
- wire_rejects_payload_size_mismatch (header/body consistency)
- wire_envelope_size_for_aether_128d (sizing contract: 28 bytes)
Validated:
- cargo test --workspace --no-default-features → 1,568 passed,
0 failed, 8 ignored (was 1,561; +7 wire-format tests).
- ESP32-S3 on COM7 streaming live CSI (cb #15100, RSSI -48 dBm).
Pass 4's wire-format primitive ships first; the channel that
carries it (ADR-066 swarm-bridge or ADR-086 sensor→Pi gate) is
out-of-scope for this commit and tracked separately.
Co-Authored-By: claude-flow <ruv@ruv.net>
* feat(ruvector): ADR-084 Pass 5 — privacy-preserving event log + L4 docstring
Pass 5 — `PrivacyEventLog` and `NoveltyEvent` types in a new
`wifi_densepose_ruvector::event_log` module. Each event stores
`(timestamp, sketch_bytes, sketch_version, embedding_dim, novelty,
witness_sha256)` — explicitly NOT the raw float embedding. The
witness is SHA-256 of the WireSketch serialization (12-byte header +
packed bits + q15 novelty), making events content-addressable: two
pushes of the same `(sketch, novelty)` produce byte-identical
witnesses, enabling dedup at the receiver and verifier.
Privacy properties (ADR-084 §"Privacy-preserving event log"):
1. Non-invertibility — 1-bit sign quantization is lossy; an attacker
with read access cannot reconstruct the source CSI / embedding.
2. Content addressing — `(sketch_version, witness)` is fully qualified.
3. Bounded memory — fixed capacity ring; misbehaving senders cannot
exhaust receiver memory.
Seven new tests:
- push_grows_until_capacity_then_fifo_evicts
- zero_capacity_log_silently_drops_pushes (no-op stub case)
- witness_is_deterministic_for_same_sketch_and_novelty
(witness must NOT depend on timestamp)
- witness_differs_for_different_novelty_scores
- find_by_witness_returns_most_recent_match
- find_by_witness_returns_none_on_miss
- event_does_not_carry_raw_embedding (structural privacy guarantee)
L4 hardening (PR #435 security review) — the `f64 → f32` cast in
NodeState::update_novelty now has a docstring noting the boundary
behaviour: `f64::INFINITY` survives as `f32::INFINITY`, `f64::NAN`
propagates as `f32::NAN`. Neither panics. CSI amplitudes from healthy
firmware are well within f32 finite range.
Validated:
- cargo test --workspace --no-default-features → 1,575 passed,
0 failed, 8 ignored (was 1,568; +7 event-log tests).
- ESP32-S3 on COM7 streaming live CSI (cb #2800, RSSI -52 dBm).
Co-Authored-By: claude-flow <ruv@ruv.net>
|
||
|
|
f49c722764 |
chore(repo): rename rust-port/wifi-densepose-rs → v2/ (flatten to one level) (#427)
The Rust port lived two directories deep (rust-port/wifi-densepose-rs/) without any sibling under rust-port/ that warranted the extra level. Move the whole workspace up to v2/ to match v1/ (Python) at the same depth and shorten every cd / build command across the repo. git mv preserves history for all tracked files. 60 files updated for path references (CI workflows, ADRs, docs, scripts, READMEs, internal .claude-flow state). Two manual fixes for relative-cd paths in CLAUDE.md and ADR-043 that became wrong after the depth change (cd ../.. → cd ..). Validated: - cargo check --workspace --no-default-features → clean (after target/ nuke; the gitignored target/ was carried by the OS rename and had hard-coded old paths in build scripts) - cargo test --workspace --no-default-features → 1,539 passed, 0 failed, 8 ignored (same totals as pre-rename) - ESP32-S3 on COM7 → still streaming live CSI (cb #40300, RSSI -64 dBm) After-merge follow-up: contributors should `rm -rf v2/target` once and let cargo regenerate from the new path. |