Replaces the text-pill status with a 140×14 px progress bar that
fills from 0 → 99% over CALIB_DURATION_SEC (90s default). On
complete it flashes to 100% with "done" label, then hides itself
after 3s; on error it surfaces a text pill so failure modes stay
visible.
Closes the last Open Item in ADR-107.
Co-Authored-By: claude-flow <ruv@ruv.net>
Three related ADR-104 follow-ups:
1. Expose per-node drift_score on PerNodeFeatureInfo (skip-if-none
so legacy v1 baseline.json — no per_subcarrier_mean — emits
nothing instead of misleading 0.0).
2. raw.html drift sparkline below the RSSI/broadband trace, fixed
Y range [0, 0.30] with dashed presence (0.10) + warning (0.15)
thresholds so operators can read off-axis presence across nodes
without re-scaling. Stat pill "drift" shows the live numeric.
3. baseline_staleness_watch background task: when the on-disk
baseline is older than --baseline-stale-age-sec (default 4 h)
AND drift > 1.5× presence threshold for ≥3 consecutive 5-min
ticks while the classifier reports `absent`, logs a warning
suggesting recalibration. Rate-limited via
--baseline-stale-warn-cooldown-sec (default 1 h). Independent
from auto-recalibrate: that one needs a quiet room; this one
fires when the operator is *in* the room while the channel
itself has physically shifted (AP moved, furniture, etc.).
Co-Authored-By: claude-flow <ruv@ruv.net>
UI side of ADR-107: green "calibrate empty" button in raw.html next
to the existing reset/log-y controls. Click → confirm dialog tells
the operator to step out → POST /api/v1/baseline/calibrate with
90 s capture window → polls GET /api/v1/baseline every 2 s, surfaces
"recording… N/90 s" then "baseline updated ✓".
ADR-107 documents:
D1 in-process capture_baseline_to_disk (port of record-baseline.py)
D2 BASELINE_BUS broadcast forwarder so capture stays decoupled from
WS clients
D3 POST /api/v1/baseline/calibrate (immediate ack, background work)
D4 GET /api/v1/baseline (current state + cooldown + status)
D5 auto_recalibrate_task — 30-min absent+low-CV trigger, 1-h cooldown
D6 raw.html button + polling
* docs/references/espectre-techniques.md — catalogues every Pace
technique from Part-2 against what RuView has implemented, doesn't
have, or has differently. Includes ranked open-items list.
* sensing-server: revert feature_state path to vec![] amplitudes.
The previous fix made bars LOOK live by reissuing the last raw-CSI
vector on every feature_state tick — operator reported this made
the bars misleading (visually busy but unresponsive to movement).
raw.html already skips empty-amp updates so bars now refresh only
on actual fresh CSI, which is honest.
* raw.html: comment on the skip-empty branch for future-me.
* nodes[].rssi_dbm of 0 used to display literally as "0.0 dBm",
misleading the operator when rssi_history was empty on the first
few ticks. Now coerce to "--" and skip pushing zeros to the trace.
* per-node fps was 1/dt instantaneous, blown up to 235 by multiple
SensingUpdate emit paths firing back-to-back. Replaced with a
1-second windowed counter — now matches the real ~38 fps per node.
Surfaces the raw-amplitude classifier's per-node decision in
node_features[].classification so the UI can show which sensor is
actually seeing motion at any moment. Lets the operator visually find
the best sensor placement without physically moving things — just walk
around and watch which badge lights up.
Server side: adds amp_node_level() pure helper + amp_node_snapshot()
that reads AMP_LATEST, then plugs it into build_node_features so the
existing PerNodeFeatureInfo.classification carries the new labels.
UI: adds a global badge in the top bar and a per-node badge inline in
each h2, color-coded (grey/absent, blue/present_still, green/moving,
red/active) plus the live per-node CV %.
Ports Francesco Pace's ESPectre gain-lock (GPLv3) to RuView FW: medians
AGC and FFT scale over the first 300 packets after boot, then freezes
them via phy_force_rx_gain / phy_fft_scale_force. With both sensors
locked and proper AP→body→sensor geometry, a 30-s × 3-state capture
(empty / still / walk) now separates by ×3.4–×5.9 instead of ±0.02
within ±0.10 noise as in ADR-099.
Adds static/raw.html — per-node 56-subcarrier amplitude bars + RSSI/
broadband traces, no DSP, for live calibration.
ADR-100 documents the technique, boot calibration values for the
operator's deployment (AGC=42/44, both APPLIED), and the verified
three-state separation table.