-- Device diagnostics / debug-reporting subsystem (M9). -- -- When an account has users.debug_mode_enabled = true, that account's -- client(s) stream a timeseries of device state here: connectivity + -- server-health transitions (roaming / poor-data recovery), UPnP/Sonos -- sync events (locked-phone desync), and power/lifecycle state (Doze / -- battery-optimization, the prime suspect for the desync). The admin -- /admin/diagnostics view filters by account + device + time window and -- exports the slice as JSON for offline analysis. -- -- `kind` is a COARSE category gated by a CHECK whitelist; the finer -- event discriminator + all event-specific fields live in the `payload` -- jsonb. This keeps the enum small and stable so new event variants -- don't require a migration (per the enum-CHECK-whitelist rule, only a -- new *category* would). If you add a category here, mirror it in the -- handler whitelist in internal/api/diagnostics.go. -- -- Two timestamps on purpose: occurred_at is the client device clock -- (when the event happened on the phone — may be skewed/wrong, e.g. in -- a dead zone), received_at is the server clock at ingest. Retention -- pruning is keyed on received_at so a bad client clock can't keep rows -- alive forever. CREATE TABLE diagnostic_events ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, client_id text NOT NULL, app_version text, os_version text, kind text NOT NULL, payload jsonb NOT NULL DEFAULT '{}'::jsonb, occurred_at timestamptz NOT NULL, received_at timestamptz NOT NULL DEFAULT now(), CONSTRAINT diagnostic_events_kind_check CHECK (kind IN ( 'connectivity', 'upnp_sync', 'power', 'lifecycle', 'heartbeat', 'http' )) ); -- Admin reads filter by account then device, ordered newest-first. CREATE INDEX idx_diagnostic_events_user ON diagnostic_events (user_id, occurred_at DESC); CREATE INDEX idx_diagnostic_events_client ON diagnostic_events (client_id, occurred_at DESC); -- Retention pruner deletes by server-clock age. CREATE INDEX idx_diagnostic_events_received ON diagnostic_events (received_at); -- Per-account opt-in for diagnostics reporting. Admin-set primarily -- (flip remotely while a bug is live); the client also exposes a local -- OFF switch. Default false: no account reports until explicitly enabled. ALTER TABLE users ADD COLUMN IF NOT EXISTS debug_mode_enabled boolean NOT NULL DEFAULT false;