Commit Graph
821 Commits
Author SHA1 Message Date
ruv 2fbf21f426 fix(wiflow-dynamics): correct the mechanism claim, and measure it directly
Two corrections to the previous commit, both to my own reading.

1. RETRACTED: 'EIG is an overconfidence-seeking criterion'. I read my own
   diagnostic backwards. err/var ratio is 1.0 when calibrated, so EIG's
   0.96 is the WELL-calibrated end and random's 0.73 means the variance
   head assigns falsely HIGH variance to the sets random picks -- sets
   that are actually accurate. EIG is not hunting unearned confidence; it
   avoids sets the head is over-cautious about, which happen to be the
   accurate ones.

2. That two-number comparison was suggestive, not a direct test: it uses
   absolute predicted variance on selected sets, while EIG selects on
   entropy reduction from a baseline. So I ran the direct test.

DIRECT MEASUREMENT, superseding both readings: across all 27 single-probe
candidates per sample, correlating predicted entropy reduction with
realised squared-error reduction gives mean per-sample Pearson
r = +0.039, with 54.3% of samples positive (chance 50%).

EIG's predicted gain is essentially UNCORRELATED with realised benefit on
this problem. That is why it performs like random selection. Measured:
the criterion is uninformative here. INFERRED, not measured: that
variance-head miscalibration is why. The clean test is post-hoc
temperature scaling on val followed by re-measuring this correlation.

Also records that rung 4 is single-seed, so items 11-12 are one-seed
measurements -- the lesson rung 3 taught.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk
2026-09-09 00:17:05 -04:00
ruv 8e3aab75be feat(wiflow-dynamics): rung 4 MC-EIG fails, and the measured mechanism explains all four rungs
Rung 4 gives the variance head sight of revealed values and adds an
observation head so EIG can be estimated by Monte Carlo (peek-free:
candidate values are sampled from the model, never read from the
future). Pre-registered pass condition: beat no-probe AND beat random,
both by >2 SE.

Result: DID NOT CLOSE THE GAP, and it went backwards. MC-EIG is worse
than random (t=-4.13 at K=9) and worse than no-probe (t=-9.07), closing
-40% of the oracle gap. The target-peeking bound is unchanged (+2.9 pts,
t=+22.4), so the information is still demonstrably there.

THE MECHANISM, now measured rather than hypothesised. At K=9 MC-EIG
selects probe sets with LOWER predicted variance (2.91e-4 vs random's
3.45e-4) but HIGHER actual squared error (2.78e-4 vs 2.52e-4).
Error/variance ratio, 1.0 if calibrated: 0.96 for EIG vs 0.73 for random.

Expected-information-gain selection under a miscalibrated predictive is
an OVERCONFIDENCE-SEEKING criterion. EIG hunts the sets the model claims
will shrink its entropy most; when the variance head is miscalibrated
those are disproportionately the sets where confidence is unearned.

This explains the whole arc: every rung failed its calibration criterion,
and EIG's failure is downstream of that. Calibration is a PRECONDITION
for information-gain probing, not a nice-to-have. The next step is a
calibrated predictive (low-rank covariance, heavier tails, or post-hoc
recalibration), with EIG re-tested only once coverage passes -- not a
better EIG estimator.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk
2026-09-09 00:08:07 -04:00
ruv e13f61b340 docs(wiflow-dynamics): pre-register rung 4 before writing the model
Rung 4 removes the exact limitation the rung-3 bound diagnosed: the
variance head will SEE revealed values, and a new observation head
supplies p(z_t+1[S] | z_t, revealed) so EIG can be estimated by Monte
Carlo instead of a closed form that cannot represent value-dependence.
Still peek-free -- candidate values are sampled from the model, never
read from the future.

Pass condition fixed in advance: MC-EIG-greedy must beat no-probe by
>2 SE at some budget AND beat random by >2 SE with a consistent sign
across >=3 seeds. Anything less is 'did not close the gap', with the
fraction of the oracle gap closed stated as a measurement.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk
2026-09-08 23:51:59 -04:00
ruv b681313e90 feat(wiflow-dynamics): target-peeking bound at rung 3 resolves the open question
Runs the discriminating experiment: does an oracle that selects probes
using the TRUE y_t+1 beat not probing at all, at rung 3?

