Files
yellowjacket/.planning/phases/13-library-views-phantom-tracks/13-02-SUMMARY.md
T
yonlu 0a5877c177 docs(13-02): complete library filter UI & phantom resolution plan
- SUMMARY.md updated with checkpoint approval and all bugfix commits
- STATE.md updated with Phase 13 completion and new decisions
- ROADMAP.md plan checkboxes and milestone status updated
- REQUIREMENTS.md coverage updated: 31/31 v1.1 requirements complete
2026-03-16 13:03:35 -04:00

12 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
13-library-views-phantom-tracks 02 ui, api
lit
wails
library-filter
phantom-tracks
playlist-resolution
typescript
phase plan provides
13-library-views-phantom-tracks 01 Library-filtered sqlc queries and Go methods for tracks, albums, artists, genres, search
phase provides
12-library-crud-data-integrity Library CRUD API, orphan cleanup, phantom track metadata pre-population
phase provides
10-schema-migration Libraries table, library_id FK, phantom columns on playlist_tracks
Library filter dropdown UI in top bar with All Libraries default
All browse views (tracks, albums, artists, genres) respect active library filter
Search respects active library filter
Detail views (artist-details, genre-details, album tracks) respect library filter
Phantom track auto-resolution after library scan via ScanHooks callback
phantom_file_path column on playlist_tracks for phantom-to-track matching
multi-library-complete
v1.1-milestone
added patterns
Conditional Wails binding dispatch: store methods call ByLibrary variant when filter active, default variant otherwise
ScanHooks callback for cross-package phantom resolution (mirrors RemovalHooks/RescanHooks pattern)
Deferred event delegation retry in updated() for virtualizer race condition
phantom_file_path stored on removal for post-scan matching
created modified
frontend/src/components/library-filter/library-filter.ts
backend/playlist/playlist.go (ResolvePhantomTracksAfterScan)
frontend/src/store/library-store.ts
frontend/src/store/controllers/library-controller.ts
frontend/index.html
frontend/index.ts
frontend/src/components/cover-grid/cover-grid.ts
frontend/src/components/cover-grid/album-selection.ts
frontend/src/components/artists-view/artists-view.ts
frontend/src/components/genres-view/genres-view.ts
frontend/src/components/genre-details/genre-details.ts
frontend/src/components/track-list/track-list.ts
frontend/src/components/queue-panel/queue-panel.ts
backend/library/crud.go
backend/library/library.go
backend/database/database.go
backend/database/sql/schemas/playlist_tracks.sql
backend/app.go
Client-side search with filtered data source — rankTracks filters already-loaded tracks; no backend SearchTracksByLibrary call needed
Native select for library filter dropdown — compact, accessible, matches top bar height; no custom component overhead
getAlbumsByArtistNameCached returns null when filter active — avoids stale cross-library data; forces backend query
ScanHooks callback pattern for phantom resolution — mirrors RemovalHooks/RescanHooks for cross-package communication
phantom_file_path column on playlist_tracks — enables post-scan matching of phantoms to re-added tracks
M3U8-based phantom resolution — reads playlist files to match phantoms by position and file path
Conditional ByLibrary dispatch: check selectedLibraryId, call ByLibrary variant or unfiltered default
ScanHooks pattern: cross-package callbacks registered at app init to avoid circular imports
Deferred delegation guard: retry event delegation in updated() when virtualizer not ready on firstUpdated()
VIEW-01
VIEW-02
VIEW-03
VIEW-04
PLAY-01
PLAY-02
PLAY-03
45min 2026-03-16

Phase 13 Plan 02: Library Filter UI & Phantom Track Resolution Summary

Library filter dropdown in top bar with conditional ByLibrary queries across all views, plus ScanHooks-based phantom track auto-resolution after library re-scan

Performance

  • Duration: ~45 min (including checkpoint verification and bugfixes)
  • Started: 2026-03-16T13:32:45Z
  • Completed: 2026-03-16T14:18:00Z
  • Tasks: 2 (1 auto + 1 checkpoint:human-verify — APPROVED)
  • Files modified: 33 (across 4 code commits)

