- resolve() no longer discards a stored seed when the bitmap cannot be decoded (it used to
return null, dropping the theme for pre-v12 rows); it falls back to the stored pair
- fold the busy flag into the seed cache value so eviction is atomic (a busy entry could be
evicted from the separate flag LRU while its seed stayed, silently disabling the scrim)
- the artist top bar samples heroBarColor at scroll 0 to match the static HeroGradient, and
drops the wrong scroll estimate and per-pixel recomposition
- rememberOverlaidNameBusy keys its state so the cached flag is read on key change, not only
the first composition; bottomBandBusy bails out on an empty bitmap
- Paged loops now throw when they hit maxPages with a full last page, so a
truncated enumeration aborts the entity's collect and skips its prune
instead of deleting rows the sync never saw. The cap is injectable for
tests.
- Move sync serialization into SyncManager (tryLock coalesces duplicate
requests) and construct SyncService there; SourceRepository only exposes
the active MusicSource. Removes the repo <-> sync import cycle.
- Re-throw CancellationException instead of reporting a cancelled sync as
failed.
- Add buffer(1) to the outer albums/songs/playlistSongs flows so their
flowOn channel cannot hold 64 batches.
- Rank prefetch is best-effort (missing frequent/recent no longer aborts
the album sync).
- Skip playlist entries without an id, keeping positions contiguous.
- Chunk stale-disc deletes like the other prunes.
- Update the implementation plan line that still described the old
single-transaction sync.
adds docs/architecture.md (stack, layers, data model, sync, search, networking, testing, CI), docs/implementation-plan.md (phases and status), and AGENTS.md for agents and contributors