Answer: YES, decisively, and against the intuition.
  K=3  oracle 0.9620 vs no-probe 0.9416  (+0.0204, t=+19.5)
  K=9  oracle 0.9705 vs no-probe 0.9416  (+0.0290, t=+22.3)

So the marginal information in z_t+1 bands is NOT absent. Persistence
R2 0.871 suggested it might be; that inference was wrong. Probing can be
worth ~+2.9 pts at K=9. EIG-driven selection cannot find it -- it loses
0.4 pts instead of gaining 2.9, a ~3.3 pt gap to the bound.

Mechanism, and it is a design choice made here: the variance head is
deliberately blind to revealed VALUES (it sees only z_t and the mask),
which is what kept EIG closed-form and peek-free. The bound shows probe
value is value-dependent, not merely mask-dependent -- the oracle
exploits which values actually came back, and a mask-only variance head
structurally cannot represent that.

The fix is the observation head the original design constraint called
for and which was traded away for tractability: sample candidate
z_t+1[S] from p(z_t+1[S] | z_t, revealed) and average the resulting pose
entropies (Monte-Carlo EIG). That is the next experiment.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk
2026-09-08 23:50:56 -04:00
ruv db67caf134 fix(wiflow-dynamics): retract the rung-3 EIG result -- it did not replicate
Seeding Burn's parameter RNG (LinearConfig::init draws from the global
RNG, which the LCG never covered) and rerunning across three seeds
retracts the headline rung-3 claim from the previous commit.

RETRACTED: 'EIG-greedy beats random by 3-5 SE at every budget'. That came
from one unseeded init. Across seeds 2026/7/101 the sign is not stable
(K=3: -0.56 / -1.47 / +1.53; K=9: -1.67 / -2.92 / +0.39). Criterion 3
does not reproduce, so rung 3 fails ALL THREE pre-registered criteria.

ROBUST, and the real finding: probing never helps. Paired against not
probing at all, EIG-greedy scores t = -5.0, -8.5, -10.2 (seed 2026),
-5.0, -8.7, -9.8 (seed 7), -7.2, -12.7, -15.4 (seed 101) at K=3,6,9.
any_budget_where_eig_greedy_beats_no_probe_by_2se is false on every seed,
and it degrades monotonically with budget. Revealing time-averaged
z_t+1 bands adds no information about y_t+1 beyond z_t, in either model
class -- it actively costs accuracy.

Point accuracy is robust: no-probe PCK@20 0.9424/0.9375/0.9452 vs bar
0.7557. The +18.7 pts is entirely the nonlinear mean head.

The NLL caveat is now MEASURED rather than speculated: diagonalising
rung 2's pose posterior (like-for-like against rung 3's diagonal head)
gives -81.92 vs rung 3's -94.9..-102.0. Heteroscedasticity does pay on
marginals; rung 2 won joint NLL via its full 30x30 covariance. Criterion
1 still FAILS as written and is not being rewritten.

Rung-3 runs are now seeded via RUNG3_SEED (default 2026) and
reproducible; per-seed results in results/rung3_seed*.json.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk
2026-09-08 23:38:57 -04:00
ruv 472d356f2b docs(wiflow-dynamics): resume summary + correct M4 line across both rungs
M4's conclusion differs by model class, so the milestone line now states
both: rung 2 is an honest negative (underpowered-proof at n=20,000),
rung 3 shows EIG-greedy beating random by 3-5 SE at every budget. The
rung-2 negative was a property of the model class, not of EIG probing.

Adds a TL;DR so a resuming session gets the state in one screen,
including the two standing prohibitions: no calibrated-uncertainty claim
from rung 3, and no comparison to the 96.09% full-CSI number.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk
2026-09-08 23:12:50 -04:00
ruv 5498dd7ca3 feat(wiflow-dynamics): rung 3 heteroscedastic head -- EIG probing works once Sigma depends on z_t
Verdict by the pre-registered conjunction: RUNG 3 DID NOT HELP. 2 of 3
criteria missed, and that verdict stands. But the criteria conflated two
questions and rung 3 answers them oppositely:

  1. beats rung-2 pose NLL          FAIL  (-102.83 vs -113.29)
  2. coverage within +/-3pp all lvls FAIL  (+7.46pp @50%, +3.20pp @80%)
  3. EIG-greedy beats random >2 SE   PASS  decisively