Accomplishments

  • Created <library-filter> dropdown component in top bar — shows "All Libraries" default plus all configured libraries
  • Wired all browse views (tracks, albums, artists, genres) and detail views (artist-details, genre-details, album track expansion) to respect active library filter via conditional ByLibrary Wails binding calls
  • Search inherits library filter automatically — client-side rankTracks operates on filtered track data
  • Playlists remain unfiltered (cross-library by design) — verified during checkpoint
  • Fixed pre-existing virtualizer event delegation race condition from Phase 14-03 optimization
  • Added phantom track auto-resolution: ScanHooks callback triggers M3U8-based phantom matching after library scan
  • Added phantom_file_path column (migration 7) to playlist_tracks for reliable phantom→track matching
  • All 7 Phase 13 requirements verified end-to-end: VIEW-01 through VIEW-04, PLAY-01 through PLAY-03

Task Commits

Each task was committed atomically:

  1. Task 1: Add library filter state + dropdown + wire all views - 42b8cf9 (feat)
  2. Task 2: Verify library filter, cross-library playlists, and phantom tracks - Checkpoint APPROVED

Bugfix Commits (during checkpoint verification)

  1. Fix: Defer virtualizer event delegation until element exists - f05d2bb (fix)
  2. Fix: Auto-resolve phantom playlist tracks after library scan - 93262b9 (fix)
  3. Fix: Resolve phantom playlist tracks using M3U8 paths after scan - 9f595b7 (fix)

Plan metadata: 70e3814 (docs: complete library filter UI plan)

Files Created/Modified

Frontend — Library filter UI (Task 1)

  • frontend/src/components/library-filter/library-filter.ts — NEW: Library filter dropdown component (native select, design tokens)
  • frontend/src/store/library-store.ts — selectedLibraryId state, conditional ByLibrary dispatch, getLibraries(), invalidation
  • frontend/src/store/controllers/library-controller.ts — Pass-through for selectedLibraryId, setSelectedLibrary, getLibraries
  • frontend/index.html — <library-filter> element added to top bar header
  • frontend/index.ts — Import for library-filter component
  • frontend/src/components/cover-grid/cover-grid.ts — GetAlbumTracksByLibrary for album expansion dropdown
  • frontend/src/components/cover-grid/album-selection.ts — Library-aware fetchAlbumTracks helper
  • frontend/src/components/artists-view/artists-view.ts — GetAlbumsByArtistByLibrary + GetAlbumTracksByLibrary
  • frontend/src/components/genres-view/genres-view.ts — GetTracksByGenreByLibrary for context menu
  • frontend/src/components/genre-details/genre-details.ts — GetTracksByGenreByLibrary for track loading
  • frontend/wailsjs/go/library/Library.d.ts — Regenerated with ByLibrary bindings
  • frontend/wailsjs/go/library/Library.js — Regenerated with ByLibrary bindings

Frontend — Virtualizer race condition fix

  • frontend/src/components/track-list/track-list.ts — Deferred event delegation with guard flag in updated()
  • frontend/src/components/queue-panel/queue-panel.ts — Deferred event delegation with guard flag in updated()

Backend — Phantom track auto-resolution

  • backend/database/database.go — Migration 7: phantom_file_path column on playlist_tracks
  • backend/database/sql/schemas/playlist_tracks.sql — phantom_file_path column definition
  • backend/database/sql/sqlcgen/models.go — Generated model with PhantomFilePath field
  • backend/database/sql/sqlcgen/playlists.sql.go — Generated query updates
  • backend/library/crud.go — Store file_path as phantom_file_path on RemoveLibrary
  • backend/library/library.go — ScanHooks registration, phantom resolution trigger after scan
  • backend/playlist/playlist.go — ResolvePhantomTracksAfterScan: M3U8-based phantom matching
  • backend/app.go — ScanHooks wiring at app initialization
  • frontend/wailsjs/go/models.ts — Updated generated models
  • frontend/wailsjs/go/playlist/Service.d.ts — Updated generated bindings
  • frontend/wailsjs/go/playlist/Service.js — Updated generated bindings

