diff --git a/CHECKLIST.md b/CHECKLIST.md index cdeca96b..740d8711 100644 --- a/CHECKLIST.md +++ b/CHECKLIST.md @@ -5,7 +5,7 @@ at the end of every session. Pair with [`docs/references/espectre-gap-analysis.md`](docs/references/espectre-gap-analysis.md) for the technical detail behind each line. -Last sweep: **2026-05-17**, branch `feat/ota-rssi-mobile`, head `e4204595`. +Last sweep: **2026-05-17**, branch `feat/ota-rssi-mobile`, head `eec3ca6c`. --- @@ -22,8 +22,15 @@ Last sweep: **2026-05-17**, branch `feat/ota-rssi-mobile`, head `e4204595`. - [x] **ADR-103** Universal threshold via baseline-CV normalization - [x] **ADR-104** Per-subcarrier drift channel (off-axis presence) - [x] **ADR-104** NBVI Step 3 FP-rate validation (K ∈ {6,8,10,12,16,20}) +- [x] **ADR-104** Per-sub drift exposed in WS `node_features[].drift_score` + + raw.html sparkline per node (commit eec3ca6c) +- [x] **ADR-104** Baseline staleness watch — warn when on-disk baseline + > 4 h old AND drift consistently fires during `absent` periods + (commit eec3ca6c) - [x] **ADR-105** Drop all synthetic data from runtime ([signal_field, pose_keypoints, persons, fake confidence — all gated) +- [x] **ADR-105** `n_aps_used: u8` uniform field on `enhanced_motion` + + `enhanced_breathing` (commit 598a4b2f) - [x] **ADR-106** Full complex CSI in WS (`amplitude` + `phases` + meta) - [x] **ADR-106** Built-in CSI keepalive (managed `ping` per sensor) - [x] **ADR-106** Server-side µs `timestamp_us` @@ -61,18 +68,12 @@ Last sweep: **2026-05-17**, branch `feat/ota-rssi-mobile`, head `e4204595`. ### High value, low effort -- [ ] **Per-sub delta sparkline in `raw.html`** — operator sees off-axis - drift channel firing in real time. ~30 min. (ADR-104 open) - [ ] **`POST /ota/recalibrate`** — clear gain-lock NVS via REST, no USB needed. ~30 min FW + OTA. (ADR-108 open) - [ ] **Track AP MAC in NVS alongside gain-lock** — auto-invalidate stale values on AP swap. ~1 h FW + OTA. (ADR-108 open) -- [ ] **Per-subcarrier baseline AGE check** — flag for re-calibration - when channel slowly drifts. ~1 h. (ADR-104 open) - [ ] **Tailscale-target in NVS** — sensor stream keeps working when Mac roams networks. ~30 min provision + reflash. (ADR-100 open) -- [ ] **`n_aps_used` field in `enhanced_*`** — let consumers know - when multi-AP pipeline ran on a single sensor. ~30 min. (ADR-105 open) ### High value, medium effort diff --git a/docs/adr/ADR-104-per-subcarrier-drift-presence.md b/docs/adr/ADR-104-per-subcarrier-drift-presence.md index f5de2ab8..474cd684 100644 --- a/docs/adr/ADR-104-per-subcarrier-drift-presence.md +++ b/docs/adr/ADR-104-per-subcarrier-drift-presence.md @@ -136,21 +136,31 @@ conditions where a previously-clean subcarrier picks up interference. ## Open Items -* **Per-subcarrier baseline AGE check** — the per-sub vector reflects - the channel at calibration time. As the channel slowly drifts (other - WiFi clients on the AP, temperature, etc.) the per-sub baseline ages - faster than the broadband-mean baseline. Need: if `last_written_sec_ago` - > N hours AND drift consistently > threshold → flag for - re-calibration. Defer to a future ADR-109. -* **Per-subcarrier delta in UI** — `raw.html` only shows broadband - bars + global classification. A small "drift" sparkline per node - would let the operator see the off-axis channel firing. ~30 min. * **Phase-domain drift** — currently amplitude-only. Phase delta vs baseline phase would catch even subtler movement (chest-wall sub-mm motion during breathing). Requires phase baseline in `baseline.json`, which the recording script doesn't yet save. ~1 h script + ~30 min server. +## Closed + +* **Per-subcarrier baseline AGE check** — `baseline_staleness_watch` + background task warns when on-disk baseline is older than + `--baseline-stale-age-sec` (default 4 h) AND per-sub drift exceeds + 1.5× presence threshold for ≥3 consecutive 5-min ticks while the + classifier reports `absent`. Rate-limited via + `--baseline-stale-warn-cooldown-sec` (default 1 h). Independent + from `auto_recalibrate_task`: that path needs a quiet room; this + one fires when the operator is *in* the room while the channel + itself has shifted. (commit eec3ca6c) +* **Per-subcarrier delta in UI** — `raw.html` now shows a per-node + drift sparkline below the RSSI/broadband trace, fixed Y range + [0, 0.30] with dashed presence (0.10) and warning (0.15) + thresholds. Numeric "drift" stat pill in the per-node header. + Backed by a new `drift_score: Option` field on + `PerNodeFeatureInfo` (skip-if-none — distinguishes "no per-sub + baseline loaded" from "loaded and stable at 0.0"). (commit eec3ca6c) + ## References * ADR-101 — broadband classifier; this ADR adds a parallel channel. diff --git a/docs/adr/ADR-105-no-synthetic-data-in-production-runtime.md b/docs/adr/ADR-105-no-synthetic-data-in-production-runtime.md index 131bda30..7df8d76b 100644 --- a/docs/adr/ADR-105-no-synthetic-data-in-production-runtime.md +++ b/docs/adr/ADR-105-no-synthetic-data-in-production-runtime.md @@ -170,9 +170,14 @@ classification absent / present_still / present_moving / active per ADR-1 * **Real signal_field** via multistatic fusion — when ≥ 2 nodes are active, `MultistaticFuser` can produce a physically meaningful spatial map. ADR-104 will cover wiring it through. -* **Honest `enhanced_*` fields** — when the multi-AP pipeline runs - on a single sensor it still emits scores. Should add a - `n_aps_used` field so consumers know whether to trust them. + +## Closed + +* **Honest `enhanced_*` fields** — both `enhanced_motion` and + `enhanced_breathing` now carry a uniform `n_aps_used: u8` field + alongside the legacy `contributing_bssids` / `bssid_count` + counts. Consumers can gate on `n_aps_used >= 2` before trusting a + multi-AP enhancement. (commit 598a4b2f) ## References