THE CENTRAL FINDING: the M4 negative at rung 2 was a property of the
model class, not of information-gain probing. Once Sigma depends on z_t
so the probe set can vary per sample, EIG-driven selection beats random
at every budget (t = +3.11, +4.80, +5.04, +3.91 at K = 1, 3, 6, 9).
Adaptivity is real: 19,653 distinct probe sets across 20,000 samples at
K=9, where rung 2 had exactly 1 by construction.

Point accuracy also jumps: PCK@20 0.9425 vs bar 0.7557 (+18.7 pts),
against rung 2's ~0.8208 on the same pairs -- a nonlinear mean head is
worth ~+12 pts. MPJPE 0.01073. Degeneracy guard passes (pred std 0.0208).

Caveats recorded rather than hidden behind:
- The NLL comparison is not like-for-like and that is this design's
  fault: rung 2 carries a full 30x30 pose covariance, rung 3 a diagonal
  one, and joint NLL strongly rewards cross-keypoint correlation. Still
  reported as a FAIL against the criterion as written; the fix is a
  low-rank+diagonal head, not abandoning heteroscedasticity.
- Calibration got WORSE, not better (+7.46pp vs rung 2's +3.48pp at the
  50% level), same direction: over-covered at the centre. Rung 3's
  uncertainty is less trustworthy than rung 2's and no calibrated-
  uncertainty claim may be made from it.
- Rung 3's 0.9425 exceeding rung 2's 0.8568 target-peeking bound is not
  a contradiction: that bound held the linear-Gaussian predictor fixed
  and bounded probe policies within that model class only.

Methodology change recorded: training length went from a fixed 12 epochs
to early stopping on val NLL (patience 40, cap 400) after the first run
was obviously undertrained -- 12 epochs took 16s with the loss still
descending steeply. A convergence fix, not a change to any pre-registered
criterion. Both runs kept; results/rung3_undertrained_12ep.json preserved.

M1/M2 guards re-verified after all representation changes: 12/12 tests
pass, validate still exits PASS on all 6 checks.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk
2026-09-08 23:12:09 -04:00
ruv e5d9aa9409 docs(wiflow-dynamics): pre-register rung 3 before writing the model
Fixes the architecture, the three-part pass condition (beats rung-2 NLL
AND coverage within +/-3pp at all four levels AND EIG-greedy beats random
by >2 SE), and the hard time-box, before any neural code exists.

The variance head deliberately does not see revealed values, so EIG stays
closed-form and peek-free while sigma depends on z_t -- which is what
makes the probe set vary per sample, the one thing rung 2 could not do.

Backend is burn-ndarray on CPU by choice: the model is tiny, and this
removes the Blackwell sm_120 CUDA build risk. Burn 0.21 + ndarray
verified to build in 18.5s.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk
2026-09-08 23:01:27 -04:00
ruv f1e0307f35 test(wiflow-dynamics): M4 power check at 20k pairs + mechanism isolation
The 2k-pair M4 run had SE ~0.0016 against ~0.001 policy gaps, so the
null could have been a power problem. Rerun at 20k pairs (SE ~0.0002)
with paired per-sample tests: the null holds, and at K=6/K=9 EIG-greedy
is slightly but significantly WORSE than random (t -3.15, -2.09;
magnitude <=0.0009 PCK@20).

Mechanism: EIG maximises the logdet of the 30-dim pose posterior --
total joint entropy reduction -- while PCK@20 is a thresholded
per-keypoint criterion. Different utilities, so maximising the first can
slightly hurt the second. The machinery is right; the objective does not
match the metric.

Adaptivity with no information is worth nothing, as the homoscedastic
account predicts: a fresh random probe set per sample matches a fixed
set (no consistent sign across budgets).

Correction: the oracle selects bands using the TRUE y_t+1, i.e. the
target itself -- so it upper-bounds every policy including a perfect
observation-adaptive one, and is not achievable. Renamed
target_peeking_upper_bound.

