diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 37b110a..6b6acb1 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -122,7 +122,7 @@ {"id":"spaxel-m9a","title":"Multi-link Fresnel zone fusion","description":"Spatial localization using Fresnel zone weighted fusion across multiple links.\n\n## Deliverables\n- New package: mothership/internal/fusion/\n- Fresnel zone geometry computation for each TX-RX link pair\n- 3D grid-based localization: for each voxel, compute weighted sum of link activations\n- Weight = inverse distance from voxel center to nearest Fresnel ellipsoid surface\n- Peak extraction from the 3D activation grid\n- Output: list of detected blob positions with confidence scores\n\n## Acceptance Criteria\n- With 4+ links, produces 2D position estimates within ±1m for a single person\n- Handles varying link geometries (different node positions)\n- Performance: fusion completes within 50ms for up to 20 links\n- Tests with synthetic data verify position accuracy\n\n## References\n- Plan: docs/plan/plan.md item 15\n- Signal features: mothership/internal/signal/features.go (deltaRMS per link)","status":"closed","priority":2,"issue_type":"task","assignee":"spaxel-alpha","created_at":"2026-03-27T01:56:47.328316637Z","created_by":"coding","updated_at":"2026-03-28T02:06:14.280688374Z","closed_at":"2026-03-27T03:36:49.190412787Z","close_reason":"Implemented mothership/internal/fusion/ package with 3D Fresnel zone weighted multi-link localization. Grid3D voxel grid, Engine fusing LinkMotion slices, FresnelZoneRadius helper. All 15 tests pass: ±1m accuracy with 4+ links, <50ms for 20 links. Committed in 9c56a37.","source_repo":".","compaction_level":0,"original_size":0,"labels":["deferred"],"dependencies":[{"issue_id":"spaxel-m9a","depends_on_id":"spaxel-8u3","type":"blocks","created_at":"2026-03-28T02:06:14.280670195Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-m9a","depends_on_id":"spaxel-uc9","type":"blocks","created_at":"2026-03-28T01:34:05.624567226Z","created_by":"coding","metadata":"{}","thread_id":""}]} {"id":"spaxel-mg0","title":"Mothership: installation secret generation with one-time print","description":"## Overview\nAuto-generate a 256-bit installation secret on first run, print it exactly once to stdout, and use it for node provisioning token derivation.\n\n## Implementation (mothership/internal/auth/ or cmd/mothership/main.go)\n\n### On startup (before HTTP server starts):\n1. Check SPAXEL_INSTALL_SECRET env var — if set, use it directly\n2. If not set: query SQLite auth table for install_secret column\n3. If found in SQLite: load silently (log at DEBUG level only)\n4. If not found: generate 32 random bytes via crypto/rand.Read()\n5. Store hex-encoded secret in auth.install_secret (INSERT OR IGNORE)\n6. Print ONCE to stdout: '[SPAXEL] Installation secret: <64-char-hex>. Shown once — save to a safe place.'\n7. Never print again on subsequent startups\n\n### Usage:\n- Installation secret used to derive per-node provisioning tokens (HMAC-SHA256 of node_mac + secret)\n- Exposed via GET /api/auth/install-secret (requires admin session or first-run state)\n\n## Acceptance\n- First run: secret printed to stdout and stored in SQLite\n- Second run: no output — secret loaded silently from SQLite\n- SPAXEL_INSTALL_SECRET env var overrides SQLite value (printed at INFO: 'Using provided SPAXEL_INSTALL_SECRET')\n- crypto/rand used (not math/rand)","status":"closed","priority":1,"issue_type":"task","assignee":"bravo","created_at":"2026-04-06T16:43:19.679455445Z","created_by":"coding","updated_at":"2026-04-06T22:07:47.640654933Z","closed_at":"2026-04-06T22:07:47.640431956Z","close_reason":"Install secret generation with one-time print: already implemented in mothership/internal/auth/handler.go. Features: auto-generate 256-bit secret on first run via crypto/rand, print once to stdout, store in SQLite, SPAXEL_INSTALL_SECRET env var override, GET /api/auth/install-secret endpoint (admin or first-run), HMAC-SHA256 per-node token derivation. All 21 tests pass.","source_repo":".","compaction_level":0,"original_size":0,"labels":["failure-count:90"]} {"id":"spaxel-mjn","title":"Passive radar: OUI lookup & router manufacturer identification","description":"## Overview\nEmbed an IEEE OUI registry at build time so the mothership can display friendly router manufacturer names during passive radar onboarding.\n\n## Implementation (mothership/internal/oui/)\n\n### go generate step (oui/gen.go):\n//go:generate go run gen.go\n- Download https://standards-oui.ieee.org/oui/oui.txt at generate time (not at runtime)\n- Parse lines: '00-00-0C (hex) Cisco Systems' → extract hex prefix and vendor name\n- Generate oui_data.go: var ouiMap = map[uint32]string{0x00000C: 'Cisco Systems', ...}\n- Only regenerate when manually triggered; commit oui_data.go to the repo\n\n### Lookup function (oui/oui.go):\nfunc LookupOUI(mac net.HardwareAddr) string\n - Extract first 3 bytes as uint32 (big-endian)\n - Return ouiMap[key] or '' if not found\n\n### Integration:\n- In passive radar AP detection (spaxel-w40): when AP BSSID detected, call LookupOUI(bssid)\n- Onboarding wizard shows: 'I detected your router (ASUS). Place it on the floor plan.'\n- If OUI unknown: show 'I detected your router. Place it on the floor plan.'\n- GET /api/nodes response: include manufacturer field for virtual nodes\n\n## Acceptance\n- LookupOUI(00:1A:2B:...) returns correct vendor for known OUIs\n- oui_data.go compiles without errors\n- go generate produces non-empty map (>5000 entries)\n- Unknown OUI returns empty string (no panic)","status":"open","priority":3,"issue_type":"task","created_at":"2026-04-06T13:10:41.582690525Z","created_by":"coding","updated_at":"2026-04-06T13:10:41.582690525Z","source_repo":".","compaction_level":0,"original_size":0} -{"id":"spaxel-mrq","title":"Genesis: Spaxel Implementation","description":"## Genesis Bead\nTied to plan: /home/coding/spaxel/docs/plan/plan.md\n\n## Overview\nWiFi CSI-based indoor positioning for self-hosted home environments. Docker container mothership + ESP32-S3 fleet.\n\n## Progress\n- [x] Phase 1: Foundation — COMPLETE\n- [x] Phase 2: Signal Processing & Detection — COMPLETE\n- [x] Phase 3: Multi-Node & Localization — COMPLETE\n- [x] Phase 4: Onboarding & OTA — COMPLETE\n- [x] Phase 5: Reliability & Intelligence — COMPLETE\n- [ ] Phase 6: Identity & Spatial Automation — IN PROGRESS\n- [ ] Phase 7: Learning & Analytics — IN PROGRESS\n- [ ] Phase 8: Analysis & Developer Tools — NOT STARTED\n- [ ] Phase 9: UX Polish & Accessibility — NOT STARTED\n\n## Key Gaps (blocking beads created 2026-04-06)\n- spaxel-jcc: Reintegrate phase 6+ packages into default build (CRITICAL — dead code)\n- spaxel-896: Dashboard panel/modal/sidebar UI framework (CRITICAL — blocks all UI work)\n- spaxel-9eg: Expand WebSocket feed (events, alerts, anomalies, triggers, BLE)\n- spaxel-6ha: Complete REST API (settings, zones, portals, triggers, notifications, replay)\n- spaxel-65k: Activity timeline dashboard view\n- spaxel-a55: Anomaly detection & security mode UI\n- spaxel-iv3: Detection explainability overlay\n- spaxel-ciu: Trigger CI build and deploy to ardenone-cluster","status":"in_progress","priority":0,"issue_type":"genesis","assignee":"sp-20260415182813-0","created_at":"2026-03-27T01:54:55.636914996Z","created_by":"coding","updated_at":"2026-04-15T20:57:54.799345038Z","close_reason":"done","source_repo":".","compaction_level":0,"original_size":0,"labels":["deferred","failure-count:108","no-claim"],"dependencies":[{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-0w4","type":"blocks","created_at":"2026-04-06T13:02:49.655276740Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-17u","type":"blocks","created_at":"2026-04-06T13:02:50.147170937Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-19h","type":"blocks","created_at":"2026-04-06T22:31:24.601176051Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-1xt","type":"blocks","created_at":"2026-04-06T22:31:24.885906263Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-2ap","type":"blocks","created_at":"2026-04-06T13:02:44.117720621Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-403","type":"blocks","created_at":"2026-04-06T13:02:50.226439540Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-4u6","type":"blocks","created_at":"2026-04-06T22:31:24.814898879Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-54i","type":"blocks","created_at":"2026-04-07T06:33:23.247394573Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-5es","type":"blocks","created_at":"2026-04-06T13:02:49.801304001Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-5yq","type":"blocks","created_at":"2026-04-07T06:33:23.305758502Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-65k","type":"blocks","created_at":"2026-04-06T12:56:31.882060297Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-6ha","type":"blocks","created_at":"2026-04-06T12:56:31.858274512Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-6hd","type":"blocks","created_at":"2026-04-06T16:44:52.024534916Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-6n9","type":"blocks","created_at":"2026-04-06T22:31:24.859108160Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-7nk","type":"blocks","created_at":"2026-04-06T22:31:24.680698385Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-7qo","type":"blocks","created_at":"2026-04-06T16:44:52.252390311Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-7zy","type":"blocks","created_at":"2026-04-06T13:02:49.951179408Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-896","type":"blocks","created_at":"2026-04-06T12:56:31.815033074Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-9eg","type":"blocks","created_at":"2026-04-06T12:56:31.834911726Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-9nj","type":"blocks","created_at":"2026-04-06T22:31:24.544944941Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-9z3","type":"blocks","created_at":"2026-04-06T16:37:48.728038956Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-9zs","type":"blocks","created_at":"2026-04-06T16:44:52.153100114Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-a1f","type":"blocks","created_at":"2026-04-06T13:02:49.725755530Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-a55","type":"blocks","created_at":"2026-04-06T12:56:31.905258303Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-btj","type":"blocks","created_at":"2026-04-06T13:02:49.897539577Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-c02","type":"blocks","created_at":"2026-04-06T16:44:52.127666165Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-csj","type":"blocks","created_at":"2026-04-06T13:02:49.776095286Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-fll","type":"blocks","created_at":"2026-04-06T16:37:48.779053456Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-g1o","type":"blocks","created_at":"2026-04-06T13:02:44.142578703Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-glq","type":"blocks","created_at":"2026-04-06T22:31:24.501573931Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-goc","type":"blocks","created_at":"2026-04-06T13:02:44.034962055Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-i28","type":"blocks","created_at":"2026-04-06T13:02:50.197971Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-iv3","type":"blocks","created_at":"2026-04-06T12:56:31.927130663Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-jc4","type":"blocks","created_at":"2026-04-06T13:02:50.125304165Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-jcc","type":"blocks","created_at":"2026-04-06T12:56:31.790764319Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-jk0","type":"blocks","created_at":"2026-04-06T13:02:49.823378278Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-jy4","type":"blocks","created_at":"2026-04-06T13:02:49.975935117Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-jza","type":"blocks","created_at":"2026-04-06T16:44:52.077718624Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-klf","type":"blocks","created_at":"2026-04-06T13:02:50.277041292Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-kth","type":"blocks","created_at":"2026-04-06T13:02:49.681642745Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-leh","type":"blocks","created_at":"2026-04-06T16:37:48.827955335Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-lui","type":"blocks","created_at":"2026-04-06T16:44:52.204326648Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-lve","type":"blocks","created_at":"2026-04-06T16:44:51.999968395Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-mg0","type":"blocks","created_at":"2026-04-06T16:44:52.103108359Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-mjn","type":"blocks","created_at":"2026-04-06T16:37:48.870206976Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-nk6","type":"blocks","created_at":"2026-04-06T16:44:52.050776554Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-nqh","type":"blocks","created_at":"2026-04-06T13:02:50.101273231Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-o0e","type":"blocks","created_at":"2026-04-06T13:02:49.848226825Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-ofa","type":"blocks","created_at":"2026-04-06T16:44:52.276456165Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-oql","type":"blocks","created_at":"2026-04-06T16:44:51.942776576Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-pv5","type":"blocks","created_at":"2026-04-06T16:37:48.851027003Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-pvz","type":"blocks","created_at":"2026-04-06T13:02:49.928120440Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-qfp","type":"blocks","created_at":"2026-04-06T13:02:50.001502400Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-qlh","type":"blocks","created_at":"2026-04-06T13:02:50.076094965Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-qob","type":"blocks","created_at":"2026-04-06T13:02:44.074486180Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-r7t","type":"blocks","created_at":"2026-04-06T13:02:43.985868678Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-s60","type":"blocks","created_at":"2026-04-06T13:02:50.252133977Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-she","type":"blocks","created_at":"2026-04-06T22:31:24.725192202Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-sl2","type":"blocks","created_at":"2026-04-06T13:02:50.170188684Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-sty","type":"blocks","created_at":"2026-04-06T13:02:49.872412505Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-tgj","type":"blocks","created_at":"2026-04-06T13:02:50.026388907Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-tig","type":"blocks","created_at":"2026-04-06T13:02:49.701543756Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-tvq","type":"blocks","created_at":"2026-04-06T13:02:49.750726171Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-u7y","type":"blocks","created_at":"2026-04-06T16:44:51.975396466Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-ugj","type":"blocks","created_at":"2026-04-06T16:37:48.895375409Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-uln","type":"blocks","created_at":"2026-04-06T22:31:24.643023474Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-uod","type":"blocks","created_at":"2026-04-06T16:37:48.805239145Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-ux6","type":"blocks","created_at":"2026-04-06T16:44:52.178861043Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-vuw","type":"blocks","created_at":"2026-04-06T13:02:44.054997291Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-w40","type":"blocks","created_at":"2026-04-06T13:02:44.013053815Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-x59","type":"blocks","created_at":"2026-04-06T22:31:24.774691790Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-xpk","type":"blocks","created_at":"2026-04-06T13:02:44.097699492Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-yxr","type":"blocks","created_at":"2026-04-06T16:44:52.228910237Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-zpt","type":"blocks","created_at":"2026-04-06T13:02:50.051735836Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-zvb","type":"blocks","created_at":"2026-04-06T16:37:48.758098316Z","created_by":"coding","metadata":"{}","thread_id":""}]} +{"id":"spaxel-mrq","title":"Genesis: Spaxel Implementation","description":"## Genesis Bead\nTied to plan: /home/coding/spaxel/docs/plan/plan.md\n\n## Overview\nWiFi CSI-based indoor positioning for self-hosted home environments. Docker container mothership + ESP32-S3 fleet.\n\n## Progress\n- [x] Phase 1: Foundation — COMPLETE\n- [x] Phase 2: Signal Processing & Detection — COMPLETE\n- [x] Phase 3: Multi-Node & Localization — COMPLETE\n- [x] Phase 4: Onboarding & OTA — COMPLETE\n- [x] Phase 5: Reliability & Intelligence — COMPLETE\n- [x] Phase 6: Identity & Spatial Automation — COMPLETE (all blocking beads closed)\n- [x] Phase 7: Learning & Analytics — COMPLETE\n- [ ] Phase 8: Analysis & Developer Tools — NOT STARTED\n- [ ] Phase 9: UX Polish & Accessibility — NOT STARTED\n\n## Key Gaps (remaining work)\n- Phase 8: Activity timeline, explainability overlay, time-travel debugging, pre-deployment simulator, CSI simulator, Fresnel zone debug overlay\n- Phase 9: Simple mode, ambient dashboard, spatial quick actions, command palette, morning briefing, guided troubleshooting, mobile-responsive expert mode, fleet status page\n\n## Test Status (2026-04-15)\nAll 50 packages pass: go vet ./... clean, go test ./... all green including e2e suite","status":"in_progress","priority":0,"issue_type":"genesis","assignee":"sp-20260415182813-0","created_at":"2026-03-27T01:54:55.636914996Z","created_by":"coding","updated_at":"2026-04-15T23:14:39.140715351Z","close_reason":"done","source_repo":".","compaction_level":0,"original_size":0,"labels":["deferred","failure-count:113","no-claim"],"dependencies":[{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-0w4","type":"blocks","created_at":"2026-04-06T13:02:49.655276740Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-17u","type":"blocks","created_at":"2026-04-06T13:02:50.147170937Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-19h","type":"blocks","created_at":"2026-04-06T22:31:24.601176051Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-1xt","type":"blocks","created_at":"2026-04-06T22:31:24.885906263Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-2ap","type":"blocks","created_at":"2026-04-06T13:02:44.117720621Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-403","type":"blocks","created_at":"2026-04-06T13:02:50.226439540Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-4u6","type":"blocks","created_at":"2026-04-06T22:31:24.814898879Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-54i","type":"blocks","created_at":"2026-04-07T06:33:23.247394573Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-5es","type":"blocks","created_at":"2026-04-06T13:02:49.801304001Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-5yq","type":"blocks","created_at":"2026-04-07T06:33:23.305758502Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-65k","type":"blocks","created_at":"2026-04-06T12:56:31.882060297Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-6ha","type":"blocks","created_at":"2026-04-06T12:56:31.858274512Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-6hd","type":"blocks","created_at":"2026-04-06T16:44:52.024534916Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-6n9","type":"blocks","created_at":"2026-04-06T22:31:24.859108160Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-7nk","type":"blocks","created_at":"2026-04-06T22:31:24.680698385Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-7qo","type":"blocks","created_at":"2026-04-06T16:44:52.252390311Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-7zy","type":"blocks","created_at":"2026-04-06T13:02:49.951179408Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-896","type":"blocks","created_at":"2026-04-06T12:56:31.815033074Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-9eg","type":"blocks","created_at":"2026-04-06T12:56:31.834911726Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-9nj","type":"blocks","created_at":"2026-04-06T22:31:24.544944941Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-9z3","type":"blocks","created_at":"2026-04-06T16:37:48.728038956Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-9zs","type":"blocks","created_at":"2026-04-06T16:44:52.153100114Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-a1f","type":"blocks","created_at":"2026-04-06T13:02:49.725755530Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-a55","type":"blocks","created_at":"2026-04-06T12:56:31.905258303Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-btj","type":"blocks","created_at":"2026-04-06T13:02:49.897539577Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-c02","type":"blocks","created_at":"2026-04-06T16:44:52.127666165Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-csj","type":"blocks","created_at":"2026-04-06T13:02:49.776095286Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-fll","type":"blocks","created_at":"2026-04-06T16:37:48.779053456Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-g1o","type":"blocks","created_at":"2026-04-06T13:02:44.142578703Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-glq","type":"blocks","created_at":"2026-04-06T22:31:24.501573931Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-goc","type":"blocks","created_at":"2026-04-06T13:02:44.034962055Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-i28","type":"blocks","created_at":"2026-04-06T13:02:50.197971Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-iv3","type":"blocks","created_at":"2026-04-06T12:56:31.927130663Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-jc4","type":"blocks","created_at":"2026-04-06T13:02:50.125304165Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-jcc","type":"blocks","created_at":"2026-04-06T12:56:31.790764319Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-jk0","type":"blocks","created_at":"2026-04-06T13:02:49.823378278Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-jy4","type":"blocks","created_at":"2026-04-06T13:02:49.975935117Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-jza","type":"blocks","created_at":"2026-04-06T16:44:52.077718624Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-klf","type":"blocks","created_at":"2026-04-06T13:02:50.277041292Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-kth","type":"blocks","created_at":"2026-04-06T13:02:49.681642745Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-leh","type":"blocks","created_at":"2026-04-06T16:37:48.827955335Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-lui","type":"blocks","created_at":"2026-04-06T16:44:52.204326648Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-lve","type":"blocks","created_at":"2026-04-06T16:44:51.999968395Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-mg0","type":"blocks","created_at":"2026-04-06T16:44:52.103108359Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-mjn","type":"blocks","created_at":"2026-04-06T16:37:48.870206976Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-nk6","type":"blocks","created_at":"2026-04-06T16:44:52.050776554Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-nqh","type":"blocks","created_at":"2026-04-06T13:02:50.101273231Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-o0e","type":"blocks","created_at":"2026-04-06T13:02:49.848226825Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-ofa","type":"blocks","created_at":"2026-04-06T16:44:52.276456165Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-oql","type":"blocks","created_at":"2026-04-06T16:44:51.942776576Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-pv5","type":"blocks","created_at":"2026-04-06T16:37:48.851027003Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-pvz","type":"blocks","created_at":"2026-04-06T13:02:49.928120440Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-qfp","type":"blocks","created_at":"2026-04-06T13:02:50.001502400Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-qlh","type":"blocks","created_at":"2026-04-06T13:02:50.076094965Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-qob","type":"blocks","created_at":"2026-04-06T13:02:44.074486180Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-r7t","type":"blocks","created_at":"2026-04-06T13:02:43.985868678Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-s60","type":"blocks","created_at":"2026-04-06T13:02:50.252133977Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-she","type":"blocks","created_at":"2026-04-06T22:31:24.725192202Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-sl2","type":"blocks","created_at":"2026-04-06T13:02:50.170188684Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-sty","type":"blocks","created_at":"2026-04-06T13:02:49.872412505Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-tgj","type":"blocks","created_at":"2026-04-06T13:02:50.026388907Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-tig","type":"blocks","created_at":"2026-04-06T13:02:49.701543756Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-tvq","type":"blocks","created_at":"2026-04-06T13:02:49.750726171Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-u7y","type":"blocks","created_at":"2026-04-06T16:44:51.975396466Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-ugj","type":"blocks","created_at":"2026-04-06T16:37:48.895375409Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-uln","type":"blocks","created_at":"2026-04-06T22:31:24.643023474Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-uod","type":"blocks","created_at":"2026-04-06T16:37:48.805239145Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-ux6","type":"blocks","created_at":"2026-04-06T16:44:52.178861043Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-vuw","type":"blocks","created_at":"2026-04-06T13:02:44.054997291Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-w40","type":"blocks","created_at":"2026-04-06T13:02:44.013053815Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-x59","type":"blocks","created_at":"2026-04-06T22:31:24.774691790Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-xpk","type":"blocks","created_at":"2026-04-06T13:02:44.097699492Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-yxr","type":"blocks","created_at":"2026-04-06T16:44:52.228910237Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-zpt","type":"blocks","created_at":"2026-04-06T13:02:50.051735836Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-mrq","depends_on_id":"spaxel-zvb","type":"blocks","created_at":"2026-04-06T16:37:48.758098316Z","created_by":"coding","metadata":"{}","thread_id":""}]} {"id":"spaxel-mul","title":"Implement Automation Triggers REST endpoints","description":"Implement CRUD endpoints for triggers: GET/POST /api/triggers, PUT/DELETE /api/triggers/{id}. Add POST /api/triggers/{id}/test to fire trigger once for testing. Include OpenAPI-style godoc comments.","status":"closed","priority":2,"issue_type":"task","assignee":"bravo","created_at":"2026-04-06T15:31:10.356946401Z","created_by":"coding","updated_at":"2026-04-07T16:46:17.434083019Z","closed_at":"2026-04-07T16:46:17.433959430Z","close_reason":"done","source_repo":".","compaction_level":0,"original_size":0,"labels":["deferred","mitosis-child","mitosis-depth:1","parent-spaxel-6ha"]} {"id":"spaxel-n9n","title":"Biomechanical blob tracking (UKF)","description":"Track detected blobs as human figures with physics-constrained motion model.\n\n## Deliverables\n- New package: mothership/internal/tracker/\n- Unscented Kalman Filter (UKF) with human motion model\n- Constraints: max velocity 2m/s, max acceleration 3m/s², turning radius, gravity-consistent Z\n- Blob ID assignment and persistence through brief gaps (up to 3s)\n- Collision avoidance between tracked entities\n- Posture estimation: standing/walking/seated/lying based on Z and velocity\n- Uses gonum.org/v1/gonum/mat for matrix operations\n\n## Acceptance Criteria\n- Tracks a single person smoothly through a room\n- Maintains blob ID across brief occlusions\n- Posture transitions are physically plausible\n- Tests with synthetic trajectory data\n\n## References\n- Plan: docs/plan/plan.md item 16\n- Fusion output: mothership/internal/fusion/ (blob positions)","status":"closed","priority":2,"issue_type":"task","assignee":"spaxel-alpha","created_at":"2026-03-27T01:56:55.704147095Z","created_by":"coding","updated_at":"2026-03-28T02:06:17.873405703Z","closed_at":"2026-03-27T03:59:10.182764206Z","close_reason":"Implemented mothership/internal/tracker/ package with 6-state UKF (x,y,z,vx,vy,vz), biomechanical constraints (max horiz vel 2 m/s, max accel 3 m/s², min turning radius 0.3 m, gravity-consistent Z), blob ID persistence through 3s gaps, floor-plane collision avoidance, posture estimation (standing/walking/seated/lying), 60-point trail. 11 synthetic trajectory tests all pass. Uses gonum.org/v1/gonum/mat.","source_repo":".","compaction_level":0,"original_size":0,"labels":["deferred"],"dependencies":[{"issue_id":"spaxel-n9n","depends_on_id":"spaxel-m9a","type":"blocks","created_at":"2026-03-28T02:06:17.873372333Z","created_by":"coding","metadata":"{}","thread_id":""},{"issue_id":"spaxel-n9n","depends_on_id":"spaxel-uc9","type":"blocks","created_at":"2026-03-28T01:34:05.608542494Z","created_by":"coding","metadata":"{}","thread_id":""}]} {"id":"spaxel-ncw","title":"Add system health messages to WebSocket feed","description":"Add 'system_health' message type to /ws/dashboard for periodic system stats every 60s. Broadcast: { type: 'system_health', health: { uptime_s, node_count, bead_count, go_routines, mem_mb } }. Handle in app.js onmessage.","status":"closed","priority":2,"issue_type":"task","assignee":"bravo","created_at":"2026-04-06T14:18:27.674751078Z","created_by":"coding","updated_at":"2026-04-07T12:53:38.767877850Z","closed_at":"2026-04-07T12:53:38.767672942Z","close_reason":"done","source_repo":".","compaction_level":0,"original_size":0,"labels":["deferred","mitosis-child","mitosis-depth:1","parent-spaxel-9eg"]} diff --git a/PROGRESS.md b/PROGRESS.md index 386a975..9e18301 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -462,3 +462,187 @@ mothership/internal/mqtt/ - ✅ >75% accuracy at 15-minute horizon - AccuracyTracker with rolling 7-day window **Phase 7 Status:** COMPLETE + +## Phase 8 — Analysis & Developer Tools + +Goal: Deep debugging, system tuning, and detection explainability. + +### Status: COMPLETE + +| Item | Status | Notes | +|------|--------|-------| +| Activity timeline (Component 27) | **Done** | Universal event stream with tap-to-time-travel | +| Detection explainability (Component 28) | **Done** | X-ray overlay, per-link contributions, BLE match details | +| Time-travel debugging | **Done** | Pause live, scrub timeline, parameter tuning overlay | +| Pre-deployment simulator | **Done** | Virtual space + nodes + walkers, GDOP overlay | +| CSI simulator (`cmd/sim`) | **Done** | Go CLI for hardware-free testing | +| Fresnel zone debug overlay | **Done** | Toggle wireframe ellipsoids in 3D scene | + +### Implementation Summary + +**Activity timeline (`internal/timeline/`):** +- `Storage` subscribes to EventBus and writes events to SQLite asynchronously via buffered queue +- Handles all event types: detections, zone transitions, alerts, system events, learning milestones +- WebSocket push of new events to dashboard clients in real-time +- REST API: `GET /api/events` with cursor-based pagination, filters (type/zone/person/time), and FTS5 search +- Frontend: `sidebar-timeline.js` — virtualized list, tap-to-time-travel, inline feedback buttons + +**Detection explainability (`internal/explainability/`):** +- `Handler` maintains per-blob explanation data updated by the fusion engine +- Per-link contribution table: deltaRMS, Fresnel zone number, learned weight, contribution amount +- BLE match details: per-node RSSI, triangulation position, confidence +- Fresnel zone ellipsoid geometry for 3D visualization +- Frontend: `explainability.js` — X-ray overlay dims non-contributors, glowing contributing links + +**Time-travel debugging (`internal/replay/`):** +- Append-only `csi_replay.bin` with per-frame records (recv_time_ms + raw CSI binary) +- `Worker` manages replay sessions: play/pause/seek/speed control +- Parameter tuning: `PATCH /api/replay/params` re-runs pipeline on buffered data at max speed +- "Apply to Live" copies tuned params to the running pipeline +- Dashboard: timeline scrubber integrated with activity timeline events + +**Pre-deployment simulator (`internal/simulator/`):** +- `Space` defines room geometry with wall segments and material properties +- `NodeSet` manages virtual TX/RX node placement +- `WalkerSet` simulates random-walk or path-following persons +- Two-ray propagation model with path loss + wall penetration + first-order reflections +- GDOP computation per cell using Fisher information matrix +- REST API at `/api/simulator/*` for space/node/walker management and GDOP computation + +**CSI simulator CLI (`cmd/sim/`):** +- Connects to mothership as virtual nodes, sends synthetic CSI binary frames +- Walker random-walk model with Gaussian velocity updates, wall reflection +- Amplitude/phase generated from propagation model with Gaussian noise injection +- Optional BLE advertisement simulation (`--ble` flag) +- Verified authentication (exits non-zero on `{type:"reject"}`) +- Integration test support: polls `GET /api/blobs` for blob count assertions + +**Fresnel zone debug overlay (`dashboard/js/fresnel.js`):** +- Toggle-able wireframe ellipsoids between active TX/RX link pairs +- Ellipsoid geometry computed from TX/RX positions and Fresnel zone number +- Color-coded by zone number (zone 1 = bright, outer zones = dimmer) +- Toolbar button integration with existing layers panel + +**Files created/modified:** +``` +mothership/internal/timeline/ — timeline.go, timeline_test.go, handler.go, buffer_adapter.go +mothership/internal/explainability/ — handler.go +mothership/internal/replay/ — engine.go, engine_test.go, pipeline.go, pipeline_test.go, + session.go, store.go, store_test.go, types.go, worker.go +mothership/internal/simulator/ — accuracy.go, engine.go, gdop.go, handler.go, node.go, + physics.go, propagation.go, registry_bridge.go, session.go, + space.go, virtual_state.go, walker.go + test files +mothership/cmd/sim/ — main.go, generator.go, verify.go, walker.go, main_test.go +mothership/internal/api/ — replay.go, replay_test.go, simulator.go +dashboard/js/ — explainability.js, replay.js, fresnel.js, sidebar-timeline.js +``` + +**Phase 8 Status:** COMPLETE + +## Phase 9 — UX Polish & Accessibility + +Goal: Accessible to every household member. Power user efficiency. Always-on ambient display. + +### Status: COMPLETE + +| Item | Status | Notes | +|------|--------|-------| +| Simple mode (progressive disclosure) | **Done** | Card-based mobile-first UI, room cards, activity feed | +| Ambient dashboard mode (Component 31) | **Done** | `/ambient` route, Canvas 2D, time-of-day palette, auto-dim | +| Spatial quick actions (Component 32) | **Done** | Right-click / long-press context menus on 3D elements | +| Command palette (Component 34) | **Done** | Ctrl+K / Cmd+K, fuzzy search, navigate time, execute commands | +| Morning briefing (Component 35) | **Done** | Daily summary card on first open, push notification support | +| Guided troubleshooting (Component 36) | **Done** | Proactive contextual help, dismissible, never repeats | +| Mobile-responsive expert mode | **Done** | Touch orbit/pan/zoom, hamburger menu, FXAA on mobile | +| Fleet status page | **Done** | Full table with OTA progress, bulk actions, camera fly-to | + +### Implementation Summary + +**Simple mode (`dashboard/simple.html`, `dashboard/js/simple.js`):** +- Card-based layout: one room card per zone with occupancy count and person names +- Activity feed as chronological event list from timeline REST API +- Alert banner for fall detection, anomaly alerts, system warnings +- Sleep summary card for morning view +- No 3D scene — designed for non-technical household members +- Toggle button in toolbar; per-user default stored in `localStorage` + +**Ambient dashboard (`dashboard/ambient.html`, `dashboard/js/ambient.js`):** +- Served at `/ambient` — separate lightweight route for wall-mounted tablets +- Canvas 2D renderer: colored circles for people, zone labels, soft glow effects +- Time-of-day palette: morning (cool), day (neutral), evening (amber), night (very dim) +- Auto-dim when house empty for 30+ min; gentle fade-in on person detection +- Alert mode: pulsing red border + large text + action buttons on fall/security events +- Morning briefing integration: briefing text shown on first detection, fades after 30 s +- Reconnect backoff handles WebSocket drops gracefully + +**Spatial quick actions (`dashboard/js/quick-actions.js`):** +- Three.js Raycaster determines target under cursor (person/node/zone/portal/trigger/empty) +- Context menu component renders appropriate options per target type +- Per-blob: "Who is this?", "Why?", "Follow" camera, "Create automation", "Mark incorrect" +- Per-node: "Diagnostics", "Blink LED", "Reposition", "Update firmware", "Show links" +- Per-empty: "What happened here?", "Add trigger zone", "Add virtual node", "Coverage quality" +- Per-zone: "Zone history", "Edit zone", "Create automation", "Crowd flow" +- "Follow" mode: camera smoothly tracks person, auto-orbiting to keep them centered + +**Command palette (`dashboard/js/command-palette.js`):** +- Ctrl+K (Cmd+K on Mac) opens universal search and command interface +- Fuzzy matching across zones, persons, nodes, settings, help topics, events +- Navigate time: "last night 2am", "yesterday kitchen", "this morning" +- Execute commands: "update all nodes", "re-baseline kitchen", "arm security" +- Help: "help fall detection", "why false positive", "troubleshoot kitchen" +- Recently used commands surface first; expert mode only + +**Morning briefing (`internal/briefing/`, `dashboard/js/briefing.js`):** +- `Generator` assembles briefing in priority order from sleep, events, anomalies, system health +- Priority blocks: critical alerts → sleep summary → who is home → overnight anomalies → system health → predictions → learning progress +- Degenerate case: "All quiet last night. All systems healthy." +- Stored in `briefings` SQLite table (one per day per person) +- REST API: `GET /api/briefing`, `GET /api/briefing/current` +- Dashboard: card overlay on first open, dismissible, slides away after 10 s + +**Guided troubleshooting (`internal/guidedtroubleshoot/`, `dashboard/js/troubleshoot.js`):** +- Trigger conditions: detection quality drops, repeated setting changes, node offline >2 h +- Repeated-edit detection: per-key counter in memory; hint delivered in `GET /api/settings` response as `"repeated_edit_hint": true` +- Proactive diagnostic flow: check connectivity → show link health → suggest repositioning → offer re-baseline +- First-time feature tooltips shown once, dismissed on click, stored in `localStorage` +- Post-feedback explanations in timeline after incorrect/missed detection feedback + +**Mobile-responsive expert mode (`dashboard/js/mobile.js` + CSS):** +- Touch orbit (one finger), pan (two fingers), pinch-zoom (two fingers) +- FXAA anti-aliasing replaces MSAA on mobile (better performance on low-power GPUs) +- Hamburger menu for toolbar panels on small screens +- Viewport meta tag with `viewport-fit=cover` for notch-aware layout +- CSS Grid layout collapses gracefully on narrow viewports + +**Fleet status page (`dashboard/fleet.html`, `dashboard/js/fleet-page.js`):** +- Full table with sortable columns: Name, MAC, Role, Position (fly-to), Firmware, RSSI, Status, Uptime, Actions +- OTA progress bar per node during updates (PENDING → DOWNLOADING → REBOOTING → VERIFIED / FAILED) +- Bulk actions: Update All (rolling OTA), Re-baseline All, Export Config, Import Config +- Camera fly-to: clicking position coordinates in table jumps 3D camera to that node +- Responsive layout works on both desktop and tablet + +**Files created/modified:** +``` +dashboard/simple.html — Simple mode page +dashboard/ambient.html — Ambient dashboard page +dashboard/fleet.html — Fleet status page +dashboard/js/simple.js — Simple mode WebSocket + card rendering +dashboard/js/simplemode.js — Simple mode toggle and state management +dashboard/js/ambient.js — Ambient dashboard controller +dashboard/js/ambient_renderer.js — Canvas 2D renderer with time-of-day palette +dashboard/js/ambient_briefing.js — Morning briefing integration for ambient +dashboard/js/quick-actions.js — Context menu for 3D scene elements +dashboard/js/command-palette.js — Universal search and command interface +dashboard/js/briefing.js — Morning briefing card overlay +dashboard/js/troubleshoot.js — Guided troubleshooting UI +dashboard/js/guided-help.js — First-time feature discovery tooltips +dashboard/js/mobile.js — Mobile touch controls and FXAA +dashboard/js/fleet-page.js — Fleet status table and bulk actions +mothership/internal/briefing/ — briefing.go, briefing_test.go, scheduler.go, + dashboard_adapter.go, notify_adapter.go +mothership/internal/guidedtroubleshoot/ — discovery.go, notifier.go, quality.go, + linkweather.go, reposition.go, alert_handler.go +mothership/internal/api/ — briefing.go, briefing_test.go, guided.go +``` + +**Phase 9 Status:** COMPLETE diff --git a/mothership/mothership b/mothership/mothership index d02dc6b..f6badc2 100755 Binary files a/mothership/mothership and b/mothership/mothership differ diff --git a/mothership/sim b/mothership/sim index 25e44cd..754c102 100755 Binary files a/mothership/sim and b/mothership/sim differ