Follow-ups on top of the M9 device-diagnostics release, all CI-green on dev.
Web
Diagnostics view defaults to most-recent-500; start/end window moved to a collapsed "Advanced" disclosure; newest-first sort (with oldest-first option); export follows displayed order.
Diagnostics relabel
route + player_state events now report kind: "playback" (they fire for every output, not just UPnP); upnp_sync reserved for genuine UPnP/Sonos signal. Migration 0037 extends the kind CHECK.
Output-picker bug fix
OutputRoute.isConnected was derived from RouteInfo.connectionState, which stays DISCONNECTED for local SYSTEM routes even when active — so a connected Bluetooth device showed "Available". Subtitle now uses isSelected; dead isConnected field removed.
Diagnostics depth (for the UPnP desync hunt)
Per-skip track_change event captured at the instant of each skip — the 45s heartbeat was missing rapid-skip moments.
Track-identity enrichment: track_change + heartbeat now carry local_track_id + sonos_uri (the URL the speaker is actually streaming), so a re-cast index wobble no longer makes desync ambiguous.
sonos_* zeroed unless a remote route is active — fixes cast→phone stale-state pollution.
Follow-ups on top of the M9 device-diagnostics release, all CI-green on dev.
## Web
- Diagnostics view defaults to most-recent-500; start/end window moved to a collapsed "Advanced" disclosure; **newest-first sort** (with oldest-first option); export follows displayed order.
## Diagnostics relabel
- `route` + `player_state` events now report `kind: "playback"` (they fire for every output, not just UPnP); `upnp_sync` reserved for genuine UPnP/Sonos signal. Migration 0037 extends the kind CHECK.
## Output-picker bug fix
- `OutputRoute.isConnected` was derived from `RouteInfo.connectionState`, which stays DISCONNECTED for local SYSTEM routes even when active — so a connected Bluetooth device showed "Available". Subtitle now uses isSelected; dead `isConnected` field removed.
## Diagnostics depth (for the UPnP desync hunt)
- **Per-skip `track_change` event** captured at the instant of each skip — the 45s heartbeat was missing rapid-skip moments.
- **Track-identity enrichment**: `track_change` + heartbeat now carry `local_track_id` + `sonos_uri` (the URL the speaker is actually streaming), so a re-cast index wobble no longer makes desync ambiguous.
- `sonos_*` zeroed unless a remote route is active — fixes cast→phone stale-state pollution.
Refs Scribe M9 (#119), tasks #1204 #1205 #1210 #1211.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The diagnostics view already defaulted to the most recent 500 events (no
window); make that the obvious path. Device/Kind stay primary; the
start/end window + row cap move into a collapsed "Advanced filters"
disclosure (auto-opens when a window is active) with a "Reset to recent
500" action. Caption now states whether you're seeing the recent default
or a windowed slice.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K55iTxn95BtshocgdE1shW
Relabel (#1204): route + player_state events fire for every output route,
not just UPnP — split them into a new 'playback' kind; 'upnp_sync' now
means genuinely UPnP/Sonos signal (drops, resync). Migration 0037 adds
'playback' to the kind CHECK; server whitelist, Android reporter labels,
and the web kind filter updated.
Web sort: the diagnostics list gains a Newest/Oldest-first sort (default
newest at top); export follows the displayed order.
Fix (#1205): OutputRoute.isConnected was derived from RouteInfo.connectionState,
which stays DISCONNECTED for local SYSTEM routes even when active — so a
connected Bluetooth device showed "Available" and reported connected:false.
The picker subtitle now uses isSelected (route == selected route); the dead
isConnected field is removed and the misleading `connected` field dropped
from the diagnostics route event (it only ever logs the active route).
Refs Scribe M9 (#119), tasks #1204#1205.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K55iTxn95BtshocgdE1shW
Heartbeats are 45s apart and missed a rapid skip burst (local_index
16→22 in one gap). Add a 'playback' track_change event emitted on each
queue-index / current-track change, snapshotting local vs Sonos
index+position + server_health + upnp_loading + route — so a transient
skip-induced desync is captured at the instant it happens. (uiState is a
conflated StateFlow, so a very rapid burst may coalesce intermediate
indices; we still get the boundaries + the snapshot.)
Refs Scribe M9 (#119), task #1210.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K55iTxn95BtshocgdE1shW
Numeric indices wobble across re-casts (offset +1↔0 seen during output
toggling), making "same track?" ambiguous. Enrich both the track_change
event and the heartbeat with local_track_id (TrackRef.id) and sonos_uri
(RemotePlayerState.currentTrackUri — the URL the speaker is actually
streaming), so a desync is unambiguous.
Also fixes the cast→phone stale-state pollution (#1211): sonos_* is now
zeroed unless a remote route is active, via a shared putSonos() helper —
so a just-ended cast's RemotePlayerState can't masquerade as live Sonos
data in the diagnostics.
Refs Scribe M9 (#119), tasks #1210#1211.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K55iTxn95BtshocgdE1shW
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Follow-ups on top of the M9 device-diagnostics release, all CI-green on dev.
Web
Diagnostics relabel
route+player_stateevents now reportkind: "playback"(they fire for every output, not just UPnP);upnp_syncreserved for genuine UPnP/Sonos signal. Migration 0037 extends the kind CHECK.Output-picker bug fix
OutputRoute.isConnectedwas derived fromRouteInfo.connectionState, which stays DISCONNECTED for local SYSTEM routes even when active — so a connected Bluetooth device showed "Available". Subtitle now uses isSelected; deadisConnectedfield removed.Diagnostics depth (for the UPnP desync hunt)
track_changeevent captured at the instant of each skip — the 45s heartbeat was missing rapid-skip moments.track_change+ heartbeat now carrylocal_track_id+sonos_uri(the URL the speaker is actually streaming), so a re-cast index wobble no longer makes desync ambiguous.sonos_*zeroed unless a remote route is active — fixes cast→phone stale-state pollution.Refs Scribe M9 (#119), tasks #1204 #1205 #1210 #1211.
🤖 Generated with Claude Code