Also records that M3 forecasting (0.8195) and diag_repr sensing (0.8132)
are NOT like-for-like (different subsets, train sizes, lambda, bars), so
no comparison between them is licensed.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk
2026-09-08 23:00:38 -04:00
ruv 863e5d4348 feat(wiflow-dynamics): M3 action-conditioned dynamics + M4 information-gain eval
M3 (rung 2, closed-form joint Gaussian over [z_t, z_t+1, y_t+1]):
  PCK@20 0.8195 vs mean-pose bar 0.7561 -- beats the bar by +6.33 pts.
  Persistence R2 0.871, so one-step dynamics is not trivial.
  Calibration MARGINALLY FAILS the pre-registered criterion: coverage
  error +3.48pp at the 50% level against a +/-3pp tolerance (80/90/95
  all pass). Reported as a fail; the tolerance is not widened post hoc.

M4: EIG-greedy beats fixed and random on EIG nats at every budget and
  the oracle bracket holds -- but the advantage does NOT convert into
  pose accuracy. Greedy/fixed/random differ by ~0.001 PCK@20, inside the
  random policy's own seed spread. Honest negative.
  The oracle (peeks at true z_t+1) reaches 0.8596 vs 0.8230 at K=9,
  so +3.7 pts of headroom exists for a per-sample adaptive policy.
  Confirms the pre-registered caveat: a homoscedastic Sigma makes the
  EIG-optimal probe set identical at every timestep, so this is
  information-theoretic set selection, not adaptive sensing.

DEVIATION FROM PRE-REGISTRATION, with the measurement that forced it:
  the pre-registered 27-bands-x-20-frames representation (chosen on the
  reasoning that collapsing time would destroy motion signal) was
  measured to be backwards, and was the worst of seven variants -- the
  only one failing to beat the bar (0.7495 vs 0.7523).
  Pose signal lives in fine channel structure; averaging 20 adjacent
  channels destroys it while averaging over 20 frames costs ~nothing.
  Revised to 270 bands of 2 channels averaged over time, with action
  granularity (27 groups of 20 channels) decoupled from feature
  granularity. Full sweep in results/repr_diagnostic.txt.

EIG machinery independently unit-tested: 7/7 including non-negativity,
monotonicity, greedy-vs-brute-force, and analytic-vs-Monte-Carlo.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk
2026-09-08 22:55:07 -04:00
ruv c1e2c60570 docs(wiflow-dynamics): pre-register M3/M4 before fitting any model
Representation, model ladder, metrics, extended guards, and the M4
policy-comparison protocol are all fixed in advance -- including the
bracketing check (oracle upper-bounds, random lower-bounds, EIG must sit
between) and the EIG unit tests.

Records two honesty constraints agreed before seeing any result:
a homoscedastic Sigma makes EIG a FIXED policy, not adaptive sensing;
and pose accuracy from 27 band means is not comparable to the 96%
full-CSI number, so it is reported against the 75.4% mean-pose bar only.

Also dumps the remaining M3 fixtures so no Rust code touches the pickle.
Verified globally that within-file consecutive window indices are
consecutive in time: 359,500 valid (t, t+1) pairs = 360,000 - 500 file
boundaries. Full cleaned GT cross-checks exactly against the validated
M2 test fixture.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk
2026-09-08 22:40:23 -04:00
ruv 9459a94e22 feat(wiflow-dynamics): M1 anti-Goodhart harness + M2 baseline reproduction
M1 - Rust evaluation harness (harness/), guards before capability:
  - metrics.rs: upstream-exact torso-normalized PCK + MPJPE, plus
    absolute_pck retained deliberately as a foil for the broken protocol
  - guards.rs: constant-pose detector, mean-pose honesty bar, corruption
    masks, finite checks, upstream zero-cleaning reimplemented
  - npy.rs: mmap .npy reader (the CSI array is 15.5 GB)
  - 5/5 tests pass, including a reproduction of the retracted 92.9% failure:
    absolute-0.2 scores >0.99 on a constant predictor where torso-normalized
    scores <0.60 and the degeneracy guard trips

M2 - reproduces the honest baseline, all 6 checks pass:
  full PCK@20 0.9608877 vs recorded 0.9608815 (delta 6.1e-6)
  clean PCK@20 0.9661492 vs recorded 0.9661454 (delta 3.8e-6)
  Rust f64 is bit-identical to an independent Python f64 computation.
  GT independently rebuilt from all_keypoints.npy: 0 mismatches after
  cleaning, 8098 before, so the clean path is genuinely exercised.