Decisions Made

  • Client-side search filtering: rankTracks already operates on filtered track data from library store — no separate backend SearchTracksByLibrary call needed
  • Native <select> for library filter: Compact, accessible, 32px height matching search bar, no custom dropdown overhead
  • getAlbumsByArtistNameCached returns null when filter active: Forces backend query to avoid showing stale cross-library cached albums
  • ScanHooks callback for phantom resolution: Mirrors established RemovalHooks/RescanHooks pattern — avoids circular dependency between library and playlist packages
  • M3U8-based phantom resolution over SQL-only: Reads playlist files to match phantoms by both position and phantom_file_path — handles pre-existing phantoms (match by position) and new ones (match by stored path)
  • phantom_file_path column: Stored at removal time so post-scan resolution can match even when M3U8 position changes

Deviations from Plan

Auto-fixed Issues

1. [Rule 3 - Blocking] Wails bindings not yet generated for ByLibrary methods

  • Found during: Task 1 (start)
  • Issue: Plan 13-01 added Go methods but Wails bindings weren't regenerated
  • Fix: Ran wails generate module before implementation
  • Files modified: frontend/wailsjs/go/library/Library.d.ts, Library.js
  • Verification: All ByLibrary imports resolve correctly
  • Committed in: 42b8cf9

2. [Rule 1 - Bug] Info type uses lowercase property names

  • Found during: Task 1 (TypeScript typecheck)
  • Issue: library-filter.ts used lib.ID and lib.Name but Wails-generated Info type uses lib.id and lib.name
  • Fix: Changed to lowercase property access
  • Files modified: frontend/src/components/library-filter/library-filter.ts
  • Verification: tsc --noEmit passes cleanly
  • Committed in: 42b8cf9

3. [Rule 1 - Bug] Virtualizer event delegation race condition

  • Found during: Checkpoint verification (Task 2)
  • Issue: Pre-existing race from Phase 14-03 — event delegation on virtualizer failed when element wasn't rendered yet on firstUpdated()
  • Fix: Added retry in updated() with guard flag; delegation happens once virtualizer exists
  • Files modified: frontend/src/components/track-list/track-list.ts, frontend/src/components/queue-panel/queue-panel.ts
  • Verification: Track list and queue panel click/context-menu events work reliably on app launch
  • Committed in: f05d2bb

4. [Rule 2 - Missing Critical] Phantom track auto-resolution after library scan

  • Found during: Checkpoint verification (Task 2)
  • Issue: Phantom tracks not auto-resolved when library re-added and scanned — users would need to manually resolve each one
  • Fix: Added phantom_file_path column (migration 7), store file_path on removal, ResolvePhantomTracksAfterScan via ScanHooks callback with M3U8 path comparison
  • Files modified: backend/database/database.go, backend/database/sql/schemas/playlist_tracks.sql, backend/library/crud.go, backend/library/library.go, backend/playlist/playlist.go, backend/app.go
  • Verification: Remove library → re-add → scan → phantoms automatically resolve to real tracks
  • Committed in: 93262b9, 9f595b7

Total deviations: 4 auto-fixed (2 bugs, 1 blocking, 1 missing critical) Impact on plan: All fixes essential for correctness. Virtualizer fix resolved pre-existing race condition exposed by multi-library testing. Phantom auto-resolution is critical UX — users should not need to manually fix playlist tracks after re-adding a library.

Issues Encountered

None beyond the deviations documented above.

User Setup Required

None - no external service configuration required.

Next Phase Readiness

  • Phase 13 complete — All 7 requirements verified (VIEW-01 through VIEW-04, PLAY-01 through PLAY-03)
  • v1.1 Multi-Library Support milestone complete — Phases 9-13 all done
  • Phase 14 (Performance Optimization) already complete — executed in parallel during v1.1 development
  • All browse views, search, playlists, and phantom tracks working correctly in multi-library context

Self-Check: PASSED


Phase: 13-library-views-phantom-tracks Completed: 2026-03-16