Commit Graph

20 Commits

Author SHA1 Message Date
rUv 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>
2026-06-11 17:02:23 -04:00
rUv 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>
2026-06-10 15:21:09 -04:00
rUv 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 13b08927 which committed an intermediate M7 state with one
failing test. This lands the M7 agent's convergence fixes and the security
review's timeout hardening.

## Fixes
- swarm_sim.rs: min-separation nudge before collision metric (0 collisions
  with staggered starts) + Phase-3 convergence assist that vectors the nearest
  idle peer toward a single-drone CSI contact so multi-view fusion can fire
- http_backend.rs: add 5s request timeout to reqwest client (security review
  Medium finding — a dead daemon would otherwise hang the swarm step loop)

## Security review verdict (HttpRufloBackend)
Safe to merge. No credentials in requests, serde_json prevents injection,
fail-open on daemon-down is documented and appropriate for SAR missions,
MAVLink passed as structured text (not raw bytes). Timeout fix applied.

## Tests
- --no-default-features: 87/87 pass
- --features ruflo,itar-unrestricted: 100/100 pass

Co-Authored-By: claude-flow <ruv@ruv.net>

* perf(swarm): add PPO training-throughput benchmark + fix bench crate-name imports

- bench_ppo_update: PPO update over 64-transition buffer — 244 µs median
- fix: bench imports referenced stale `wifi_densepose_swarm` (pre-rename),
  corrected to `ruview_swarm` so the bench target compiles

M6 benchmark suite now 5/5 compiling and running. Tests unchanged: 87/100.

Co-Authored-By: claude-flow <ruv@ruv.net>

* feat(swarm): real Candle autodiff PPO + A-MAPPO role attention + GPU training (M4)

Replaces the finite-difference PPO placeholder with a real GPU-capable Candle
0.9 autodiff trainer, adds A-MAPPO heterogeneous-role attention, a runnable
training binary, and right-sized GCP/local launch scripts. This is the unlock
that makes "GPU long training cycles" actually mean something — the previous
ppo_update did no gradient descent.

## Real autodiff PPO (feature `train`, optional `cuda`)
- candle_ppo.rs: CandleActorCritic (64→128→64 MLP + action/value heads +
  learnable log_std), CandlePpoConfig, CandleTrainer with GAE and a genuine
  optimizer.backward_step over the network. select_device() picks CUDA when
  built --features cuda and a GPU is present, else CPU.
- Verified: 5-episode CPU smoke run shows value_loss 12643→12375 (critic
  actually learning); safetensors checkpoint saved. Placeholder never moved weights.

## A-MAPPO heterogeneous-role attention (role_attention.rs, always compiled)
Addresses the four sensor-vs-relay edge cases:
- relay attention floor (prevents collapse — relays produce no CSI)
- role-segmented sensor/relay attention pools (variable neighbor cardinality)
- sensor-gated triangulation-geometry penalty (protects 3-view fusion baseline,
  ADR-148 §4.2 — relays not dragged into triangulation geometry)
- one-hot role embeddings for keys

## Training binary
- src/bin/train_marl.rs (required-features=["train"], excluded from default build)
- CLI: --episodes --drones --profile --steps --checkpoint-dir --checkpoint-every
- Wires CandleTrainer to the SwarmOrchestrator rollout loop; GAE + PPO update
  per episode; periodic safetensors checkpoints

## Right-sized launch (scripts/gcp/)
- provision_marl.sh: g2-standard-16 (1× L4, 16 vCPU, ~$1.40/hr) — NOT the
  $29/hr A100×8 box. MARL is rollout-bound not matmul-bound; ~21× cheaper.
- run_marl_train.sh: GCP rsync + train + checkpoint pull
- run_marl_train_local.sh: local RTX 5080, $0
- A100×8 provision_training.sh left for OccWorld (which saturates the GPUs)

## Tests
- --no-default-features: 91/91 (87 + 4 role_attention)
- --features train: 96/96 (+ 5 candle_ppo, incl. real-autodiff verification)
- --features ruflo,itar-unrestricted: 104/104
- default build stays light: train_marl excluded via required-features