Measured a reproducibility floor rather than assuming one: TF32 on/off
alone moves PCK@20 by 1.36e-5, and the recorded 2026-06-10 value lies
between the two. No improvement below ~1.4e-5 PCK@20 is real.

Answers the previously-unknown honesty bar: a mean-pose predictor scores
75.37% torso-PCK@20 on the WiFlow test split (oracle 75.46%). The model's
96.09% beats it by 20.7 points -- unlike the ESP32 set, where the bar was
95.9% and nothing beat it.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk
2026-09-08 22:36:01 -04:00
ruv 4704e96620 docs(wiflow-dynamics): charter + verified ground truth state file
Establishes the action-conditioned dynamics objective, the verified
upstream protocol (split/PCK/GT semantics read from source), the
retraction this work must not repeat, and the milestone order
(guards before capability).

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01JJnDvpYYk2rTvjkudQUoCk
2026-09-08 22:23:40 -04:00
ruv d6407ae01b feat(swarm): ADR-149 evaluation harness — GDOP, IQM+bootstrap CI, noise sweep
Stage-1 kinematic evaluator per ADR-149 (peer-reviewed). Pure Rust, no new deps.

evals/:
- gdop.rs: 2D Geometric Dilution of Precision ((HᵀH)⁻¹ trace-sqrt); None for
  <2 observers or collinear/singular geometry
- stats.rs: IQM (Agarwal 2021) + 95% stratified-bootstrap CI (deterministic LCG)
  + probability_of_improvement
- metrics.rs: EpisodeMetrics + AggregateMetrics::from_strata (IQM±CI, seed-stratified)
- runner.rs: seeded kinematic rollout (FlightPattern-driven), seed×episode matrix,
  3σ×3κ default noise sweep (Gaussian amplitude × von Mises phase)
- report.rs + eval_swarm bin: generates evals/RESULTS.md leaderboard

RESULTS.md surfaces the real coverage-vs-localization-precision trade-off via GDOP:
partitioned wins coverage (100%) but single-drone sightings (GDOP 0 → 7.0m);
pheromone gets multistatic fusion (GDOP 1.6 → 4.1m). Wi2SAR 5m paper-baseline row included.

Stage-2 (Gazebo/PX4 SITL false-alarm + collision on median seeds) is documented follow-on.

Tests: 116 default / 133 full+train (+13 eval tests), 0 failed. Clippy clean (-D warnings).

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-30 17:16:26 -04:00
ruv 744cca39e9 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:38 -04:00
ruv bfcbf38c50 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>
2026-05-30 15:49:52 -04:00
ruv 5617ec344b 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>
2026-05-30 14:45:46 -04:00
ruv 17f3a1a235 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>
2026-05-30 14:20:13 -04:00
ruv 5450bfdc60 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>
2026-05-30 12:54:15 -04:00
ruv d60410326f docs(adr-148): mark M4 complete — real GPU autodiff training; overall 98%
Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-30 12:44:25 -04:00
ruv 4f004e018b 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>
2026-05-30 12:43:56 -04:00
ruv f2bd035a22 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>
2026-05-30 02:17:16 -04:00
ruv 0d116d3075 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>
2026-05-30 02:10:58 -04:00
ruv 13b08927ef 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>
2026-05-30 02:01:00 -04:00
ruv 4aee5c9fb1 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>
2026-05-30 01:49:45 -04:00
ruv b392aded6c 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>
2026-05-30 01:33:40 -04:00
ruv 802176c02d 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>
2026-05-30 01:30:14 -04:00
ruv 944e014c59 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>
2026-05-30 01:28:45 -04:00
ruv 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>
v1451
2026-05-29 20:52:51 -04:00
ruv da40503a9e docs(adr-147): add real CSI benchmark — 208ms median, 3.98GB VRAM, 72 frames/sec
Real data: archive/v1 CSI proof dataset (seed=42, 3rx, 56sc, 100Hz, 1000 frames)
Pipeline: CSI amplitude → presence → ENU position → voxels → OccWorld inference
20 inference windows, no mocks.

