Operator UI review on the merged build:
- Hero row felt 'odd' — operator chose remove-entirely on the
AskUserQuestion options. The Playlists row already leads with
For-You and Recently Added leads with the newest album, so the
hero duplicated both anchors. Pulled HomeHeroCard.svelte + all
page-level wiring (systemShuffle/getPlaylist imports + playForYou/
playLatestAlbum handlers) and reverted the within() test scoping
added in 682d7a5e.
- Rediscover tiles step down to w-32 (albums) / w-28 (artists),
matching the Most Played CompactTrackCard visual weight on web.
Reinforces the page hierarchy: fresh content gets real estate,
throwbacks recede.
The new hero card on Home renders the For-You playlist twice — once
at the top in the featured row and once in the Playlists carousel.
The two existing assertions that did screen.getByText('For You')
now match both and fail. Wrap each in within(playlistsSection) so
the test targets the carousel render specifically.
Mirrors the Android change from commit bf61d6cf. Web's Home Playlists
row gains the 5 secondary system kinds (deep_cuts, rediscover,
new_for_you, on_this_day, first_listens) after the Songs-like slots,
in server-registry order, when they exist. No placeholders for these
— they depend on library shape (Deep cuts needs deep albums, On this
day needs prior history, etc.) so a missing one means "not enough
data," not "still building."
Test coverage: new `renders secondary system kinds` test creates a
mocked owned set with for_you + deep_cuts + new_for_you and asserts
all three card names render. Existing tests (5 placeholders, building
variant, For-You + 3 placeholders) unchanged since none seed
secondary kinds.
Most Played multi-row is NOT touched — web already chunks the 75
tracks into 3 rows of 25 via the existing HorizontalScrollRow rows=
{chunk(...,25)} pattern. My initial task description was wrong on
that point.
Naming collision (`rediscover` playlist vs Rediscover recommendations
section both read "Rediscover" on the same Home) deferred — operator
follow-up. Same on Android.
Scribe 531, local task #61.
Replaces the per-kind refresh/shuffle handlers with one generic
pair driven off the kind registry, in lockstep across both clients.
Server:
- systemPlaylistKind gains Singleton; RefreshableSystemKind(key)
exported. for_you/discover singleton; songs_like_artist not.
- New generic POST /api/playlists/system/{kind}/refresh and
GET /api/playlists/system/{kind}/shuffle ({kind} = raw
system_variant). Non-singleton/unknown kind → 404. Deleted
playlists_{foryou,discover}_refresh.go and the per-kind shuffle
wrappers; serveSystemPlaylistShuffle core kept.
- playlistRowView.refreshable: server-derived flag so clients show
the refresh affordance generically without hardcoding kinds.
Web (not drift-cached → uses the server flag):
- refreshSystem(variant) replaces refreshForYou/refreshDiscover;
systemShuffle drops the for_you→for-you mapping (raw variant).
- PlaylistCard + detail page gate the kebab/Refresh button on
playlist.refreshable; label is "Refresh {name}". Tests reworked;
obsolete refresh-foryou/discover api tests deleted.
Flutter (list tiles are drift-cache-sourced → derive, no migration):
- Playlist.refreshable getter = isSystem && variant !=
songs_like_artist (holds for all current + planned kinds).
- refreshSystem/systemShuffle use the raw variant; PlaylistCard +
detail screen gate kebab/Regenerate/source-tagging on refreshable
so songs_like_artist plays via get() (no by-kind endpoint).
Pure-plumbing refactor; CI verifies parity. Next (R3): the five
discovery mixes — each a candidate query + one registry entry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces duplicated inline vi.mock('$lib/api/likes', ...) blocks with
the emptyLikesMock() helper, and (where previously left in working
tree by a parallel quarantine sweep) rolls in the matching
emptyQuarantineMock() switchovers in the same files.
Together with commit dd67f28 — which already swept the four search/*
test files for likes — this completes the 18 likes-mock conversions
called out in #375.
LikeButton.test.ts kept inline — it uses a state-driven mock to
assert the toggle behavior.
SKIPPED:
- liked.test.ts: re-exports createLikedTracksInfiniteQuery and the
album/artist variants that emptyLikesMock() doesn't provide.
- playlist.test.ts: hand-rolled { subscribe, data } stub instead of
readable(...) — different store contract.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>