Co-Authored-By: claude-flow <ruv@ruv.net>

* docs(adr-148): mark M4 complete — real GPU autodiff training; overall 98%

Co-Authored-By: claude-flow <ruv@ruv.net>

* feat(swarm): training visualizer — JSONL telemetry + self-contained HTML viewer

Adds an offline, dependency-free visualization for the drone training system:
a top-down swarm replay synced with training-metric curves, fed by a JSONL
telemetry log the trainer emits. No server, no build step, no CDN.

## Telemetry recorder (integration/telemetry.rs, always compiled, no new deps)
- TelemetryRecorder writes newline-delimited JSON: one `meta` (profile, area,
  ground-truth victims), many `step` (per-tick drone x/y/heading/battery/detection
  + coverage%), and per-episode `episode` (mean_return, policy_loss, value_loss).
- Written by hand (no serde_json) so it stays in the default build; 2 tests.

## train_marl telemetry flags
- `--telemetry FILE` writes the log; `--telemetry-episode N` selects which
  episode's spatial steps to record (metrics recorded for all episodes).

## Visualizer (viz/swarm_viz.html — single file, vanilla JS + canvas)
- LEFT: top-down replay — heading-oriented drone triangles (cyan/lime on
  detection), victim markers, growing coverage heatmap, detection pulse rings,
  play/pause/scrub/speed controls + live coverage/detection readout.
- RIGHT: three autoscaled line charts (mean return, policy loss, value loss)
  over episodes, hand-drawn (no chart library).
- Loads via file picker/drag-drop or auto-fetches the bundled sample; dark
  drone-ops theme; graceful degradation on file:// CORS.
- viz/sample_telemetry.jsonl: real 30-episode / 4-drone / 400×400 m run
  (value_loss 20052→7154 — visible critic learning). Parses 1 meta / 60 step / 30 episode.

## Usage
  cargo run --release -p ruview-swarm --features train,cuda --bin train_marl -- \
      --episodes 5000 --telemetry run.jsonl
  open v2/crates/ruview-swarm/viz/swarm_viz.html  # load run.jsonl

Tests unchanged (91 default / 96 train / 104 ruflo+itar); telemetry adds 2.

Co-Authored-By: claude-flow <ruv@ruv.net>

* feat(swarm): selectable flight + self-learning patterns, wired into training + viz

Adds multiple flight/coverage-optimization strategies and self-learning
strategies, selectable from the trainer, and fixes drone clustering — the
demo sweep now covers 36% of the area (was ~0.9%) with 4 disjoint strips.

## Flight patterns (planning/patterns.rs) — `FlightPattern`
- PartitionedLawnmower (new default): area split into per-drone strips → no
  overlap, coverage scales ~linearly with swarm size (clustering fix)
- Boustrophedon (baseline), Spiral, Pheromone (stigmergic), PotentialField,
  LevyFlight. from_str/name/all + next_target(&PatternContext).

## Self-learning patterns (marl/learning.rs) — `LearningPattern`
- Mappo (CTDE centralized critic), Ippo (independent, jamming-robust),
  MappoCuriosity (count-based intrinsic novelty), MetaRl (MAML fast-adapt).
- CuriosityModule (visit_bonus = beta/sqrt(count), novelty decays on revisit),
  MetaAdapter (base + fast-weights, reset_fast/consolidate), shaped_reward().

## Trainer wiring (bin/train_marl.rs)
- --flight-pattern {boustrophedon|partitioned|spiral|pheromone|potential|levy}
- --learn-pattern  {mappo|ippo|curiosity|meta}
- Rollout now moves each drone per the selected FlightPattern (PatternContext
  with visited trail + live peers), curiosity-shapes the reward, and logs
  CTDE vs independent. Telemetry meta profile carries the pattern labels so the
  viewer header shows `flight=… · learn=…`.

