Release v2026.05.15.0 — system playlists v2, offline cache rework, CI speedup #48

Merged
bvandeusen merged 262 commits from dev into main 2026-05-15 23:15:48 -04:00
bvandeusen commented 2026-05-15 23:15:37 -04:00 (Migrated from git.fabledsword.com)

Large batch. Two umbrellas + play-event correctness + CI. CI green on dev; the pre-PR cross-cutting consistency audit found no blocking issues (route↔client contracts, removed-handler cleanup, sqlc/drift sync, two-bucket migration, refreshable web-flag vs Flutter-derived parity all verified).

#411 — System playlists v2

  • Snapshot/shuffle behavior: For-You no longer force-refreshes on play; shuffle-on-play default; For-You expanded to 100; sample-history rotation dedup (server-side play_events.source + rotation state).
  • Generalized per-tile refresh affordance (web kebab + Flutter); detail-screen Regenerate for system playlists.
  • Play-event reporting fixed: in-queue track end classified ended-vs-skipped (was poisoning recommendation skip-ratios) and offline replay capture so cached-track plays aren't lost.
  • R1→R3 kind-registry refactor: generic /api/playlists/system/{kind}/{refresh,shuffle}; adding a mix = one query + one registry entry.
  • Five new mixes: Deep Cuts, Rediscover, New for you, On This Day, First Listens.
  • "Refreshed …" freshness subtitle on system tiles.
  • "Not for me" intentionally dropped (likes + skips are the only feedback signals).

#427 — Offline cache rework

  • S1: /healthz reachability offlineProvider (smoke-safe; no connectivity coupling).
  • S2: single 5GB capBytes → independent Liked + Rolling 5GB caps; bucket = liked-ness; storage-only dedup (never filters playback); orphan-partial accounting so the rolling cap truly bounds disk; liked-protective eviction. Explicit Download removed.
  • S3: two cap selectors + per-bucket usage meters.
  • S4: offline disables system-playlist play; always-present Shuffle all (online GET /api/library/shuffle; offline cache-union); offline Home tiles "Recently played" / "Liked".

CI

  • Debug APK gated to main only (dev runs ~3min not ~10); runner image republished with the Android SDK baked in (the ~7-min per-run download is gone); Dockerfile shard-hardened for reliable future pushes.

Device-check checklist (post-merge, on a real build)

  • S2 (highest risk — deletes cached files): lower the Liked cap and the Rolling cap; confirm each evicts only its own bucket and liked tracks survive rolling pressure; two usage meters update.
  • Airplane mode: Library "Shuffle all" plays the cached union; Home shows "Recently played"/"Liked" tiles that play from cache; system-playlist tile play is disabled.
  • Online: "Shuffle all" hits /api/library/shuffle; offlineProvider flips after ~3 failed /healthz probes and recovers on reconnect.
  • The five new mixes populate sensibly on a built library; "Refreshed …" subtitle shows.
  • Play a multi-track queue to completion: rows show was_skipped=false (not the old false-skip); offline plays replay on reconnect with correct timestamps.

🤖 Generated with Claude Code

Large batch. Two umbrellas + play-event correctness + CI. CI green on `dev`; the pre-PR cross-cutting consistency audit found no blocking issues (route↔client contracts, removed-handler cleanup, sqlc/drift sync, two-bucket migration, `refreshable` web-flag vs Flutter-derived parity all verified). ## #411 — System playlists v2 - Snapshot/shuffle behavior: For-You no longer force-refreshes on play; shuffle-on-play default; For-You expanded to 100; sample-history rotation dedup (server-side `play_events.source` + rotation state). - Generalized per-tile refresh affordance (web kebab + Flutter); detail-screen Regenerate for system playlists. - Play-event reporting fixed: in-queue track end classified ended-vs-skipped (was poisoning recommendation skip-ratios) **and** offline replay capture so cached-track plays aren't lost. - R1→R3 kind-registry refactor: generic `/api/playlists/system/{kind}/{refresh,shuffle}`; adding a mix = one query + one registry entry. - Five new mixes: Deep Cuts, Rediscover, New for you, On This Day, First Listens. - "Refreshed …" freshness subtitle on system tiles. - "Not for me" intentionally **dropped** (likes + skips are the only feedback signals). ## #427 — Offline cache rework - S1: `/healthz` reachability `offlineProvider` (smoke-safe; no connectivity coupling). - S2: single 5GB `capBytes` → independent **Liked + Rolling** 5GB caps; bucket = liked-ness; storage-only dedup (never filters playback); orphan-partial accounting so the rolling cap truly bounds disk; liked-protective eviction. **Explicit Download removed.** - S3: two cap selectors + per-bucket usage meters. - S4: offline disables system-playlist play; always-present **Shuffle all** (online `GET /api/library/shuffle`; offline cache-union); offline Home tiles "Recently played" / "Liked". ## CI - Debug APK gated to `main` only (dev runs ~3min not ~10); runner image republished with the Android SDK baked in (the ~7-min per-run download is gone); Dockerfile shard-hardened for reliable future pushes. ## Device-check checklist (post-merge, on a real build) - [ ] **S2 (highest risk — deletes cached files):** lower the Liked cap and the Rolling cap; confirm each evicts only its own bucket and **liked tracks survive rolling pressure**; two usage meters update. - [ ] Airplane mode: Library "Shuffle all" plays the cached union; Home shows "Recently played"/"Liked" tiles that play from cache; system-playlist tile play is disabled. - [ ] Online: "Shuffle all" hits `/api/library/shuffle`; `offlineProvider` flips after ~3 failed `/healthz` probes and recovers on reconnect. - [ ] The five new mixes populate sensibly on a built library; "Refreshed …" subtitle shows. - [ ] Play a multi-track queue to completion: rows show `was_skipped=false` (not the old false-skip); offline plays replay on reconnect with correct timestamps. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bvandeusen/minstrel#48