Co-Authored-By: claude-flow <ruv@ruv.net>
v1450 v1449
2026-05-29 19:56:28 -04:00
ruv bb7de84cb4 docs: add Phase 3+5 scripts to user guide and README world model row
- User guide: full retrain workflow (record → vqvae → transformer → serve)
  with checkpoint path usage
- README: note fine-tune capability in world model capability row

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-29 19:50:21 -04:00
ruv cd1c391afc feat(worldmodel): ADR-147 Phase 3+5 — RuViewOccDataset domain adapter + retraining pipeline
Phase 3 — scripts/ruview_occ_dataset.py:
- RuViewOccDataset: WorldGraph JSON snapshots → OccWorld (F,H,W,D) tensors
- Indoor class remapping: person→7, floor→9, wall→11, furniture→16, free→17
- Zero ego-poses (fixed indoor sensor, no ego-motion)
- record_snapshot() helper for training data accumulation
- Validated: 5 windows, (16,200,200,16) tensor, person+floor voxels confirmed

Phase 5 — scripts/occworld_retrain.py:
- record: stream WorldGraph snapshots from sensing server REST API
- vqvae: fine-tune VQVAE tokenizer on RuView occupancy (200 epochs, AdamW)
- transformer: fine-tune autoregressive transformer with frozen VQVAE

wifi-densepose-worldmodel v0.3.0 published to crates.io

Co-Authored-By: claude-flow <ruv@ruv.net>
v1448 v1447
2026-05-29 18:46:56 -04:00
ruv 28a27bbfd8 fix(worldmodel): use published worldgraph v0.3.0 instead of path dep (crates.io publish prep)
Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-29 18:43:35 -04:00
rUv 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>
v1444
2026-05-29 16:53:51 -04:00
rUv 2cc9f8acb3 Merge pull request #853 from ruvnet/feat/adr-136-146-streaming-engine
RuView Streaming Engine (ADR-135..146): auditable environmental intelligence
v0.3.0-streaming-engine
2026-05-29 09:42:46 -04:00
ruv d24bf36110 release: version bumps for crates.io publish (streaming-engine cascade)
- core 0.3.0->0.3.1 (ComplexSample/CanonicalFrame/provenance + blake3 dep)
- ruvector 0.3.0->0.3.1 (ClockQualityGate)
- bfld 0.3.0->0.3.1 (privacy control plane)
- signal 0.3.1->0.3.2 (fuse_scored_calibrated/ArrayCoordinator/evolution/rf_slam)
- geo: add license/repository for first publish; worldgraph/engine pin geo version
- new: geo 0.1.0, worldgraph 0.3.0, engine 0.3.0

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-29 09:26:38 -04:00
ruv c60a55ca6e docs: RuView streaming-engine v0.3.0 release notes (intro + usage)
Introduction (auditable environmental intelligence / trust throughline), what's
new per ADR-135..146, quick-start usage for StreamingEngine, the 4 validated
acceptance paths, ~6.35us/cycle benchmark, build/test, and honest status.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-29 08:46:12 -04:00
ruv 95bdd37e76 bench+test: engine per-cycle benchmark + ADR-142 acceptance path
- engine: criterion benchmark engine_cycle — full process_cycle (4 nodes / 56
  subcarriers) measured at ~6.35 us/cycle, ~7800x under the 50ms (20Hz) budget.
- signal: ADR-142 acceptance test — 3 links drift 30 frames -> ChangePoint ->
  VoxelMap accumulates -> low-confidence voxels suppressed -> VoxelGate
  Restricted emits histogram only -> ADR-137 contradiction recorded.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-29 08:42:46 -04:00
ruv 020aa08049 test(sensing-server): ADR-140 live acceptance — snapshot to expired-rejection
Drives a real SemanticBus: raw snapshot (fall_detected, past warmup) ->
FallRisk primitive -> SemanticStateRecord (provenance) -> single-signal rule
fires / multi-signal agreement rule does NOT (no false escalation) -> expired
record rejected. Proves the ADR-140 credibility path end to end.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-29 08:37:28 -04:00
ruv 5878868060 feat(signal,engine): ADR-137 calibration-mismatch contradiction + trust witness
- signal: MultistaticFuser::fuse_scored_calibrated() threads per-node
  CalibrationId; agreeing epochs → calibration_id set + CalibrationApplied
  evidence; disagreeing → calibration_id None + CalibrationIdMismatch flag
  (forces demotion). +2 tests.
