- StorageSubscriber: refactor to two-phase shutdown (forwarders drain
EventBus channels into queue, then worker drains queue to SQLite)
ensuring no in-flight events are lost on Stop()
- Fix bus_test: increase channel capacity to avoid non-blocking drops
in TestEventBusConcurrentPublish
- Fix events_test: set MaxOpenConns(1) for in-memory SQLite to prevent
concurrent-connection data visibility issues
- Fix storage_test: remove manual ctx/cancel initialization after
StorageSubscriber struct was refactored to separate workerCtx/forwarderCtx
- Fix api/events.go: zone_id and person_id always take precedence;
until timestamp uses < (cutoffMs+1000) to include full RFC3339 second
- Fix api/events_test: default mode is expert, simple mode requires
explicit ?mode=simple parameter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Zero connected nodes is valid for headless/standby deployments. The
5-minute grace period was causing an infinite crash loop: healthz
returned 503 after 5min uptime → liveness probe failed → pod restarted.
The "no nodes connected" reason is still reported but no longer sets
status to degraded.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of serving the full dashboard HTML (which exposes the UI shell
when the auth overlay is deleted), serve a minimal page that only loads
the auth JS and CSS. Deleting the overlay now reveals a blank page.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Chi panics if r.Use() is called after any route registration.
Move the auth middleware registration before RegisterRoutes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The PIN overlay was client-side only — deleting the DOM element bypassed
auth entirely. Add global chi middleware that returns 401 on protected
endpoints when no valid session cookie is present. Static files pass
through so the login page renders. During onboarding (no PIN set), all
routes remain open.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SQLite doesn't support IF NOT EXISTS on ALTER TABLE ADD COLUMN.
The error_message and error_count columns already exist in the
triggers table from v0.1.33, causing migration 7 to fail on upgrade.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- fleet/handler.go: add ota. package prefix to NodeOTAProgress
- notification_settings.go: remove duplicate declarations of
testNotificationRequest, validateChannelConfig, writeJSON, and
writeJSONError that conflict with notifications.go and utils.go;
fix missing closing brace in validateTimeFormat
- cmd/mothership/main.go: use sigproc.HealthLogEntry (the actual
return type of GetHealthHistory) instead of diagnostics.LinkHealthSnapshot
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add real-time OTA update progress broadcasting to dashboard clients:
- Hub.BroadcastOTAProgress() sends progress updates over WebSocket
- OTA Manager now broadcasts state transitions: pending → downloading → rebooting → verified/failed/rollback
- Dashboard can show live OTA status per node during fleet updates
- Includes test suite for fleet page functionality
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The fleet status page is fully implemented with all required features:
- HTML structure with navigation, filters, bulk actions, modals
- JavaScript with state management, filtering, sorting, inline editing, camera fly-to, CSV export
- CSS with responsive design and comprehensive styling
- REST API endpoints for all fleet operations
- Comprehensive test coverage
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Show "updating" status for nodes undergoing OTA updates
- Add OTAInProgress field to FleetNode response
- Improve updateAllNodes to trigger rolling OTA updates
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds frontend implementation for feature discovery notifications:
- Polls /api/help/notifications every 30 seconds
- Displays notification cards with slide-in animation
- Handles action button clicks for navigation
- Persists dismissed notifications to prevent re-display
- Auto-dismisses after 30 seconds
Completes the feature discovery notifications feature for Phase 9,
complementing the backend notifier and monitor implementations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Show non-blocking prompt card when ambient confidence drops below 0.6 for >5 minutes
- Add 'Diagnose' button that fetches and displays root cause analysis
- Add 'Dismiss for today' option with localStorage persistence
- Implement pulsing amber highlight on 3D link lines for degraded links
- Display diagnostic results in plain English with possible causes and actions
- Do NOT show prompts for transient drops (< 5 minutes)
- Add GetDiagnosticFor method to diagnostics package for timestamp-based queries
- Wire up /api/links/{linkID}/diagnostics endpoint with health snapshots
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add mode switching for timeline panel. Expert mode shows all event
types with system events as secondary (smaller, greyed). Simple mode
shows only person-relevant events: ZoneTransition, FallDetected,
AnomalyDetected, SleepSessionEnd. Mode is set by dashboard mode and
passed as ?mode=expert or ?mode=simple to API.
Changes:
- dashboard/index.html: Add sidebar timeline panel HTML and script include
- dashboard/js/sidebar-timeline.js: Add sidebar timeline with mode switching
- dashboard/js/sidebar-timeline.test.js: Add tests for mode switching behavior
- mothership/internal/ingestion/server.go: Add health score to link info
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add mode switching for timeline panel with ?mode=expert or ?mode=simple
- Expert mode displays all event types with system events as secondary (smaller, greyed)
- Simple mode shows only person-relevant events: ZoneTransition, FallDetected, AnomalyDetected, SleepSessionEnd, zone_entry/exit, portal_crossing, fall_alert, anomaly, security_alert
- Backend defaults to expert mode when mode parameter is empty or invalid
- Frontend syncs dashboard mode with SpaxelSimpleModeDetection for mode changes
- Add CSS styling for new event types (ZoneTransition, FallDetected, AnomalyDetected, sleep_session_end)
- Update isValidEventType to include new event types
The writeJSON function uses json.NewEncoder which adds a newline
character. Changed raw string literals to interpreted strings
so \n becomes an actual newline character.
- Added dropdown menu for More actions button with options:
- Re-assign Role
- View Health History
- View Event History
- Remove from Fleet
- Added CSS styles for dropdown menus with proper positioning
and hover states
- Extended FleetHandler with additional API endpoints:
- PATCH /api/nodes/{mac}/label - update node label
- POST /api/nodes/{mac}/locate - send identify command
- POST /api/nodes/{mac}/role - assign new role
- DELETE /api/nodes/{mac} - remove from fleet
- Added label validation (max 32 characters)
- Improved test code quality with helper functions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix TestAnalyticsHandler_ErrorHandling to use proper in-memory database
instead of nil database which caused nil pointer dereference
- Update handleGetCorridors to return corridors wrapped in {corridors: [...]}
for consistency with frontend expectations from crowdflow.js
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- cmd/mothership/main.go: fix GetAllZones (single return), LastSeenAt vs LastSeenMs,
remove undefined fusionEngine block, fix weights.GetLinkWeight usage, hoist
learningHandler scope, remove unused recordingBuf/lastDetectionEvent vars, remove
sync import, fix computeZoneQuality pointer dereference, fix pred field names
(PredictedNextZoneID/PredictionConfidence), fix AccuracyStats.TotalPredictions,
add GetNodeOfflineDuration to healthProviderAdapter, fix GetAccuracyDelta stub
- internal/api/guided.go: refactor GuidedManager interface to use time.Duration for
TriggerNodeOffline, use any for zonesHandler/nodesHandler, remove diagnostics.Tooltip
dependency, add GetTooltipAny type-assertion approach for cross-package tooltip access
- internal/api/tracks.go: unify TracksProvider to use signal.TrackedBlob directly via
type alias to resolve interface mismatch
- internal/api/diurnal.go: add signalProcessorManagerAdapter and
NewDiurnalHandlerFromSignal to bridge signal.ProcessorManager to DiurnalProcessorManager
- internal/guidedtroubleshoot/quality.go: add RecordEdit, MarkHintShown, GetTooltipAny
methods to Manager to satisfy api interfaces
- internal/fusion/fusion.go: remove unused log import, fix oy declared-and-not-used
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The crowdflow.js module expected a person filter dropdown in the patterns
section of the dashboard UI. This dropdown allows filtering flow and dwell
data by specific people or viewing all people together.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix Viz3D exports to include flow visualization functions
- Export setFlowLayerVisible, setDwellLayerVisible, setCorridorLayerVisible
- Export setFlowTimeFilter, setFlowData, setDwellData, setCorridorData
- Remove duplicate setDwellLayerVisible function definition
This completes the crowd flow visualization feature that was
already implemented in the backend (flow.go) and frontend
(crowdflow.js, viz3d.js) but had missing exports in the Viz3D module.
Add comprehensive MQTT and webhook integration for Home Assistant and external services:
MQTT Client (internal/mqtt/client.go):
- Optional MQTT client with exponential backoff reconnect (5s-120s)
- TLS support for mqtts:// connections
- Home Assistant auto-discovery for persons, zones, fall detection, system health, system mode
- Topic structure: spaxel/{mothership_id}/person/{id}/presence, zone/{id}/occupancy, etc.
- LWT (Last Will and Testament) for availability
- Dynamic configuration updates via API
- Retained messages for presence and occupancy states
MQTT Publisher (internal/mqtt/publisher.go):
- Event bus subscriber publishing zone entry/exit, fall alerts, anomalies
- Person presence tracking across zones with home/not_home states
- Zone occupancy counting with occupants list
- Periodic system health publishing (60s interval)
- HA discovery methods for all entity types
- Person and zone discovery removal on delete
System Webhook (internal/webhook/publisher.go):
- Single webhook URL receiving all events with X-Spaxel-Event header
- JSON payload with event_type, timestamp, zone, person, blob_id, severity, detail
- Retry policy: one retry after 30s on 5xx errors
- Test webhook endpoint for configuration verification
API Integration Handler (internal/api/integrations.go):
- GET/POST /api/settings/integration for MQTT and webhook configuration
- POST /api/settings/integration/test for testing connections
- Settings persisted in database settings table
- Integration with existing MQTTClient and WebhookPublisher interfaces
Dashboard Integration UI (dashboard/integrations.html, js/integrations.js):
- Settings panel with MQTT broker URL, username, password (masked), TLS toggle
- Discovery prefix configuration
- Test Connection and Publish Discovery buttons
- System webhook URL configuration with enable toggle
- Connection status indicator with error messages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>