154626ae94
Reported: on poor coverage a track ends and the next (uncached) track never starts — streams, hangs, no retry. Root cause: a buffering stall emits NO error event so the onError path never fires and there was no stall watchdog; even on a real error _handlePlaybackError immediately skipped the literal-next (likely also-unreachable) source with no retry. - _reconcileStallWatchdog: while playing+buffering, a 15s window; if buffered position hasn't advanced it's a dead stream → recover; if progressing, re-arm (slow-but-downloading is fine). Driven from _broadcastState like the idle/position reconcilers. - _recoverPlayback unifies stall + onError: retry the SAME track once (skipToQueueItem rebuilds a fresh source/HTTP — a transient blip no longer loses it); on exhaustion, surface via the #58 SnackBar and skip to the next cached track, else pause (no thrashing through unreachable streams). - per-track retry budget resets when a track reaches ready+playing. - _handlePlaybackError now delegates into the unified path. Core playback change — device-verify on a throttled connection. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>