- engine: process_cycle_calibrated() per-node calibration path; process_cycle
  delegates with a uniform epoch. TrustedOutput gains a deterministic BLAKE3
  witness over (provenance || class). calibration_version='cal:none' on mismatch.
- ADR-137 acceptance test: two frames + mismatched calibration -> QualityScore
  contradiction -> Restricted -> calibration_id None -> witness stable. +happy path.
- 11 engine tests, signal 411+ lib tests; workspace 0 errors.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-29 08:35:40 -04:00
ruv 2517a16d88 feat(engine): compose ADR-138/142/143 + ADR-139 live loop
- ADR-138: process_cycle runs ArrayCoordinator when node geometry is registered;
  array contradictions (CoherenceDrop/GeometryInsufficient) fold into the
  privacy demotion; DirectionalEvidence surfaced in TrustedOutput
- ADR-142: per-node mean-amplitude → EvolutionTracker; cross-link change-point
  recorded as a WorldGraph Event node
- ADR-143: ingest_reflectors() runs Rf-SLAM discovery, writes stable
  Wall/Furniture reflectors as ObjectAnchor nodes
- ADR-139 live loop: update_person_track(), apply_active_privacy_mode()
  (PrivacyRollup suppresses person_track under identity-strict modes),
  snapshot_json()
- Acceptance test live_frame_to_reload_same_contents: full path
  fusion->worldgraph->privacy_rollup->persist->reload->same contents, no raw RF
- 9 engine tests; workspace 0 errors

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-29 08:31:05 -04:00
ruv 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>
2026-05-29 08:21:48 -04:00
ruv f2e9e2f2bd docs(adr): add Implementation Status & Integration to ADR-136..146
Weaves the three framing points into every ADR in the series:
- skeleton/scaffolding (data contracts + trust/privacy/audit machinery +
  algorithms; real, tested, compiling) that existing sensing code plugs into
- Built (tested building block) vs Integration glue (not yet on the live 20 Hz
  path) — per-ADR, with commit + issue references
- trust throughline (traceable evidence, sensor agreement, calibration
  provenance, auditable privacy)
