diff --git a/.gitignore b/.gitignore index 0aae0374..7fcea94e 100644 --- a/.gitignore +++ b/.gitignore @@ -258,3 +258,11 @@ v2/crates/rvcsi-node/*.node v2/crates/rvcsi-node/binding.js v2/crates/rvcsi-node/binding.d.ts v2/crates/rvcsi-node/npm/ + +# ADR-117/118/119/120: deployment-specific training artifacts. +# - recordings/ are large (175 MB each) and room/operator-specific +# - adaptive_model.json is regenerated by `POST /api/v1/adaptive/train` +# - baseline.json is regenerated by `POST /api/v1/baseline/calibrate` +v2/data/recordings/ +v2/data/adaptive_model.json +v2/data/baseline.json diff --git a/CHECKLIST.md b/CHECKLIST.md index e8b555a9..591a4884 100644 --- a/CHECKLIST.md +++ b/CHECKLIST.md @@ -5,15 +5,39 @@ 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 `0ec1e4b0`. -Status: 47 Done / 0 Open in-scope. Deferred items (out of session scope, +Last sweep: **2026-05-18**, branch `feat/ota-rssi-mobile`, head `12e1cf9d`. +Status: 50 Done / 0 Open in-scope. Deferred items (out of session scope, each with explicit reason) listed at the bottom. -This count includes the ADR-100..114 carry-in from the prior agent + this -session's ADR-115 (FW set-target REST), ADR-116 (WiFlow-v1 Rust loader), -ADR-116 cosmetic (UI dropdown), and ADR-117 (process hygiene + audit -follow-ups). ADR-111 is intentionally absent (folded into ADR-109 during -the AP-MAC tracking work). +This count includes the ADR-100..114 carry-in from the prior agent + +this session's: +* **ADR-115** — FW `/ota/set-target` REST endpoint +* **ADR-116** — WiFlow-v1 supervised pose loader in Rust + UI dropdown +* **ADR-117** — process hygiene (ping zombies, loopback filter, audit sweep) +* **ADR-118** — feature decorrelation + multi-node 22-feature extractor +* **ADR-119** — frame-level MLP classifier (22→32→6) replacing LogReg fallback +* **ADR-120** — windowed temporal classifier (W-MLP, 440→64→6) + + hybrid priority (rule-based owns 4 base classes, W-MLP owns + waving/transition) + two-layer label smoothing + + `/api/v1/adaptive/debug` diagnostic endpoint + +ADR-111 is intentionally absent (folded into ADR-109 during the AP-MAC +tracking work). + +Adaptive classifier accuracy trajectory across the session: +``` +2-node 15-feat LogReg 40.4% baseline +6-node 15-feat LogReg 44.4% +4.0 (more sensors) +6-node 22-feat LogReg 49.58% +5.2 (ADR-118 feature engineering) +6-node 22-feat MLP 53.53% +3.95 (ADR-119 non-linear) +6-node 22-feat W-MLP 90.40% +36.87 (ADR-120 temporal context) + ───── +total +50.0 pts vs baseline +``` +W-MLP 90.40% is training-set accuracy; live `transition` class is +over-represented (model overfit to ambiguous training frames). +Held-out test set + cleaner per-class re-records are the recommended +next step. --- @@ -91,6 +115,30 @@ the AP-MAC tracking work). runtime classifier; sensing tab container restored; multi-node test guards external :5005; docs/typo/range sweep. +### Adaptive Classifier (data pipeline + model) + +- [x] **ADR-118** Feature decorrelation + multi-node extractor — + audit on 6-node 151k-frame set found 21 multicollinear pairs + + 1 dead feature (`amp_min` constant 0); refactored to 22 features + (4 global + 6 nodes × 3) with proper z-score normalisation. + Accuracy 44.4% → 49.58% (commit `e86f6506`). +- [x] **ADR-119** Frame-level MLP (22→32→6 ReLU+softmax) replaces + LogReg fallback — manual backprop, no external ML crate, + ~3k weights, trains in seconds. Accuracy 49.58% → 53.53% + (+3.95 pts, concentrated on motion classes — exactly where + non-linear combinations matter; commit `94330708`). +- [x] **ADR-120** Windowed temporal classifier (W-MLP, 440→64→6) — + stacks 20 frames × 22 features for temporal pattern recognition. + Captures walking cadence (~2 Hz), sit-stand cycles (~0.5 Hz), + gesture rhythm (1-2 Hz). Accuracy 53.53% → 90.40% training + (+36.87 pts; held-out generalisation TBD). Hybrid priority: + rule-based owns 4 base classes (ESPectre F1>96%), W-MLP owns + `waving`/`transition` exclusively. Two-layer label smoothing + (15-tick mode + 2-tick confirm) stops UI flicker. + `/api/v1/adaptive/debug` exposes raw model labels for + operator diagnostics (commits `da4c123d`, `442c03da`, `3e12686a`, + `c3f00f3a`, `77d404d6`, `2956414b`, `12e1cf9d`). + ### Tests / fixtures - [x] **ADR-114** `tests/fixtures/replay_idle.jsonl` + @@ -112,7 +160,7 @@ the AP-MAC tracking work). ### Documentation -- [x] **ADR-100..117** all written (ADR-111 intentionally absent), each ≤ 200 lines +- [x] **ADR-100..120** all written (ADR-111 intentionally absent), each ≤ 200 lines - [x] `docs/references/espectre-techniques.md` — Pace technique catalogue - [x] `docs/references/espectre-gap-analysis.md` — section-by-section gap - [x] Documentation actualization sweep — every Open Items section @@ -178,7 +226,7 @@ an explicit reason. Bring them back only if scope changes. | Doc | Purpose | |---|---| -| [`docs/adr/`](docs/adr) | All ADRs 001-117 (111 absent); 100-117 are this session | +| [`docs/adr/`](docs/adr) | All ADRs 001-120 (111 absent); 100-120 are this session | | [`docs/references/espectre-techniques.md`](docs/references/espectre-techniques.md) | Pace technique catalogue + RuView adoption | | [`docs/references/espectre-gap-analysis.md`](docs/references/espectre-gap-analysis.md) | Section-by-section gap with priority table | | [`docs/references/ota-pipeline.md`](docs/references/ota-pipeline.md) | OTA recipe — port 8032, three FW prereqs | diff --git a/ui/style.css b/ui/style.css index f9150a4f..1648c38d 100644 --- a/ui/style.css +++ b/ui/style.css @@ -2033,6 +2033,22 @@ canvas { color: var(--color-error); } +/* ADR-117 follow-up: extended class vocabulary from adaptive_classifier. */ +.sensing-class-label.present_moving { + background: rgba(var(--color-warning-rgb), 0.15); + color: var(--color-warning); +} + +.sensing-class-label.waving { + background: rgba(155, 89, 182, 0.15); /* purple — gestures, body still */ + color: rgb(155, 89, 182); +} + +.sensing-class-label.transition { + background: rgba(230, 126, 34, 0.18); /* orange — discrete event */ + color: rgb(230, 126, 34); +} + .sensing-confidence { display: grid; grid-template-columns: 70px 1fr 40px; diff --git a/v2/data/adaptive_model.json b/v2/data/adaptive_model.json deleted file mode 100644 index 6074b80a..00000000 --- a/v2/data/adaptive_model.json +++ /dev/null @@ -1,267 +0,0 @@ -{ - "class_stats": [ - { - "label": "absent", - "count": 862, - "mean": [ - 66.68196972264862, - 67.23973219951662, - 65.0340640002779, - 205.65861248066514, - 1.2587006960556917, - 8.192575406032482, - 0.0, - 9.823395623712905, - 6.970045450727901, - -0.04488812678641681, - -0.9594767860850162, - 10.78889030301701, - 0.8330000846014487, - 22.47189099978742, - 22.47189099978742 - ], - "stddev": [ - 64.0493846652119, - 90.27545165651007, - 40.157907144682206, - 161.60550836256004, - 1.3807130815029451, - 3.2814660018571113, - 0.0, - 2.219723108446689, - 1.6521309619598676, - 0.342852106459665, - 0.30620004291079783, - 3.529722483499124, - 0.17574148506941875, - 5.519861526721805, - 5.519861526721805 - ] - }, - { - "label": "present_still", - "count": 852, - "mean": [ - 66.39259262094396, - 64.42298266818027, - 68.34546366405283, - 203.34049479166666, - 1.1900821596244182, - 8.200704225352112, - 0.0, - 10.032339700775715, - 7.234479413048846, - 0.027056637948278107, - -0.9161490234231624, - 10.991429347401095, - 0.8298622589530178, - 23.588978503428145, - 23.588978503428145 - ], - "stddev": [ - 59.144593976065984, - 82.61098004853669, - 40.08306971525127, - 152.89405234329087, - 1.2031203046363153, - 3.0571012493320526, - 0.0, - 2.22294769203091, - 1.6508044238677446, - 0.3315329147240876, - 0.29437997092330526, - 3.3214071045026303, - 0.17096813624285292, - 5.622953396738593, - 5.622953396738593 - ] - }, - { - "label": "present_moving", - "count": 808, - "mean": [ - 65.17005228763453, - 66.55424930761484, - 63.785855267654334, - 208.73719832920793, - 1.3400990099009942, - 7.570544554455446, - 0.0, - 10.069915394050431, - 6.923405617584522, - -0.1440461642917184, - -1.0022460352626226, - 10.664608744841848, - 0.8384559212414682, - 21.798331033369895, - 21.798331033369895 - ], - "stddev": [ - 66.1800697503931, - 93.22042148141067, - 42.07226450730718, - 164.93282045618218, - 1.3706144246607475, - 3.1453995481213224, - 0.0, - 2.431170975696439, - 1.672707406405861, - 0.35643090355922863, - 0.30897080072710387, - 3.325911716352165, - 0.1806597020966414, - 5.418714527442832, - 5.418714527442832 - ] - }, - { - "label": "active", - "count": 794, - "mean": [ - 61.85289600233076, - 61.12723986655727, - 62.468831971775344, - 193.2018524349286, - 1.2329974811083138, - 8.083123425692696, - 0.0, - 9.747035051350043, - 7.009904234422278, - 0.007176072447431498, - -0.9950501087764124, - 11.015545839210892, - 0.8278984910895401, - 22.445656559614797, - 22.445656559614797 - ], - "stddev": [ - 50.44687370766278, - 74.07914900524236, - 31.558067649516538, - 121.0762294406304, - 1.2507304998955402, - 3.4503520526220344, - 0.0, - 2.2730029390882156, - 1.6768264387667406, - 0.3214256392367928, - 0.31003127617615406, - 3.1187829194728285, - 0.1772099351197549, - 5.595050695741912, - 5.595050695741912 - ] - } - ], - "weights": [ - [ - 0.9923736589617821, - -0.4600422332552322, - -0.3922101552522972, - -0.1686954616947851, - -0.08471937018349271, - 0.033940973559074515, - 0.0, - -1.116294981490482, - -0.213861080404439, - -0.41727297566573723, - 0.08025552056009382, - 0.20864577739519874, - 0.36814779033318357, - 0.46242679535538855, - 0.46242679535538855, - 0.09475205040199337 - ], - [ - 0.04661470129518883, - 0.7974124099989739, - 0.3953040913806362, - -1.2708868935843511, - 0.10073070355913086, - 0.0735810797517633, - 0.0, - -0.3957608057630568, - 0.22091779039114648, - -0.43105406953304665, - 0.24907697332262252, - -0.17604200203759515, - -0.5059663705836186, - 0.5740861193153091, - 0.5740861193153091, - 0.020569218347928304 - ], - [ - -0.5295363836864718, - 0.14729609046092632, - 0.16131671233151712, - 0.15039859740752318, - 0.08189110214725194, - -0.1429062024394049, - 0.0, - 2.459247211223509, - -0.162133339181718, - 0.6345474095048843, - 0.16626892477248892, - 0.2710091094981082, - -0.08197569509399917, - -1.2007197895193034, - -1.2007197895193034, - -0.10027402587742726 - ], - [ - -0.5094519765704947, - -0.48466626720467487, - -0.1644106484598614, - 1.2891837578716183, - -0.0979024355228887, - 0.0353841491285671, - 0.0, - -0.9471914239699604, - 0.15507662919500606, - 0.2137796356938993, - -0.49560141865520463, - -0.30361288485571664, - 0.21979427534444013, - 0.16420687484859928, - 0.16420687484859928, - -0.015047242872495047 - ] - ], - "global_mean": [ - 65.08291570815048, - 64.88537161757283, - 64.96650236787292, - 202.8304440905207, - 1.25474969843183, - 8.016887816646562, - 0.0, - 9.918865477040464, - 7.036167472733628, - -0.038097952045357715, - -0.9672836370393502, - 10.86491812646321, - 0.8323017200972911, - 22.58850497890069, - 22.58850497890069 - ], - "global_std": [ - 60.376895354908775, - 85.49291935872783, - 38.814475392686795, - 151.54766198012683, - 1.3049002582695195, - 3.2446975526483737, - 1e-9, - 2.2904371592847603, - 1.667114434239705, - 0.34470363318292857, - 0.3067332188136679, - 3.334427501751985, - 0.17614366955910027, - 5.577838072123601, - 5.577838072123601 - ], - "trained_frames": 3316, - "training_accuracy": 0.4149577804583836, - "version": 1 -} \ No newline at end of file