## Verification
- Browser pass (viz at localhost:8777): partitioned run renders 4 distinct
  serpentine coverage bands, header shows the patterns, final coverage 36.3%,
  scrubber/speed/playback work, ZERO console errors. Screenshot confirmed.
- Regenerated viz/sample_telemetry.jsonl: 1 meta / 120 step / 30 episode,
  coverage 0.9% → 36.3%.

## Tests
- --no-default-features: 103/103 (was 91; +6 patterns +6 learning)
- --features train: 108/108

Co-Authored-By: claude-flow <ruv@ruv.net>

* feat(swarm): add flight-pattern telemetry presets for the visualizer

5 loadable presets (verified browser-distinct, physics-ordered coverage):
pheromone ~44% > potential ~40% > partitioned 36% > spiral ~13% > levy ~5%.
Load any in viz/swarm_viz.html to compare flight strategies without retraining.

Co-Authored-By: claude-flow <ruv@ruv.net>

* chore(swarm): clippy-clean + publish guard for ruview-swarm

- ruview-swarm src is now 0 clippy warnings across default/train/full feature
  sets (derive Default, targeted allows for intentional from_str + bounded
  casts + borrow-required index loops; removed redundant unsigned .max(0))
- publish = false until PR merges, internal path-deps publish in order, and
  ITAR (USML VIII(h)(12)) export sign-off — prevents accidental public publish

Tests unchanged: 103 default / 108 train / 116 ruflo+itar / 120 full+train.
(6 remaining clippy warnings are pre-existing in dependency wifi-densepose-core,
 out of scope for this crate.)

Co-Authored-By: claude-flow <ruv@ruv.net>

* ci(swarm): add ruview-swarm CI guard