ADR-136 §8 carries the full series framing; 137-146 carry per-ADR status.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-29 08:09:23 -04:00
ruv f18b096f2f feat(nn): ADR-146 RF encoder multi-task heads + uncertainty (#850)
- nn/rf_encoder.rs (forward-looking; extends ADR-024 AETHER):
  - RfEmbedding (256-d pure-Rust f32 ABI), TaskKind (7 heads)
  - LinearHead: W*emb+b + separate log-variance projection → HeadOutput with
    softplus uncertainty + confidence(); MultiTaskHeads.forward_subset() for
    ADR-145 ablation toggling
  - calibration_robustness_loss (ADR-135 invariance), triplet_loss (ADR-024)
  - ContrastiveBatcher: deterministic cross-environment positive / different-
    state negative triplet sampling (ADR-027 MERIDIAN)
- 7 tests; workspace 0 errors

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-28 23:41:25 -04:00
ruv 0f336b7d36 feat(train): ADR-145 ablation eval harness + privacy-leakage/latency metrics (#849)
- train/ablation.rs: FeatureSet matrix (CSI/CIR/CSI+CIR/+Doppler/+BFLD/+UWB);
  AblationMetrics (presence acc, loc err, FP/FN, latency p50/p95, privacy
  leakage, cross-room degradation) derived deterministically from VariantRun
- membership_inference_leakage(): MIA proxy = |AUC-0.5|*2 (0 indistinguishable,
  1 perfectly separable); latency_percentiles_ms (nearest-rank); confusion_rates
- AblationReport.to_markdown() (deterministic), csi_cir_beats_csi_only()
  acceptance check
- 5 tests; workspace 0 errors

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-28 23:38:43 -04:00
ruv b10bc2e9ab feat(mat): ADR-144 UWB range-constraint fusion (#848)
- mat/localization/range_constraint.rs (forward-looking; no UWB hw yet):
  - RangeConstraint domain model (anchor_id/pos/measured_range/uncertainty/
    signal_quality); predicted_range/residual/mahalanobis/is_consistent
  - RangeConstraintFusion::refine() — Newton-normalized weighted least-squares
    that constrains a CSI/CIR prior toward range spheres, Mahalanobis-gates
    inconsistent (NLOS/multipath) ranges; returns RefineResult with rejected
    anchors + RMS residual
  - associate() disambiguates which track a range belongs to (re-ID hook)
- 4 tests (converges to truth, absurd range gated, consistency math, track
  association); workspace 0 errors

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-28 23:35:30 -04:00
ruv 2d4f3dea53 feat(signal): ADR-143 RF-SLAM reflector discovery + anchor learning (#847)
- ruvsense/rf_slam.rs (forward-looking, ships v1 fixed-map first):
  - RfSlam::fixed_map() — discovery disabled (v1); with_discovery() — v2
  - ReflectorObservation (CIR-tap sighting), PersistentReflector (per-axis
    Welford position, migration_m_per_day, classify Wall/Furniture/Mobile)
  - observe(): nearest-reflector association within assoc_radius or seed new;
    coherence-gated; static_anchors() rejects Mobile → ADR-139 ObjectAnchor set
  - persistent_count() for topology-change detection
- 6 tests (fixed-map no-op, persistence, low-coherence reject, cluster split,
  mobile excluded, static→Wall); workspace 0 errors

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-28 23:29:14 -04:00
ruv 1f8e180d69 feat(signal): ADR-142 evolution tracker + temporal VoxelMap (#846)
- ruvsense/evolution.rs (extends ADR-030):
  - TemporalVoxel: Bayesian log-odds occupancy update, evidence_count,
    confidence = 1-exp(-count/5) (5-frame low-confidence floor), Welford
    variance, doppler attribution, last_update_ns
  - TemporalVoxelMap: persistent grid, observe(), low_confidence_indices()
  - EvolutionTracker: per-link Welford baselines + cross-link change-point
    (>=3 links beyond 2sigma in one window); divergence checked vs prior baseline
  - VoxelGate: privacy demotion (Anonymous clears doppler+confidence, keeps
    occupancy; Restricted → occupancy histogram only, raw map cleared)
- reuses field_model::WelfordStats; 6 tests; workspace 0 errors

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-28 23:26:28 -04:00
ruv 7d88eb84c7 feat(bfld): ADR-141 privacy control plane — modes, actions, attestation (#845)
- privacy_mode.rs: PrivacyMode (RawResearch/PrivateHome/EnterpriseAnonymous/
  CareWithConsent/StrictNoIdentity) layered over the existing 4-class
  PrivacyClass; each mode pins target_class + enforced PrivacyAction bitset +
  soul_signature_enabled
- PrivacyAction enum (Allow/SuppressIdentity/ReduceResolution/DropRaw/AggregateOnly)
- PrivacyModeRegistry (std-gated, heap audit log per ESP32 no_std convention):
  active-mode source of truth, is_action_enforced(), set_mode() appends
  hash-chained PrivacyAttestationProof (BLAKE3, ADR-010), verify_chain()
- no_std-safe: PrivacyMode/Action/AttestationProof are heap-free; registry
  std-gated. Builds --no-default-features AND --features std.
- 6 tests incl. tamper-detection; workspace 0 errors

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-28 23:23:01 -04:00
ruv 169a355bde feat(sensing-server): ADR-140 semantic state record + Ruflo agent bridge (#844)
- semantic/record.rs: SemanticStateRecord (kind/room/node/timestamp/expiry/
  confidence/model_version/calibration_version/privacy_action/evidence_refs) —
  the auditable wire form of an ADR-139 SemanticState node, enriched from the
  existing SemanticEvent via RecordContext
- PrivacyAction enum (Allow/AnonymizeByRoom/StripBiometrics); StripBiometrics
  removes HR/BR evidence tags at the record boundary
- Ruflo agent bridge: MultiSignalRule.evaluate() fires AgentRoute only on
  multi-signal agreement (fall_risk + elderly_anomaly → caregiver_escalation);
  route_all() sorts by severity + dedups
- 4 tests; workspace 0 errors

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-28 23:17:53 -04:00