Quality gate #7: Verify nodes without valid tokens are rejected with HTTP 401.
- Created as7_auth_reject_test.go following AS1-AS6 pattern
- Tests WebSocket connection without X-Spaxel-Token header
- Verifies HTTP 401 Unauthorized response
- Validates simulator exits non-zero with invalid token
- Confirms no zombie nodes in fleet after rejection
- Registered AS7_AuthRejectIntegration in test runner
Closes: bf-2d9fj
Add migration_018 that creates all 8 prediction subsystem tables in the main
database, consolidating the separate prediction.db and prediction_accuracy.db
files. Add NewModelStoreWithDB and NewAccuracyTrackerWithDB constructors that
accept an existing *sql.DB connection, and update main.go to use the main
database connection instead of separate files.
- Added migration_018_add_prediction_tables with all prediction tables
- Added NewModelStoreWithDB() to accept shared DB connection
- Added NewAccuracyTrackerWithDB() to accept shared DB connection
- Updated Close() to only close DB when we own it (path != "")
- Updated main.go prediction init to use mainDB
The legacy NewModelStore() and NewAccuracyTracker() constructors remain for
backward compatibility (tests continue using their own migrations).
Closes: bf-38wcp
- Use chi.Walk instead of len/range on chi.Routes interface
- chi.Routes returns an interface type, not a slice, which cannot
be used with len() or range directly
- chi.Walk is the proper API for iterating registered routes
The test verifies that both Handler and FleetHandler can be registered
on the same router without chi panicking on duplicate routes.
Closes: bf-3o15x
The simulator API was registered at /simulator but the startup log
claimed /api/simulator. Fixed the route registration to match the
log and align with the REST API pattern (all endpoints under /api/).
The dashboard page route (/simulator serving simulator.html) remains
unchanged - only the API endpoint path was fixed.
Closes: bf-1f55j
- Add flag.Parse() call in TestMain to initialize testing flags before
running tests, fixing panic when test functions call testing.Short()
- Add proper PASS/FAIL reporting for each test in the sequence
- Apply go fmt formatting to io_install_upgrade_test.go
The IO-7..IO-11 failure and edge onboarding tests were already
implemented in the codebase. This fix ensures they can run properly
by initializing the testing framework before calling test functions.
Closes: bf-1922s
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Implements IO-5 acceptance test which verifies:
- A person can be created via POST /api/people
- A simulated BLE device (from spaxel-sim) is discovered
- The BLE device can be assigned to a person via PUT /api/ble/devices/{mac}
- The device registration is persisted correctly with person_id, person_name, and person_color
Also fixes a bug in mothership/cmd/mothership/main.go where
SetBriefingProvider was called before dashboardHub was initialized,
causing a nil pointer dereference on startup. The call is now
made after the hub is created.
Closes: bf-3cagn (IO-5: BLE device-identity onboarding test)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Implements plan §Disk Full Handling:
- Created internal/diskspace package with monitor goroutine (60s poll interval)
- At <100 MB free: stop CSI replay buffer writes, emit system alert event
- At <20 MB free: also pause crowd flow accumulation and prediction updates
- Detection and localization continue regardless of disk state
- Added /api/diskspace/stats endpoint for dashboard integration
Changes:
- internal/diskspace/monitor.go: Core monitor with state machine (normal/warning/critical)
- internal/diskspace/monitor_test.go: Unit tests for pause/resume behavior
- internal/recorder/manager.go: Added PauseWrites/ResumeWrites/IsPaused methods
- internal/recorder/manager_test.go: Tests for paused frame dropping
- internal/analytics/flow.go: Added PauseWrites/ResumeWrites/IsPaused methods
- internal/analytics/flow_test.go: Tests for paused trajectory/dwell accumulation
- internal/prediction/predictor.go: Added PauseUpdates/ResumeUpdates/IsPaused methods
- internal/prediction/predictor_test.go: Tests for paused prediction updates
- cmd/mothership/main.go: Integrated monitor initialization and API endpoint
All writes are no-ops while paused, with automatic resume when space recovers.
Closes: bf-4jb0a
Add support for 'predicted_enter' trigger condition that fires when a
prediction indicates a person is likely to enter a zone within a configured
time window (default 30 minutes). Uses rising-edge detection with 60-minute
cooldown per person-zone combination.
Changes:
- Add migration_017 to expand triggers table CHECK constraint to include
'predicted_enter' (SQLite table recreation required)
- Update volume store init() for new databases with expanded constraint
- Add predicted_enter to API validation in volume_triggers.go
- Implement evaluatePredictedEnter() in volume store with rising-edge
detection and cooldown tracking
- Add PredictionProvider interface and SetPredictionProvider() methods
to both volume.Store and automation.Engine
- Wire predicted_enter evaluation into 10 Hz fusion tick pipeline
Closes: bf-20sp3
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds three test cases for quality gate #7:
1. TestAS7_AuthRejectMissingToken - verifies nodes without tokens are rejected
2. TestAS7_AuthRejectInvalidToken - verifies nodes with invalid tokens are rejected
3. TestAS7_AuthAcceptValidToken - verifies nodes with valid tokens are accepted
Each test verifies:
- Simulator exits non-zero on rejection
- Mothership logs the rejection
- No nodes connect when auth fails
Closes: bf-2d9fj
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Change runtime from debian:12-slim to gcr.io/distroless/static-debian12:nonroot
- Remove wget health check (distroless has no shell)
- Embed dashboard via go:embed (dashboard files now part of binary)
- Add build tag support for conditional embedding (production vs development)
- Dashboard serving code supports both embedded and filesystem-based serving
The dashboard is now embedded in the Go binary using go:embed with the
'embed' build tag. Production Docker builds use -tags=embed to enable
dashboard embedding, while development builds fall back to filesystem
serving. This aligns with the plan's security requirements for non-root
distroless runtime while maintaining developer ergonomics.
Closes: bf-1chgr
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add ARG TARGETPLATFORM/TARGETARCH for cross-platform builds
- Cross-compile Go binary using GOOS/GOARCH from TARGETPLATFORM
- ESP32 firmware build is amd64-only (ESP-IDF is x86_64)
- Creates placeholder on arm64 builds
- Removes placeholder in final stage, adds README
- Supports docker buildx --platform linux/amd64,linux/arm64
Closes: bf-2bxpx
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add IO-1 (Fresh install / first boot) and IO-2 (Idempotent restart & upgrade-in-place)
integration tests as hard-gate tests for releases. These tests validate the entire
new-user journey with zero physical hardware.
IO-1 validates:
- Dashboard serves on fresh install (200 OK)
- First-run PIN setup flow
- Migrations run and complete
- PIN persists across restart check
- Health endpoint returns green
- No nodes attached on fresh install
IO-2 validates:
- PIN configuration persists across restart
- Node registry persists across restart
- No re-setup prompt after restart
- Prior data is readable after restart
- Pre-upgrade DB backup exists
The tests use the existing TestHarness infrastructure and follow the plan's
Installation & Onboarding Test Plan scenarios.
Closes: bf-1r6ww
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add io_install_upgrade_test.go with IO-1 and IO-2 test scenarios
- IO-1: Fresh install / first boot
- Verifies mothership starts with empty volume
- Checks first-run setup page is served
- Validates PIN setup and persistence
- Confirms migrations run and health is green
- Ensures no nodes attached on fresh install
- IO-2: Idempotent restart & upgrade-in-place
- Verifies restart preserves PIN, nodes, and zones
- Checks no re-setup prompt after restart
- Validates data persists across restarts
- Confirms backup directory exists for upgrades
- Update integration_test.go TestMain to include IO tests
Closes: bf-dhlyk
The prediction subsystem previously created 8 tables at runtime without
version tracking in separate SQLite databases (prediction.db,
prediction_accuracy.db). This created schema drift issues where changes
were unversioned and difficult to track.
Changes:
- Add prediction_schema_version table to prediction.db (model.go)
- Add prediction_accuracy_schema_version table to prediction_accuracy.db (accuracy.go)
- Convert migrate() functions to use versioned migrations (version 1)
- All 8 tables now created through versioned migration system:
- zone_transitions_history, transition_probabilities, dwell_times, person_zone_entry
- recorded_predictions, accuracy_stats, zone_occupancy_patterns, zone_occupancy_history
Closes: bf-38wcp
- Remove duplicate node-specific routes (role, label, locate, delete) from
FleetHandler.RegisterRoutes to avoid chi panic on duplicate registration
- Keep only unique FleetHandler routes: /api/fleet/health, /api/fleet/history,
/api/fleet/optimise, /api/fleet/simulate
- Add startup smoke test TestRouteRegistrationNoPanic to verify both Handler
and FleetHandler can be registered on same router without panic
main.go registers both fleet.NewHandler and fleet.NewFleetHandler on the
same router, which previously caused chi to panic due to duplicate routes:
POST /api/nodes/{mac}/role
PATCH /api/nodes/{mac}/label
POST /api/nodes/{mac}/locate
DELETE /api/nodes/{mac}
The Handler has comprehensive node/room/mode endpoints while FleetHandler
focuses on health/optimization/simulation, so duplicates are removed from
FleetHandler.
Closes: bf-3o15x
Detailed IO-1..IO-11 scenarios validating the full new-user journey (fresh install ->
first-run PIN setup -> device onboarding -> operational) entirely via the spaxel-sim
ESP32 simulator, hardware-free and deterministic in CI. IO-1/3/4/6 are release hard-gates.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Delete compiled Go binaries (sim, spaxel-sim, cmd/sim/spaxel-sim, mothership/{sim,spaxel-sim},
*.test, acceptance.test) and the tracked dashboard/node_modules/ (6689 files) that were
polluting the repo. Add .gitignore rules so they stay out. Dashboard deps regenerate via npm ci.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
All functionality specified in the task is already in place:
- HA discovery configs published with retain=true on first connect
- Discovery re-published when zones/persons are added/renamed via callbacks
- Empty retained payload published on zone/person deletion
- MQTT command subscriptions wired (rebaseline, security_mode, system_mode)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The MQTT command/rebaseline and HA auto-discovery lifecycle management
were already implemented. Only removed an unused stub function that was
causing compilation to fail.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add CollisionDetector in fleet/collision.go that tracks frame arrivals from TX nodes
- Detect collisions when CSI frames from different TX nodes arrive within 3ms
- Calculate collision rate over 60-second sliding window
- Trigger adaptive re-stagger when collision rate exceeds 5% threshold
- Re-stagger generates random slot offsets to break up persistent collisions
- Rate limit re-stagger to minimum 30-second intervals
- Integrate with ingestion server to record frame arrivals
- Integrate with fleet manager to push updated config messages on re-stagger
- Add comprehensive unit tests for collision detection logic
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Added dashboard/static/js/fleet.js: core fleet management module with API functions for node operations (identify, reboot, update, remove), bulk operations (update all, re-baseline all), role assignment, config export/import, and camera fly-to integration
- Added dashboard/static/css/fleet-page.css: complete styling for fleet table with sortable columns, status indicators, action buttons, bulk actions bar, filters, modals, and responsive layout
- Fleet table columns: Name, MAC, Status, Firmware, Uptime, Position, Role, Health, Packet Rate, Temperature, Actions
- Bulk actions: Update All (rolling OTA with 30s stagger), Re-baseline All, Export Config, Import Config
- Camera fly-to: clicking position coordinates stores MAC in localStorage and navigates to live view with highlight
- All node actions execute correctly: identify (blink LED), update firmware, remove from fleet, re-assign role
- CSV export generates downloadable report with all node metrics
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Features implemented:
- Full table with sortable columns: Name, MAC, Role, Position, Firmware, RSSI, Status, Uptime, Actions
- Role dropdown for changing node roles
- Position column with camera fly-to on click
- Firmware version with update badge
- Bulk actions: Update All, Re-baseline All, Export Config, Import Config, CSV Report
- Node actions: Restart, Update, Remove, Identify (blink LED)
- Responsive design for desktop and mobile
- Toast notifications and modal dialogs
Complete mobile touch interaction support for the 3D expert dashboard:
- Touch orbit/pan/zoom: Single-finger rotate, two-finger pan, pinch-zoom
(Three.js OrbitControls native support)
- Hamburger menu for panel navigation: Collapsible sidebar with:
* Fleet Status
* Zones
* Automation Triggers
* Settings
- Bottom sheet panels on mobile: Slide-up panels with drag-to-close
gesture, 70vh max height, safe-area inset support
- Touch-optimized buttons: Minimum 44×44px tap targets throughout
- No hover-dependent UI: All interactions work via touch
- Long-press context menu: Mobile replacement for right-click
Files:
- dashboard/static/js/mobile.js - Mobile interaction controller
- dashboard/static/css/mobile.css - Mobile-specific styles
Acceptance:
✓ Three.js scene responds to touch gestures
✓ Hamburger menu opens panel navigation
✓ Panels slide in from bottom on mobile
✓ All buttons are touch-friendly (≥44px)
✓ No features require hover
✓ Long-press context menu works on mobile
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add hamburger menu for mobile panel navigation
- Create bottom sheet panels that slide from bottom on mobile
- Implement touch-optimized UI with 44x44px minimum tap targets
- Add mobile-specific panel content for Fleet Status, Zones, Triggers, Settings
- Support drag-to-close gesture on bottom sheets
- Maintain existing desktop panel behavior
- Integrate with existing systems (Viz3D, SpaxelPanels, SpatialQuickActions)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Proactive contextual help that appears when users encounter problems,
but never when things are working well.
Trigger conditions:
- Detection quality drop: Zone-level quality below 60% for >24h
triggers helpful banner with guided diagnostics flow
- Repeated setting changes: Same settings key modified 3+ times
within 60-minute sliding window triggers helpful hint
- Node offline: Any node offline for >2 hours shows troubleshooting
- First-time feature discovery: Brief tooltip shown once per feature
- After false positive feedback: Inline response explaining adjustments
- After successful calibration: Positive reinforcement message
Implementation:
- ZoneQualityTracker: Tracks per-zone detection quality, triggers
callbacks when quality degrades
- EditTracker: Monitors settings edits for repeated changes
- FleetNotifier: Tracks node offline events
- DiscoveryTracker: Manages first-time feature tooltips
- API endpoints: /api/guided/* for diagnostics and feedback
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Implemented proactive contextual help that appears when users encounter
problems, but never when things are working well.
Trigger conditions:
- Detection quality drops: Zone-level detection quality below 60%
for >24 hours triggers helpful banner with guided diagnostics flow
- Repeated setting changes: Same qualifying settings key modified 3+
times within 60-minute sliding window triggers hint banner
- Node offline: Any node offline for >2 hours shows timeline event
with expandable troubleshooting steps
- First-time feature discovery: Brief, non-intrusive tooltips shown
once per feature
- After false positive feedback: Inline response explaining threshold
adjustments
- After successful calibration: Positive reinforcement message
showing quality improvement
Design principles:
- Reactive, not proactive: help appears only when something seems wrong
- Dismissible in one tap: never blocks UI
- Never repeats after dismissal (stored in localStorage)
- Always explains what will happen next
- Never condescending: assumes user is intelligent
All tests pass (go test ./internal/guidedtroubleshoot/... and
npm test -- --testPathPattern=troubleshoot).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add briefing dashboard adapter wiring to hub for morning briefing push
- Morning briefing provides warm summary when user first opens dashboard each day
- Content includes: sleep summary, who is home, overnight anomalies, system health, predictions, learning progress
- Briefing generated automatically and stored as daily record in briefings table
- Delivered via dashboard (card overlay on first open), push notification at configured time, or webhook
- Display modes supported: expert mode (overlay), simple mode (first card), ambient mode (fade-in text)
- Briefing scheduler generates daily briefings and sends push notifications
- API endpoints: GET/POST /api/briefing/* for managing briefings
- Go function GenerateBriefing(date, person) assembles briefing in priority order
- Sections: critical alerts → sleep → who's home → anomalies → system health → predictions → learning
- Stored as daily record in briefings table with sections_json
- Expert mode: card overlay on first open, dismissible, slides away after 10s
- Simple mode: morning card as first card in layout
- Ambient mode: text fades in on first person detection, stays for 30s
- Delivery via dashboard, push notification, or webhook
- All acceptance criteria met
Dedicated display mode for wall-mounted tablets or always-on screens.
Served at /ambient as separate lightweight route.
Implementation:
- Simplified, stylized top-down floor plan with clean lines and soft rounded corners
- People appear as softly glowing colored circles (BLE-identified) or neutral dots (unknown)
- Room labels show subtle occupancy: 'Kitchen · Alice' or 'Bedroom · Empty'
- Smooth, calm animations with interpolated positions (no jitter, no snapping)
- Canvas 2D renderer for minimal resource usage (no Three.js)
- Time-of-day awareness: morning (bright/cool), day (neutral), evening (warm/amber), night (very dim)
- Auto-dim when house empty for 30+ minutes with 'All secure' text
- Alert mode: pulsing red border, large text, action buttons for fall/security events
- Morning briefing integration: shows briefing text on first person detection
Files:
- dashboard/ambient.html - Ambient mode page
- dashboard/js/ambient.js - Ambient controller with WebSocket
- dashboard/js/ambient_renderer.js - Canvas 2D renderer with time-of-day palettes
- dashboard/js/ambient_briefing.js - Morning briefing integration
- dashboard/css/ambient.css - Comprehensive styling with time-of-day themes
Acceptance:
- Runs unattended on wall-mounted tablet for 7+ days
- Time-of-day palette transitions smoothly
- Alert mode breaks the calm appropriately
- Morning briefing displays on first detection
- Resource usage: <30 MB RAM, <5% CPU target
- Fix auto-dim timeout from 60 seconds to 30 minutes (30 * 60 * 1000ms)
- Add auth.js dependency to ambient.html for proper authentication
- Rewrite ambient.test.js to remove problematic require() statements
- Update ambient.js with proper time-of-day handling
- Enhance ambient.css with time-of-day palette themes
Ambient mode provides a simplified, always-on display for wall-mounted
tablets with Canvas 2D rendering, time-of-day awareness, auto-dim after
30min of no presence, alert mode with pulsing red border, and morning
briefing integration.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Card-based mobile-first UI for non-technical users
- Room cards showing occupancy count, person names, and status color
- Activity feed with chronological event list from timeline
- Alert banner for fall detection, anomaly alerts, and system warnings
- Quick actions: arm/disarm security, re-baseline, silence alerts
- Sleep summary card showing last night's sleep data
- Toggle between simple/expert mode with localStorage preference
- Added /simple route in mothership for serving simple mode page
- Added purple color scale to tokens.css for sleep features
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add simple.html: mobile-first card-based UI without 3D scene
- Room cards show occupancy count, person names, and status color
- Activity feed displays chronological events from timeline
- Alert banner for fall detection, anomaly alerts, system warnings
- Quick actions: arm/disarm security, re-baseline, silence alerts
- Sleep summary card showing last night's sleep data
- Simple/expert mode toggle button in all views
- Mobile-responsive layout with touch-friendly interface
- Per-user default mode stored in localStorage
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Raycast against both wireframe and fill meshes for reliable detection
- Wireframe is always present even when fill has 0 opacity (zones 2+)
- Sort intersections by distance to get the closest intersection
- Fixes hover detection not working properly for multi-zone Fresnel ellipsoids