Commit Graph

41 Commits

Author SHA1 Message Date
bvandeusen d77f2d4459 docs(plan): add M5a Lidarr connection + search/add implementation plan
21-task TDD plan implementing the M5a spec (committed in 17406ee).
Tasks 1-4 are step-level (write test → run → implement → run → commit);
Tasks 5-21 are paragraph-level with template references back to 1-4
to keep the plan navigable while preserving full coverage.

Coverage:
- Task 1: migration 0010 + sqlc queries
- Task 2: internal/lidarr typed client
- Task 3: internal/lidarrconfig singleton wrapper
- Task 4: internal/lidarrrequests Service
- Task 5: internal/lidarrrequests Reconciler worker
- Task 6: RequireAdmin middleware
- Tasks 7-10: API handlers (search, requests CRUD, admin lidarr, admin requests)
- Task 11: cmd/minstrel main wires reconciler
- Tasks 12-13: web design tokens + API clients
- Tasks 14-15: DiscoverResultCard + StatusPill components
- Tasks 16-20: /discover, /requests, /admin/* routes
- Task 21: verification + branch finish

Self-review checklist confirms every spec section maps to a task.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 15:01:20 -04:00
bvandeusen 17406eebee docs(spec): add M5a Lidarr connection + search/add design
M5a is the foundation slice of M5 (Lidarr integration + quarantine).
Decomposed into M5a/M5b/M5c during brainstorming on 2026-04-29:

- M5a (this spec) — Lidarr connection + search/add + admin shell
- M5b — Quarantine workflow (per-user soft-hide, admin resolution)
- M5c — Radio suggested-additions (out-of-library MBIDs surfaced)

Each ships as its own PR with its own brainstorm/spec/plan cycle.

Key decisions captured:
- Permissions: search-all, add-admin via request queue
- Config: DB-only, Settings UI is the only entry point (no YAML)
- Settings shape: dedicated /admin/* route group, hard route gate
- Search UX: standalone /discover route, all three granularities
- Lifecycle: library scan as source of truth + 5-min reconciler worker
- Quality profile/root folder: default + per-add admin override
- UI lands at FabledSword design system bar (forest-teal accent;
  Moss/Bronze/Oxblood action buttons; per project_design_system memory)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 14:25:08 -04:00
bvandeusen 2e6d2ffd03 docs(plan): add M4c radio similarity-driven candidate pool plan
6-task TDD-shaped plan covering: LoadRadioCandidatesV2 sqlc query
(5-way UNION), Score() extension with SimilarityScore + SimilarityWeight,
LoadCandidatesFromSimilarity Go function with CandidateSourceLimits +
10 integration tests covering all sources + dedup + exclude + recently-
played + seed-excluded + empty library, radio handler with exclude
param + parseExcludeParam helper + M3 fallback + 4 new HTTP tests,
frontend queue refresh at 80% with radioSeedId state + clear-on-non-
radio-enqueue + 5 vitest tests, final verification + branch finish
(closes M4).
2026-04-29 06:58:22 -04:00
bvandeusen 267c4ad80b docs(spec): add M4c radio similarity-driven candidate pool design
Third and final M4 sub-plan (Fable #347) — closes M4. Replaces M3's
whole-library candidate pool with a 5-way SQL UNION (LB-similar tracks /
tracks by similar artists / MB-tag overlap / likes-overlap / random
fill). Adds a new SimilarityScore × SimilarityWeight term to M3's Score()
formula. Web client auto-refreshes the queue when 80% consumed via a new
?exclude= query param. No lazy LB fetch (M4b's worker + random
augmentation handle sparse data). Fallback to M3 LoadCandidates on any
similarity-pool error.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 23:02:01 -04:00
bvandeusen 9ec9caf667 docs(plan): add M4b ListenBrainz inbound similarity ingest plan
8-task TDD-shaped plan covering: migration 0009 (track_similarity +
artist_similarity tables with multi-source PK), sqlc queries (list-
needing, get-by-mbids, upsert), LB client SimilarRecordings + Similar
Artists methods (7 httptest tests each), Worker skeleton + tickOnce
with track and artist passes (10 integration tests covering top-K=20,
7-day cap, in-library filter, 429-abort-tick, transient-skip, no-MBID
skip), main.go boot wiring, final verification.
2026-04-28 19:51:57 -04:00
bvandeusen 86cb8e5cbf docs(spec): add M4b ListenBrainz inbound similarity ingest design
Second M4 sub-plan (Fable #346). Periodic worker pulls track-track and
artist-artist similarity edges from LB's public /explore/* endpoints,
filters to the local library, stores top-20 per source track in two new
tables (track_similarity, artist_similarity). Hourly tick, batch=5,
weekly re-fetch cap per row, passive retry via timer. No auth (public
endpoints). Discovery within library handled by LB's collaborative-
filtering response naturally surfacing unplayed library tracks; spec
notes M4c will add a serendipity floor + lazy fetch + sparse-fallback.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 19:46:23 -04:00
bvandeusen a90deab862 docs(plan): add M4a outbound scrobble worker implementation plan
12-task TDD-shaped plan covering: migration 0008 (users LB columns +
scrobble_queue table), sqlc query additions, pure ListenBrainz HTTP
client with typed errors and httptest coverage, threshold function,
MaybeEnqueue with idempotent insert + threshold gate, backoff schedule
1m→5m→30m→2h→6h, Worker with batched submit + retry/fail/auth handling,
playevents.Writer post-commit best-effort hooks, GET/PUT
/api/me/listenbrainz endpoints, /settings page in SvelteKit, full
verification sequence.
2026-04-28 08:34:26 -04:00
bvandeusen 791c6c7fd6 docs(spec): add M4a outbound scrobble worker design
First M4 sub-plan (Fable #345). Outbound scrobble worker with batching +
exponential-backoff retry. Per-user token, plaintext storage, pull-based
30s timer worker, patient backoff (1m → 5m → 30m → 2h → 6h, 5 attempts).
New scrobble_queue table; sent rows deleted (canonical record stays in
play_events.scrobbled_at). Minimal /settings page in M4a as scaffold for
M6's full Settings sub-plan to extend.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 08:21:43 -04:00
bvandeusen 00483539ad docs(plan): add M3 session similarity implementation plan
Eight-task TDD-shaped plan covering pure Similarity + ContextualMatchScore
functions, Score extension with ContextWeight=2.0, two new sqlc queries
(ListActiveContextualLikesForUser, GetCurrentSessionVectorForUser),
LoadCandidates signature change to accept currentVector, radio handler
wiring, end-to-end contextual ranking test, and final verification.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 19:57:18 -04:00
bvandeusen 20963847c2 docs(spec): add M3 session similarity + contextual_match_score design
Closes M3 — adds the `contextual_match_score` term to the scoring formula
via weighted-Jaccard similarity (tags 0.7, artists 0.3) over the user's
contextual_likes. Reads the current session vector from the most recent
open play_event (populated by sub-plan #2). Cold-start paths collapse to
zero contribution, preserving v1 behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 19:57:10 -04:00
bvandeusen 38beadf22e docs(plan): add M3 session vectors + contextual likes implementation plan
7 tasks: contextual_likes migration + sqlc, events.sql vector queries +
LikeTrack :execrows change, BuildSessionVector pure function, vector
capture in playevents.Writer, contextual_likes helpers + api handler
updates, subsonic handler updates, final verification.
2026-04-27 10:05:52 -04:00
bvandeusen 08eb1e3f8c docs(spec): add M3 session vectors + contextual likes design
Sub-plan #2 of 3 in M3 (Fable #341). Two write-path additions on top
of M2's events + likes infra:

1. play_started: compute session vector from prior 5 tracks in
   current play_session, write to play_events.session_vector_at_play.
2. like a track: if open play_event has populated vector, snapshot
   into contextual_likes with vector + session_id. Soft-delete on
   unlike (deleted_at column gates engine queries).

Includes new migration 0007_contextual_likes — the table was missing
from migration 0005 despite being mentioned in its comments. Adds
GIN index for sub-plan #3's similarity queries.

Backend-only slice; no UI changes.
2026-04-27 09:56:45 -04:00
bvandeusen b9937d6e3b docs(plan): add M3 weighted shuffle v1 implementation plan
7 tasks: pure score function, pure shuffle orchestrator, sqlc
LoadRadioCandidates query, DB-backed candidate loader,
RecommendationConfig + YAML, /api/radio handler rewrite, final
verification + branch finish.
2026-04-27 07:35:13 -04:00
bvandeusen f51b7c05ae docs(spec): add M3 weighted shuffle v1 design
Replaces the M2 stub /api/radio with real weighted-shuffle scoring:
LikeBoost + recency_decay - skip_ratio penalty + jitter, hard
suppression of last-hour plays. Wide candidate pool (whole library)
for v1; M4 adds similarity-based pool refinement. Pure scoring
function in internal/recommendation; live-DB candidate loader; HTTP
handler is a thin shim. No web changes — existing playRadio action
already calls /api/radio.

Sub-plan #1 of 3 in M3 (Fable #340). Session vectors and contextual
match score land in the next two sub-plans.
2026-04-27 01:35:51 -04:00
bvandeusen 1f08ee39bd fix(web): add likes/QueryClient mocks to route tests rendering LikeButton
Pages that render TrackRow/AlbumCard/ArtistRow/PlayerBar now indirectly
mount LikeButton, which calls useQueryClient(). Route tests need the
same mocks the component-level tests have.

- Added createLikedIdsQuery mock to 7 route test files
- Added useQueryClient mock to all affected route tests
- Added readable store import where needed
2026-04-26 17:18:39 -04:00
bvandeusen a3c05aeb34 docs(plan): add M2 likes implementation plan
9 tasks: schema + sqlc, native API, Subsonic star/unstar,
Subsonic getStarred/getStarred2, web likes.ts factory + mutations,
LikeButton component, wire LikeButton into existing components,
/library/liked page, final verification.
2026-04-26 15:52:42 -04:00
bvandeusen f91257a79e docs(spec): add M2 likes sub-plan design
Three new tables (general_likes, general_likes_albums,
general_likes_artists), seven native /api/likes/* endpoints, four
Subsonic handlers (star/unstar/getStarred/getStarred2), and web UI
heart buttons on TrackRow/AlbumCard/ArtistRow/PlayerBar plus a new
/library/liked page. Closes M2 (with the events foundation already
in main).

Liked state is computed client-side via O(1) Set lookups against a
single createLikedIdsQuery cache; server stays simple per request
(no joins added to entity reads). Optimistic updates with rollback.
2026-04-26 15:44:45 -04:00
bvandeusen 7df48ee9bc docs(plan): add M2 events sub-plan implementation plan
9 tasks: schema migration, events config, playsessions service,
playevents writer, /api/events handler, Subsonic scrobble integration,
web clientId + events dispatcher, layout wiring, final verification.
Tightly TDD-shaped with full code in every step.
2026-04-25 19:56:58 -04:00
bvandeusen f3b28db9d1 docs(spec): add M2 events sub-plan design
Specifies POST /api/events with start+end semantics, session service
(30-min rolling), play/skip classification (spec §6 boundaries),
sendBeacon + auto-close-on-next-start for abandoned plays, and
Subsonic /rest/scrobble integration that writes synthetic completed
plays so third-party clients feed the recommendation pipeline.

Schema migration ships the full M2 set (play_events, skip_events,
sessions) including M3-only nullable columns so M3 doesn't need a
follow-up migration. General likes deferred to a separate M2
sub-plan; this slice covers spec §13 step 5 only.
2026-04-25 19:39:16 -04:00
bvandeusen 8fd193e186 docs(plan): add web UI search implementation plan
12 tasks: server radio stub, web types, player-store enqueue/playRadio,
query helpers, TrackRow div+role refactor, AlbumCard +queue overlay,
SearchInput header component, SearchSkeleton, Shell wiring, /search
main page, three overflow pages, final verification.
2026-04-25 14:59:08 -04:00
bvandeusen 3588af83e5 docs(spec): add web UI search design
Specifies header search bar (debounced, URL-synced ?q=), /search page
with three sections of top-10 reusing library components, three
overflow routes (/search/artists, /search/albums, /search/tracks)
backed by createInfiniteQuery. Track click triggers playRadio with
seed via a new /api/radio stub that returns the seed alone (M4 fills
in real similarity-based selection later, response shape final). Adds
+queue button on TrackRow and AlbumCard via new enqueueTrack /
enqueueTracks player-store actions.
2026-04-25 14:49:01 -04:00
bvandeusen 7c19fe8a6a docs(plan): add web UI player implementation plan
8 TDD tasks: player rune store (2 tasks covering basic actions then
shuffle/repeat/audio-reporting), MediaSession glue, click-to-play
wiring in TrackRow + album page, PlayerBar component, Shell 3rd-row
integration, audio element wiring in root layout, and a final
verification + branch-finish step.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 08:57:52 -04:00
bvandeusen d6ea734152 docs(spec): add web UI player design
Queue-based playback on top of HTMLAudioElement: rune store + pure
action functions, single <audio> mounted in the root layout driven
via $effect, MediaSession metadata/action handlers, and a persistent
bottom bar with shuffle/repeat/volume. Non-goals: server-side listen
history (awaiting /api/events), cross-refresh persistence, keyboard
shortcuts.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 08:50:58 -04:00
bvandeusen f9b02c4051 docs(plan): Task 13 — match the type-safety fixes applied in Task 12
page.params.id is string|undefined in SvelteKit types; use ?? ''.
TanStack Query's error is typed as Error; cast through unknown to ApiError.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 20:13:20 -04:00
bvandeusen 25d44a180c docs(plan): update Tasks 12/13 to unwrap TanStack Query's Readable store
TanStack Query 5's Svelte adapter returns a Readable<QueryObserverResult>
store, not a plain object. Pages need to destructure with $-subscription:
  const queryStore = $derived(createXQuery(id));
  const query = $derived($queryStore);

Applied to artist + album detail page templates. Task 11 already
landed with this fix (and readable-wrapped mocks in test-utils).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 20:01:46 -04:00
bvandeusen 4cd67405c9 docs(plan): update useDelayed task to match .svelte.test.ts convention
Rune-using test files need the .svelte. infix so vite-plugin-svelte
processes them. Also adds the flushSync() after $effect.root in the
'source already true' test so fake-timer advances fire the scheduled
timeout.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 18:16:37 -04:00
bvandeusen ea17be9d45 docs(plan): add web UI library views implementation plan
14 TDD tasks: API types, formatDuration + covers helpers, useDelayed
rune, TanStack Query wrappers, 5 shared components (ArtistRow,
AlbumCard, TrackRow, LibrarySkeleton, ApiErrorBanner), 3 page routes
(/, /artists/:id, /albums/:id) with mocked-query integration tests,
and a final verification + branch-finish step.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 08:18:08 -04:00
bvandeusen 248f03495e docs(spec): add web UI library views design
Covers the three browse routes (/, /artists/:id, /albums/:id) on top
of the existing /api/artists, /api/artists/{id}, /api/albums/{id},
and /api/albums/{id}/cover endpoints. Uses TanStack Query's infinite
query for the artists list with Load-more pagination, reusable card
and row components, and a shared delayed-skeleton pattern for
cache-hit navigation feel.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 08:09:55 -04:00
bvandeusen fe410f14eb docs(plan): correct login test filename to avoid route walker collision
Renamed references from +page.test.ts to login.test.ts throughout,
matching the fix in 8896159. Keeps the plan accurate for re-runs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 06:10:29 -04:00
bvandeusen 12bf873f39 docs(plan): fix invalid toMatchObject<T> type-arg in auth plan
vitest's toMatchObject doesn't accept a type parameter. Removed the
annotation in Task 3 test code (and the unused ApiError import) so
re-runs of the plan don't reintroduce the type error fixed in 8a6a496.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 17:26:37 -04:00
bvandeusen 0b5c4a13bf docs(plan): add web UI auth implementation plan
12 TDD tasks: deps, api client, query client, auth store, 401
interceptor, Shell, placeholder pages, login page, root layout
wiring, and final verification + branch finish. Each task writes
the failing test first, then minimum implementation, then commits.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 15:47:23 -04:00
bvandeusen d53e7e2985 docs(spec): add web UI auth design
Covers the login flow, session store, fetch wrapper, persistent Shell,
and route guarding for the first frontend feature on top of the scaffold.
Commits to TanStack Query as the data layer going forward.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 15:40:07 -04:00
bvandeusen 8da51a1d13 docs: add Plan 3 web UI media endpoints implementation plan
Four-task TDD plan for /api/albums/{id}/cover and /api/tracks/{id}/stream:
scaffold media.go helpers (Task 1), cover handler (Task 2), stream handler
(Task 3), production Mount wiring + route-registration regression (Task 4).
Uses seedTrackWithFile helper to materialize real bytes on disk so Range /
If-Modified-Since tests hit http.ServeContent's real code path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 22:02:55 -04:00
bvandeusen 20c948a346 docs: clarify sidecar ordering and test helper extension in Plan 3 spec
Self-review fixups: make "first track" explicit (by disc/track order) and
replace the awkward "so existing tests continue to compile" phrasing with a
clearer description of why newLibraryRouter needs extending.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 21:52:07 -04:00
bvandeusen 7fcee05c1e docs: add Plan 3 web UI media endpoints spec
Covers /api/albums/{id}/cover and /api/tracks/{id}/stream — the byte-serving
endpoints Plan 2's cover_url / stream_url forward references point at.
Duplicates protocol-agnostic helpers locally per subsonic-is-legacy direction
rather than extracting a shared package.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 21:51:29 -04:00
bvandeusen 62e48642e2 docs(plan): add implementation plan for library reads + search
12 TDD-style tasks covering SQL additions, DTOs, conversion helpers,
per-endpoint handlers (tracks, albums, artist detail, artist list, search),
route wiring, and an end-to-end smoke + PR. Follows the approved design at
docs/superpowers/specs/2026-04-20-web-ui-library-reads-design.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 23:42:47 -04:00
bvandeusen 14aa9d7fba docs(spec): add design for library reads + search endpoints
Plan 2 of the web UI rollout. Covers GET /api/artists (paged, two sort
modes), artist/album/track detail, and unified search with enveloped
pagination and forward-reference stream/cover URLs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 23:33:51 -04:00
bvandeusen 9b0433ad9b docs: add web UI server auth foundation plan
10-task plan for the first implementation slice of the web UI
scaffold: sessions table, /api/auth/{login,logout}, /api/me, and
the RequireUser middleware (cookie + bearer). Stops short of the
library read endpoints and the SvelteKit project, which become
their own follow-up plans.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 19:30:24 -04:00
bvandeusen cb3df51f08 docs: add web UI scaffold design spec
First pass of the web UI design doc from the M1 close-out brainstorm.
Covers the stack (SvelteKit static + Go embed), auth model
(cookie + bearer from one endpoint), /api/* surface, shell layout
(sidebar + bottom player), first-cut feature scope, and how later
web UI work threads into M2–M5 alongside each backend milestone
instead of landing as a post-hoc M6.

Also ignores the local .superpowers/ brainstorming companion cache.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 19:18:04 -04:00
bvandeusen 419c0b6f4c docs: seed client spec on dev 2026-04-18 17:58:00 +00:00
bvandeusen 5861c146f7 docs: seed server spec on dev 2026-04-18 17:56:28 +00:00