Path-scoped guard for v2/crates/ruview-swarm/** (ADR-148). Complements the
main ci.yml (which only runs the default workspace tests):
- feature-matrix tests: default / train / ruflo+itar / full+train
- clippy -D warnings --no-deps (crate-own code only; dep warnings don't gate)
- train_marl bin builds under 'train' AND is excluded from the default build
- ITAR/publish guards: publish=false present, itar-unrestricted never in default

All steps verified locally green before commit.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-30 16:00:59 -04:00
ruv 8504638187 feat(signal): ADR-135 — empty-room baseline calibration
Operator-initiated calibration that records 30 s of stationary CSI,
emits a per-subcarrier baseline (amplitude mean+variance via Welford,
phase via circular sin/cos sums with von Mises dispersion), and gates
downstream stages on a deviation z-score. Plugs into multistatic
coherence gating, motion/presence detection, and the new ADR-134 CIR
estimator as a reference-subtracted input.

API surface (under wifi_densepose_signal):
  CalibrationConfig::{ht20, ht40, he20, he40}
  CalibrationRecorder { record(), finalize(), frames_recorded() }
  BaselineCalibration {
    subcarriers: Vec<SubcarrierBaseline>,
    deviation(&CsiFrame), subtract_in_place(&mut CsiFrame),
    to_bytes(), from_bytes()
  }
  CalibrationDeviationScore { amplitude_z_median, amplitude_z_max,
                              phase_drift_median, motion_flagged }
  CalibrationError { SubcarrierMismatch, TierMismatch,
                     InsufficientFrames, VersionMismatch, TruncatedBuffer }

Binary baseline format: magic 0xCA1B_0001 + u8 version=1 + u8 tier +
captured_at_unix_s (i64) + frame_count (u64) + num_subcarriers (u32) +
[SubcarrierBaseline; N] as 16 bytes each (amp_mean, amp_variance,
phase_mean, phase_dispersion as f32 LE). Hand-written serialisation so
the format is stable across Rust toolchain versions without serde drift.

CLI: new `wifi-densepose calibrate` subcommand binds a UDP listener
(0xC511_0001 frames), streams them through CalibrationRecorder, prints
a real-time z-score banner per ADR-135 §risk 1 (operator-may-be-moving),
aborts on sustained high deviation, and writes the binary baseline to
disk. Local UDP packet parser duplicated from sensing-server (per ADR
discussion — avoids cross-crate API churn).

Witness: cross-platform-deterministic SHA-256 over the per-subcarrier
quantised baseline profile (u16 LE at 1e-2/1e-4/1e-3, no sort) using
the lesson learnt from the CIR PR #837 libm-jitter fix. Hash:
d6bce07ecb1648e6936561df44bf4a3bfc17bb0ba5f692646b2301d105b52f67

CI guard: new "ADR-135 calibration witness proof (determinism guard)"
step under the Rust Workspace Tests job, adjacent to the existing
ADR-134 CIR guard. Regressions are unambiguously attributable.

Hardware-in-loop validation: full 600-frame capture exercised via the
new scripts/synth-csi-udp.py emitter targeting 127.0.0.1:5005. The CLI
binary received 600 frames at 20 Hz, z_med stable at ~0.7, motion
correctly NOT flagged, finalised baseline written to baseline.bin (860
bytes) with correct magic + version + timestamp in the header. Live
ESP32 capture from COM9 is operator follow-up — requires provisioning
the firmware's UDP target IP to match the host running the CLI.

Test results (cargo test -p wifi-densepose-signal --no-default-features):
  lib:                    382 pass / 0 fail / 1 ignored
  calibration_synthetic:   17 pass / 0 fail
  calibration_drift:        5 pass / 0 fail
  calibration_roundtrip:   10 pass / 0 fail
  cir_*:                    9 pass + 6 documented P2 ignores
  doctest:                 10 pass

Bench: 20 Criterion combinations registered
(recorder_record / recorder_finalize / deviation / record_600 /
to_bytes across HT20/HT40/HE20/HE40 tiers).

Witness: bash scripts/verify-calibration-proof.sh → VERDICT: PASS

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-28 18:57:08 -04:00
rUv 9e7fa83210
feat(signal): ADR-134 CSI→CIR via ISTA + NeumannSolver warm-start (#837)
* feat(signal): ADR-134 — CSI→CIR via ISTA + NeumannSolver warm-start

End-to-end first-class Channel Impulse Response estimation in the Rust
workspace. Bridges CSI (frequency domain) to CIR (delay domain) so
multistatic coherence gating, NLOS/LOS classification, and (at HT40+)
ToF ranging become tractable in `wifi-densepose-signal`.

Algorithm: ISTA L1 sparse recovery over a normalized DFT sub-matrix
sensing operator Φ ∈ ℂ^(K×G) with G = 3K (3× super-resolution). The
Tikhonov-regularised warm start re-uses `ruvector_solver::neumann::
NeumannSolver` — same call pattern as `fresnel.rs:280` and
`train/subcarrier.rs:225` — so no new crate dependencies.

Tiers supported: HT20 / HT40 / HE20 (Tier A-HE, C6) / HE40. The C6
HE-LTF tier is the preferred Tier A target whenever an 11ax AP is in
range; firmware substrate already shipped at v0.7.0-esp32 per ADR-110.

Measured performance (release, single CirEstimator shared across 12
links): HT20 2.72 ms / HE20 3.20 ms / HT40 13.43 ms / HE40 9.71 ms per
estimate(). HT20 12-link multistatic 17.7 ms — fits the 50 ms RuvSense
cycle; HT40 12-link 74 ms exceeds it and is flagged in ADR-134 §2.7 as
requiring Rayon parallelism or G=2K super-res reduction.

Measured Φ conditioning: κ(Φ) ≈ 1.00 identically across all tiers.
ADR-134 §2.3 was corrected — the C6 advantage is statistical SNR gain
(√(242/52) ≈ 2.16×) from more independent measurements, not improved
conditioning.

Witness: bit-deterministic SHA-256 over CirEstimator output on the
synthetic ADR-028 reference signal (100 frames, top-5 taps, 1e-6
quantization). Hash committed to expected_cir_features.sha256;
verify-cir-proof.sh wires the check into the existing witness bundle.

CI: cargo test --features cir + verify-cir-proof.sh added as separate
steps under the Rust Workspace Tests job; regressions are unambiguously
attributable.

Files:
- ADR + WITNESS-LOG-028 row 34 + CLAUDE.md module count (14 → 15)
- src/ruvsense/cir.rs (~540 LOC) + lib.rs re-exports + multistatic.rs
  wire-up (reversible via `use_cir_gate=false`)
- 3 integration tests + Criterion bench + 3 deterministic fixtures
- cir_proof_runner binary + sha256 + verify-cir-proof.sh

Test rate: 395 pass / 6 ignored (P2 ISTA hyperparameter tuning; see
#[ignore] reasons) / 0 fail. cargo check clean; verify-cir-proof.sh
VERDICT: PASS.

Co-Authored-By: claude-flow <ruv@ruv.net>

* fix(signal): make CIR witness cross-platform-deterministic

The first witness (Windows-generated hash 89704bfd…) failed on Linux CI
with a different hash (b36741bf…). Root cause: hashing `re`/`im` parts of
top-5 taps at 1e-6 precision is too tight against libm differences in
sin/cos/sqrt across glibc, MSVC, and Apple-clang. The previous
"top-5 sorted by magnitude" form also suffered from rank instability when
taps are near-tied — libm jitter could shuffle the ordering even when the
algorithm is unchanged.

New canonical form: full per-tap quantised-magnitude profile in natural
index order, no sort.

  - 156 taps × 2 bytes (u16 le) per frame = 312 bytes/frame.
  - Quantisation 1e-2 — robust to ~1e-3 float drift while still tripping
    on real algorithmic changes (e.g., a 10× lambda shift moves magnitudes
    by >1e-2).
  - No top-K selection — eliminates the unstable magnitude-sort step.

Regenerated expected_cir_features.sha256 — new hash 120bd7b1…

If the next CI run still mismatches, the cause is structural (rustfft SIMD
code path selection or NeumannSolver internal ordering), not magnitudes,
and the witness needs further coarsening or to be made platform-tagged.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-28 16:24:37 -04:00
ruv 19b445f9bb chore(adr-125 iter 1): fix C6 COM port + ship HAP-python reference impl
Two changes from the ADR-125 e2e bootstrap session:

1. CLAUDE.md hardware table: COM4 -> COM12 for ESP32-C6 (the C6 +
   Seeed MR60BHA2 dev kit now enumerates on COM12 on ruvzen, not
   COM4 as previously documented). Same fix applied to the ESP32-S3
   row (COM7 -> COM9) which CLAUDE.local.md already covered but the
   top-level table had not been updated.

2. scripts/hap-test-sensor.py — the ~80 LOC HAP-python sidecar that
   ADR-125 §2.1.a names as the reference implementation. Already
   running on ruv-mac-mini, already paired with operator's iPhone
   (paired_clients: 1), already round-trips a MotionDetected
   characteristic from a touch-file toggle through the HomePod (as
   Home Hub) to the Home app.

Substrate validated for iter 2+:
  - C6 provisioned on ruv.net (IP 192.168.1.179, ch 5, RSSI -38)
  - UDP frames: 44 packets in 8s @ mac-mini:5005 (~5.5 pps)
  - HAP bridge paired and live

Refs ADR-125, #794.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-25 16:14:51 -04:00
rUv 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)
2026-05-17 18:50:57 -04:00
ruv 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>
2026-05-12 23:00:23 -04:00
Claude d40411e6d7
feat(rvcsi): Raspberry Pi 5 (BCM43455c0) + Nexmon chip registry
Adds first-class support for the Raspberry Pi 5's WiFi chip (CYW43455 /
BCM43455c0 — the same 802.11ac wireless as the Pi 4 / Pi 3B+ / Pi 400, and the
chip with the most mature nexmon_csi support), plus a registry of the other
Nexmon-supported Broadcom/Cypress chips.

rvcsi-adapter-nexmon — new `chips.rs`:
- `NexmonChip` (Bcm43455c0, Bcm43436b0, Bcm4366c0, Bcm4375b1, Bcm4358, Bcm4339,
  Unknown{chip_ver}) + `RaspberryPiModel` (Pi5/Pi4/Pi400/Pi3BPlus/PiZero2W/
  PiZeroW) — Pi5/Pi4/Pi400/Pi3B+ → Bcm43455c0; PiZero2W → Bcm43436b0.
- `nexmon_adapter_profile(chip)` / `raspberry_pi_profile(model)` build the
  per-device `AdapterProfile` (channels: 2.4 GHz 1-13 + 5 GHz UNII for dual-band;
  bandwidths 20/40/80[/160]; expected subcarrier counts 64/128/256[/512]) that
  `validate_frame` bounds CSI frames against.
- `NexmonChip::from_chip_ver` (0x4345 → Bcm43455c0, 0x4339, 0x4358, 0x4366,
  0x4375 — best-effort; the raw `chip_ver` is always preserved) and `from_slug`
  / `RaspberryPiModel::from_slug` ("pi5", "raspberry pi 4", "bcm43455c0", ...).
- `NexmonCsiHeader::chip()`; `NexmonPcapAdapter` auto-detects the chip from the
  packets' `chip_ver` and uses the matching profile, overridable via
  `.with_chip(NexmonChip)` / `.with_pi_model(RaspberryPiModel)`; `.detected_chip()`.

rvcsi-runtime: `decode_nexmon_pcap_for(.., chip_spec)` (validate against a chip /
Pi model, drop non-conforming) + `nexmon_profile_for(spec)`; `NexmonPcapSummary`
gains `chip_names` + `detected_chip`; `CaptureSummary` gains `chip`.

rvcsi-cli: `record --source nexmon-pcap --chip pi5`; new `nexmon-chips`
subcommand (lists chips + Pi models, human or `--json`); `inspect-nexmon` and
`inspect` now print the resolved chip.

rvcsi-node (napi-rs): `nexmonDecodePcap` gains an optional `chip` arg;
`nexmonChipName(chipVer)`, `nexmonProfile(spec)`, `nexmonChips()`. @ruv/rvcsi
SDK + `.d.ts` updated (AdapterProfile / NexmonChipsListing interfaces, the new
fns, `chip` on CaptureSummary, `chip_names`/`detected_chip` on NexmonPcapSummary).

168 rvcsi tests pass (adapter-nexmon 22→28, cli 9→10), 0 failures, clippy-clean.
The synthetic test captures now stamp chip_ver = 0x4345 (the BCM4345 family chip
ID), so the chip-detection happy path is exercised end to end.
ADR-096, CHANGELOG, README, CLAUDE.md updated.

https://claude.ai/code/session_01CdYAPvRTjcch6YrYf42n1z
2026-05-13 01:32:27 +00:00
Claude b116a99481
feat(rvcsi): real nexmon_csi UDP/PCAP fidelity — chanspec decode, libpcap reader, NexmonPcapAdapter
Raises the Nexmon path from a normalized record format to parsing what the
patched Broadcom firmware actually emits, end to end.

napi-c shim (ABI 1.0 -> 1.1, additive):
- rvcsi_nx_csi_udp_header / rvcsi_nx_csi_udp_decode — parse the real nexmon_csi
  UDP payload: the 18-byte header (magic 0x1111, rssi int8, fctl, src_mac[6],
  seq_cnt, core/spatial-stream, Broadcom chanspec, chip_ver) + nsub complex CSI
  samples (modern int16 LE I/Q export — what CSIKit/csireader.py read for the
  BCM43455c0 / 4358 / 4366c0; nsub = (len-18)/4). rvcsi_nx_csi_udp_write to
  synthesize payloads for tests. rvcsi_nx_decode_chanspec — d11ac chanspec ->
  channel (chanspec & 0xff) / bandwidth (bits [13:11], cross-checked against the
  FFT size) / band (bits [15:14], cross-checked against the channel number).
  Still allocation-free, bounds-checked, structured errors, never panics.
- ffi.rs wraps it: decode_chanspec / parse_nexmon_udp_header / decode_nexmon_udp
  / encode_nexmon_udp + DecodedChanspec / NexmonCsiHeader; every unsafe block
  documented; the ABI guard now expects 1.1.

rvcsi-adapter-nexmon:
- pcap.rs — a dependency-free classic-libpcap reader (all four byte-order /
  timestamp-resolution magics; Ethernet / raw-IPv4 / Linux-SLL link types;
  tolerates a truncated final record; pcapng is a follow-up) + extract_udp_payload
  + a synthetic_udp_pcap / synthetic_nexmon_pcap test/example generator.
- NexmonPcapAdapter (a CsiSource) — reads the CSI UDP packets out of a
  `tcpdump -i wlan0 dst port 5500 -w csi.pcap` capture, decodes each via the C
  shim, stamps the frame timestamp from the pcap packet time; non-CSI packets
  counted as "skipped" in health.

rvcsi-runtime: decode_nexmon_pcap, summarize_nexmon_pcap (+ NexmonPcapSummary:
link type, CSI frame count, channels, bandwidths, subcarrier counts, chip
versions, RSSI range, time span), CaptureRuntime::open_nexmon_pcap[_bytes].

rvcsi-node (napi-rs): nexmonDecodePcap, inspectNexmonPcap, decodeChanspec,
RvcsiRuntime.openNexmonPcap. @ruv/rvcsi SDK + .d.ts updated (NexmonPcapSummary,
DecodedChanspec). rvcsi-cli: `record --source nexmon-pcap`, `inspect-nexmon`,
`decode-chanspec`.

161 rvcsi tests pass (adapter-nexmon 9->22), 0 failures, clippy-clean.
ADR-096 §2.2/§2.3/§5, CHANGELOG, CLAUDE.md updated.

https://claude.ai/code/session_01CdYAPvRTjcch6YrYf42n1z
2026-05-13 01:15:22 +00:00
Claude 684a064816
docs(rvcsi): update CHANGELOG, CLAUDE.md crate table, README docs index
- CHANGELOG: expand the rvCSI entry to cover all 9 crates (incl. rvcsi-runtime
  and the @ruv/rvcsi npm SDK), the napi-c / napi-rs seams, and the 142-test /
  clippy-clean status; note the daemon + MCP server are follow-ups.
- CLAUDE.md: add the 9 `rvcsi-*` crates to the Key Rust Crates table.
- README: add an rvCSI row to the docs index; bump the ADR count (79→96) and
  DDD-model count (7→8).

https://claude.ai/code/session_01CdYAPvRTjcch6YrYf42n1z
2026-05-13 00:18:56 +00:00
rUv 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>
2026-04-27 12:41:01 -04:00
rUv 81cc241b9e
chore(repo): move v1/ → archive/v1/ + add archive/README.md (#430)
The Rust port at v2/ has been the primary codebase since the rename
in #427. The Python implementation at v1/ is no longer the active
target; the only load-bearing path is the deterministic proof bundle
at v1/data/proof/ (per ADR-011 / ADR-028 witness verification).

Move the whole Python tree into archive/v1/ and document the policy
in archive/README.md: no new features, bug fixes only when they affect
a still-load-bearing path (currently just the proof), CI continues to
verify the proof on every push and PR.

Path references updated in 26 files via path-pattern sed (only
matches v1/<known-child> patterns, never bare v1 or API URLs like
/api/v1/). Two double-prefix typos (archive/archive/v1/) caught and
hand-fixed in verify-pipeline.yml and ADR-011.

Validated:
- Python proof verify.py imports cleanly at archive/v1/data/proof/
  (numpy/scipy still required; CI installs requirements-lock.txt
  from archive/v1/ now)
- cargo test --workspace --no-default-features → 1,539 passed,
  0 failed, 8 ignored (unaffected by Python tree relocation)
- ESP32-S3 on COM7 untouched (no firmware paths changed)

After-merge: contributors should re-run any local `python v1/...`
commands as `python archive/v1/...` (CLAUDE.md and CHANGELOG already
updated).
2026-04-25 23:07:52 -04:00
rUv 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.
2026-04-25 21:28:13 -04:00
ruv 92a6986b79 docs: update all docs for v0.5.0-esp32 release
- README: v0.5.0 in release table, binary size 990/773 KB
- CHANGELOG: v0.5.0 entry with mmWave fusion, ADR-063/064
- User guide: v0.5.0 as recommended, binary size updated
- CLAUDE.md: supported hardware table, firmware build/release
  process, real-hardware-first testing policy

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-03-15 16:17:40 -04:00
rUv 86f08303e6
docs: update changelog, user guide, and README for ADR-043 (#128)
- CHANGELOG: add ADR-043 entries (14 new API endpoints, WebSocket fix,
  mobile WS fix, 25 real mobile tests)
- README: update ADR count from 41 to 43
- CLAUDE.md: update ADR count from 32 to 43
- User guide: add 14 new REST endpoints to API reference table, note
  that /ws/sensing is available on the HTTP port, update ADR count
2026-03-03 15:21:52 -05:00
ruv c520204e12 docs: sync CLAUDE.md (uppercase) with claude.md updates
On case-insensitive Windows both files map to the same physical file but
Git tracks them as separate index entries. Force-update CLAUDE.md to match.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-03-01 21:52:36 -05:00
ruv 1f9dc60da4 docs: add Pre-Merge Checklist to CLAUDE.md
Co-Authored-By: claude-flow <ruv@ruv.net>
2026-03-01 11:30:03 -05:00
Claude 6c931b826f
feat(claude-flow): Init claude-flow v3, pretrain on repo, update CLAUDE.md
- Run npx @claude-flow/cli@latest init --force: 115 files created
  (agents, commands, helpers, skills, settings, MCP config)
- Initialize memory.db (147 KB): 84 files analyzed, 30 patterns
  extracted, 46 trajectories evaluated via 4-step RETRIEVE/JUDGE/DISTILL/CONSOLIDATE
- Run pretraining with MoE model: hyperbolic Poincaré embeddings,
  3 contradictions resolved, all-MiniLM-L6-v2 ONNX embedding index
- Include .claude/memory.db and .claude-flow/metrics/learning.json in
  repo for team sharing (semantic search available to all contributors)
- Update CLAUDE.md: add wifi-densepose project context, key crates,
  ruvector integration map, correct build/test commands for this repo,
  ADR cross-reference (ADR-014 through ADR-017)

https://claude.ai/code/session_01BSBAQJ34SLkiJy4A8SoiL4
2026-02-28 16:06:55 +00:00
Claude 6ed69a3d48
feat: Complete Rust port of WiFi-DensePose with modular crates
Major changes:
- Organized Python v1 implementation into v1/ subdirectory
- Created Rust workspace with 9 modular crates:
  - wifi-densepose-core: Core types, traits, errors
  - wifi-densepose-signal: CSI processing, phase sanitization, FFT
  - wifi-densepose-nn: Neural network inference (ONNX/Candle/tch)
  - wifi-densepose-api: Axum-based REST/WebSocket API
  - wifi-densepose-db: SQLx database layer
  - wifi-densepose-config: Configuration management
  - wifi-densepose-hardware: Hardware abstraction
  - wifi-densepose-wasm: WebAssembly bindings
  - wifi-densepose-cli: Command-line interface

Documentation:
- ADR-001: Workspace structure
- ADR-002: Signal processing library selection
- ADR-003: Neural network inference strategy
- DDD domain model with bounded contexts

Testing:
- 69 tests passing across all crates
- Signal processing: 45 tests
- Neural networks: 21 tests
- Core: 3 doc tests

Performance targets:
- 10x faster CSI processing (~0.5ms vs ~5ms)
- 5x lower memory usage (~100MB vs ~500MB)
- WASM support for browser deployment
2026-01-13 03:11:16 +00:00