From c158d6825ca42500b1255921bd50921a45ecbb45 Mon Sep 17 00:00:00 2001 From: Caleb Allen Date: Mon, 16 Mar 2026 16:44:11 -0400 Subject: [PATCH] docs: create milestone v1.2 roadmap (5 phases) --- .planning/REQUIREMENTS.md | 46 +++++----- .planning/ROADMAP.md | 172 ++++++++++++++++------------------- .planning/STATE.md | 185 ++++++++++++-------------------------- 3 files changed, 155 insertions(+), 248 deletions(-) diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 9233a01..32ec5f9 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -91,32 +91,32 @@ Which phases cover which requirements. Updated during roadmap creation. | Requirement | Phase | Status | |-------------|-------|--------| -| SCHEMA-01 | — | Pending | -| SCHEMA-02 | — | Pending | -| WRITE-01 | — | Pending | -| WRITE-02 | — | Pending | -| WRITE-03 | — | Pending | -| WRITE-04 | — | Pending | -| WRITE-05 | — | Pending | -| WRITE-06 | — | Pending | -| SYNC-01 | — | Pending | -| SYNC-02 | — | Pending | -| SYNC-03 | — | Pending | -| SYNC-04 | — | Pending | -| EDIT-01 | — | Pending | -| EDIT-02 | — | Pending | -| EDIT-03 | — | Pending | -| EDIT-04 | — | Pending | -| BATCH-01 | — | Pending | -| BATCH-02 | — | Pending | -| BATCH-03 | — | Pending | -| BATCH-04 | — | Pending | +| SCHEMA-01 | Phase 15 | Pending | +| SCHEMA-02 | Phase 15 | Pending | +| WRITE-01 | Phase 16 | Pending | +| WRITE-02 | Phase 16 | Pending | +| WRITE-03 | Phase 19 | Pending | +| WRITE-04 | Phase 16 | Pending | +| WRITE-05 | Phase 15 | Pending | +| WRITE-06 | Phase 16 | Pending | +| SYNC-01 | Phase 16 | Pending | +| SYNC-02 | Phase 16 | Pending | +| SYNC-03 | Phase 16 | Pending | +| SYNC-04 | Phase 16 | Pending | +| EDIT-01 | Phase 17 | Pending | +| EDIT-02 | Phase 17 | Pending | +| EDIT-03 | Phase 17 | Pending | +| EDIT-04 | Phase 17 | Pending | +| BATCH-01 | Phase 18 | Pending | +| BATCH-02 | Phase 18 | Pending | +| BATCH-03 | Phase 18 | Pending | +| BATCH-04 | Phase 18 | Pending | **Coverage:** - v1.2 requirements: 20 total -- Mapped to phases: 0 -- Unmapped: 20 +- Mapped to phases: 20 +- Unmapped: 0 ✓ --- *Requirements defined: 2026-03-16* -*Last updated: 2026-03-16 after initial definition* +*Last updated: 2026-03-16 — traceability updated with phase mappings (Phases 15-19)* diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 355d7bb..cc4d6b0 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -1,14 +1,14 @@ # Roadmap: YellowJacket **Created:** 2026-02-27 -**Last updated:** 2026-03-08 -**Current milestone:** v1.1 Multi-Library Support +**Last updated:** 2026-03-16 +**Current milestone:** v1.2 Tag Editing ## Milestones - ✅ **v1.0 Consolidation** — Phases 1-8 (shipped 2026-03-05) — [archive](milestones/v1.0-ROADMAP.md) -- ✅ **v1.1 Multi-Library Support** — Phases 9-13 complete (shipped 2026-03-16) -- ✅ **Performance Optimization** — Phase 14 complete (shipped 2026-03-15) +- ✅ **v1.1 Multi-Library Support** — Phases 9-14 (shipped 2026-03-16) — [archive](milestones/v1.1-ROADMAP.md) +- 🔨 **v1.2 Tag Editing** — Phases 15-19 ## Phases @@ -26,109 +26,82 @@ -### v1.1 Multi-Library Support (Phases 9-13) +
+✅ v1.1 Multi-Library Support (Phases 9-14) — SHIPPED 2026-03-16 -- [x] **Phase 9: Scan Cancellation & Keyboard Shortcuts** — Cancellable library scans and configurable keyboard shortcuts -- [x] **Phase 10: Schema & Migration** — Libraries table, library_id FK, playlist_tracks phantom rebuild, config migration (completed 2026-03-09) -- [x] **Phase 11: Per-Library Scan Pipeline** — Scan pipeline refactored for per-library scanning with sequential coordination (completed 2026-03-09) -- [x] **Phase 12: Library CRUD & Data Integrity** — Library management API, orphan cleanup, queue/playlist lifecycle, library manager UI (completed 2026-03-15) -- [x] **Phase 13: Library Views & Phantom Tracks** — Filtered presentation across all views, search, browse, and phantom track display (completed 2026-03-16) +- [x] Phase 9: Scan Cancellation & Keyboard Shortcuts (5/5 plans) — completed 2026-03-07 +- [x] Phase 10: Schema & Migration (2/2 plans) — completed 2026-03-09 +- [x] Phase 11: Per-Library Scan Pipeline (3/3 plans) — completed 2026-03-09 +- [x] Phase 12: Library CRUD & Data Integrity (2/2 plans) — completed 2026-03-15 +- [x] Phase 13: Library Views & Phantom Tracks (2/2 plans) — completed 2026-03-16 +- [x] Phase 14: Performance Optimization (4/4 plans) — completed 2026-03-15 + +
+ +### v1.2 Tag Editing (Phases 15-19) + +- [ ] **Phase 15: Schema Migration & Write Safety** — FTS5 contentless_delete migration and atomic file write utility +- [ ] **Phase 16: Tag Writing & Database Sync** — Format-specific tag writers (MP3, FLAC, cover art) with inline DB + FTS5 update pipeline +- [ ] **Phase 17: Single Track Edit** — End-to-end single track editing: UI → file write → DB sync → view refresh +- [ ] **Phase 18: Batch Edit** — Multi-select batch editing with three-state field model, progress, and batch cover art +- [ ] **Phase 19: OGG Vorbis Tag Writing** — Custom OGG page rewriter for Vorbis Comment tag writing (stretch) ## Phase Details -### Phase 9: Scan Cancellation & Keyboard Shortcuts -**Goal:** Users can control library scans (cancel/pause/resume) and operate the entire app via keyboard -**Depends on:** Nothing (builds on v1.0 foundation) -**Requirements:** SCAN-01, SCAN-02, SCAN-03, KEY-01, KEY-02, KEY-03, KEY-04, KEY-05 +### Phase 15: Schema Migration & Write Safety +**Goal:** The database and file system infrastructure supports safe, reversible tag editing — FTS5 rows can be deleted/updated and file writes never corrupt audio files +**Depends on:** Nothing (builds on v1.1 foundation) +**Requirements:** SCHEMA-01, SCHEMA-02, WRITE-05 **Success Criteria** (what must be TRUE): - 1. User can click a cancel button during a library scan and the scan stops within seconds — no database corruption, no orphaned tracks - 2. User can pause a running scan and resume it later without re-processing files that were already scanned - 3. Default keyboard shortcuts work immediately after install — play/pause, next/prev, volume up/down, search focus, queue toggle, shuffle, repeat all respond to keys - 4. User can open a settings UI, rebind any shortcut to a different key, and the new binding takes effect immediately — conflicts are warned about before saving - 5. Keyboard shortcuts are context-aware — typing in a search box doesn't trigger player shortcuts (except Escape to blur) -**Plans:** 5 plans -Plans: -- [x] 09-01-PLAN.md — Backend scan control (cancel/pause/resume methods, events, metrics) -- [x] 09-02-PLAN.md — Backend shortcuts config + frontend keyboard shortcut service -- [x] 09-03-PLAN.md — Frontend scan control UI (buttons, cancel dialog) -- [x] 09-04-PLAN.md — Frontend shortcut settings UI (record-style capture, conflict detection) -- [x] 09-05-PLAN.md — Integration verification checkpoint + 1. FTS5 search_index uses `contentless_delete=1` — deleting or updating a track's metadata in the DB correctly removes the old FTS5 entry without stale ghost results appearing in search + 2. Existing search functionality is unaffected — all current queries, ranking, and library-filtered search continue to work identically after migration + 3. The atomic write utility writes to a temp file in the same directory as the target, then renames — if the process crashes mid-write, the original file is intact and the temp file is cleaned up on next startup + 4. Unit tests verify atomic write behavior: successful write, crash simulation (temp file left behind), and cross-directory rejection +**Plans:** TBD -### Phase 10: Schema & Migration -**Goal:** The database supports multiple libraries and phantom tracks — existing users upgrade seamlessly -**Depends on:** Phase 9 (builds on existing schema and scan infrastructure) -**Requirements:** DATA-01, DATA-04, LIB-04, LIB-05, LSCAN-05 +### Phase 16: Tag Writing & Database Sync +**Goal:** The backend can write metadata tags and cover art to MP3 and FLAC files, then synchronize all changes to the database and search index in a single atomic operation +**Depends on:** Phase 15 (requires atomic write utility and FTS5 contentless_delete) +**Requirements:** WRITE-01, WRITE-02, WRITE-04, WRITE-06, SYNC-01, SYNC-02, SYNC-03, SYNC-04 **Success Criteria** (what must be TRUE): - 1. A fresh install creates a `libraries` table and `audio_files.library_id` FK — new audio files are always associated with a library - 2. An existing user's database is migrated on first launch: their single directory becomes a named library, all existing audio_files get that library_id, and everything works without any user action - 3. The `playlist_tracks` table supports nullable `audio_file_id` with phantom metadata columns — the schema is ready for phantom track preservation - 4. All migration operations complete atomically — a crash mid-migration leaves the database unchanged (not half-migrated) -**Plans:** 2/2 plans complete -Plans: -- [x] 10-01-PLAN.md — Schema definitions + Migration 6 (libraries table, library_id FK, phantom columns, track_metadata VIEW, backup, TOML migration) -- [x] 10-02-PLAN.md — sqlc queries for libraries + updated playlist phantom queries + migration integration tests + 1. A Go function can accept a track ID and a set of changed metadata fields, write those tags to an MP3 file (ID3v2), and the tags are readable back by the existing metadata reader — round-trip correctness verified by unit tests with real audio files + 2. The same function works for FLAC files (Vorbis Comments) — including files with existing padding blocks and multiple metadata blocks + 3. Cover art images (JPEG/PNG) can be embedded in both MP3 and FLAC files — the embedded image is readable back and the existing cover art pipeline (extraction, thumbnails) works with the newly embedded art + 4. After a tag write, the database reflects the new metadata within the same operation: artist/album/genre entities are created or relinked (never mutated in-place), orphaned entities with zero remaining references are cleaned up, and the FTS5 index is updated — no library rescan needed + 5. If the currently-playing track is being edited, playback is stopped before the file write begins — the user does not experience a crash or corrupted audio stream +**Plans:** TBD -### Phase 11: Per-Library Scan Pipeline -**Goal:** Users can scan individual libraries independently with proper sequential coordination -**Depends on:** Phase 10 (requires libraries table and library_id FK) -**Requirements:** LSCAN-01, LSCAN-02, LSCAN-03, LSCAN-04 +### Phase 17: Single Track Edit +**Goal:** Users can edit any track's metadata and cover art from within the app and see changes reflected everywhere immediately +**Depends on:** Phase 16 (requires tag writers and DB sync pipeline) +**Requirements:** EDIT-01, EDIT-02, EDIT-03, EDIT-04 **Success Criteria** (what must be TRUE): - 1. User can trigger a scan for a specific library and only that library's directory is scanned — other libraries are untouched - 2. Only one library scans at a time — requesting a second scan while one is running either queues it or is rejected with clear feedback - 3. Scan progress UI identifies which library is currently being scanned (library name visible in progress indicator) - 4. Existing cancel and pause/resume controls work correctly for per-library scans — cancelling one library's scan doesn't affect others -**Plans:** 3/3 plans complete -Plans: -- [x] 11-01-PLAN.md — Backend scan queue coordinator, per-library scan methods, CreateAudioFile with library_id -- [x] 11-02-PLAN.md — Frontend progress UI with library name, cancel scope modal, Scan All button -- [x] 11-03-PLAN.md — App startup auto-scan wiring, legacy single-directory cleanup + 1. User can right-click any track (in track list, album detail, queue, or playlist) and open a tag editor dialog — the editor is accessible from every place tracks appear + 2. The editor displays all 8 editable fields (title, artist, album, genre, year, track number, disc number, composer) pre-populated with the track's current values — empty fields show as empty, not "Unknown" + 3. The editor displays the track's current cover art (or a placeholder if none) with a button to select a replacement image file from disk + 4. Clicking "Save" writes the changes to the audio file, updates the database and search index, and refreshes all visible views (track list, album view, artist view, genre view, queue, now-playing bar) — the user sees the new metadata everywhere without restarting or rescanning +**Plans:** TBD -### Phase 12: Library CRUD & Data Integrity -**Goal:** Users can add, rename, and remove libraries through the UI with correct data lifecycle management -**Depends on:** Phase 11 (requires per-library scanning for add-then-scan workflow) -**Requirements:** LIB-01, LIB-02, LIB-03, LIB-06, DATA-02, DATA-03, PLAY-04 +### Phase 18: Batch Edit +**Goal:** Users can efficiently edit shared metadata across multiple tracks at once with clear visual feedback and safe defaults +**Depends on:** Phase 17 (requires single-track edit pipeline as foundation) +**Requirements:** BATCH-01, BATCH-02, BATCH-03, BATCH-04 **Success Criteria** (what must be TRUE): - 1. User can add a new library via folder picker, give it a name, and trigger a scan — new tracks appear in the library - 2. User can rename a library's display name and the change reflects everywhere immediately - 3. User can remove a library — its tracks are deleted, shared artists/albums/genres used only by that library are cleaned up, but entities shared with other libraries survive intact - 4. Removing a library cleans up FTS5 search index entries for that library's tracks (no stale search results) - 5. Queue tracks from a removed library are cascade-deleted; the queue continues playing from the next valid track -**Plans:** 2/2 plans complete -Plans: -- [x] 12-01-PLAN.md — Backend CRUD API + orphan cleanup + queue compaction + events -- [x] 12-02-PLAN.md — Frontend library management UI in settings + sidebar cleanup + 1. User can select multiple tracks (via multi-select in track list or album detail) and open a batch editor — the batch editor is accessible from the same context menu as single-track edit + 2. Each field in the batch editor shows one of three states: "keep original" (mixed values, no change), "set to value" (apply this value to all selected tracks), or "clear field" (remove this value from all) — the user can see which fields differ across the selection and choose per-field what to do + 3. For batch operations on 10+ tracks, a progress indicator shows how many tracks have been processed — the user is never left staring at a frozen UI wondering if the operation is working + 4. User can set cover art for all selected tracks at once — the same image is embedded in every selected file +**Plans:** TBD -### Phase 13: Library Views & Phantom Tracks -**Goal:** Users experience a unified multi-library presentation with optional filtering and graceful playlist preservation -**Depends on:** Phase 12 (requires library CRUD and data integrity for full integration) -**Requirements:** VIEW-01, VIEW-02, VIEW-03, VIEW-04, PLAY-01, PLAY-02, PLAY-03 +### Phase 19: OGG Vorbis Tag Writing +**Goal:** Users can edit tags on OGG Vorbis files with the same experience as MP3 and FLAC — completing full format coverage +**Depends on:** Phase 16 (requires tag writer interface and DB sync pipeline) +**Requirements:** WRITE-03 **Success Criteria** (what must be TRUE): - 1. The default track list shows tracks from all libraries merged — the user sees their complete collection as one unified view - 2. User can select a specific library from a filter control and all views (tracks, albums, artists, genres) show only that library's content - 3. Search results respect the active library filter — searching with a library selected returns only matches from that library; with "All Libraries" selected, searches everything - 4. Playlists can contain tracks from multiple libraries — adding tracks from different libraries to the same playlist works naturally - 5. When a library is removed, its tracks in playlists become phantom entries — visually distinguished (greyed out / icon) with preserved title, artist, album metadata instead of disappearing -**Plans:** 2/2 plans complete -Plans: -- [x] 13-01-PLAN.md — Backend library-filtered sqlc queries + Go methods + FTS search -- [x] 13-02-PLAN.md — Frontend library filter store + dropdown UI + all view/search wiring + verification - -### Phase 14: Performance Optimization -**Goal:** Scrolling, navigation, and rendering are as smooth and fast as possible — scrolling feels like a native animation, navigation is instant, no unnecessary re-renders -**Depends on:** Nothing (cross-cutting, can execute in parallel with v1.1 phases) -**Requirements:** PERF-SCROLL-01, PERF-SCROLL-02, PERF-SCROLL-03, PERF-NAV-01, PERF-NAV-02, PERF-RENDER-01, PERF-RENDER-02, PERF-DIAG-01 -**Success Criteria** (what must be TRUE): - 1. Scrolling in all views (tracks, albums, artists, genres, queue, playlists) is smooth at 60fps — no jank, no stuttering, no blank areas - 2. Navigating between primary views (tracks, albums, artists, genres, playlists, settings) is near-instant — no component destruction/recreation, scroll positions preserved - 3. Render hot paths (renderTrackRow, renderTrackItem) create zero new closures per frame — all event handling uses delegation - 4. Store notifications are batched (queueMicrotask) and components only re-render when their relevant data changes - 5. A profiling guide documents how to diagnose performance issues using pprof (backend) and DevTools (frontend) -**Plans:** 4/4 plans complete -Plans: -- [x] 14-01-PLAN.md — CSS containment + GPU layer promotion on all scroll containers -- [x] 14-02-PLAN.md — View caching navigation system (replace innerHTML destruction) -- [x] 14-03-PLAN.md — Render hot-path optimization (closure elimination, store granularity) -- [x] 14-04-PLAN.md — Scroll event optimization, profiling guide, performance verification checkpoint + 1. A Go function can write Vorbis Comment metadata tags to OGG Vorbis files using a custom OGG page rewriter — the file remains a valid OGG stream after writing (playable by the existing player and by external players) + 2. Tag writes to OGG files use the same atomic write-to-temp-then-rename pattern as MP3/FLAC — no corruption risk + 3. OGG tag editing is seamlessly integrated into the single-track and batch edit UIs — the user doesn't need to know or care what format a file is; the editor just works +**Plans:** TBD ## Progress @@ -146,9 +119,14 @@ Plans: | 10. Schema & Migration | v1.1 | 2/2 | Complete | 2026-03-09 | | 11. Per-Library Scan Pipeline | v1.1 | 3/3 | Complete | 2026-03-09 | | 12. Library CRUD & Data Integrity | v1.1 | 2/2 | Complete | 2026-03-15 | -| 13. Library Views & Phantom Tracks | v1.1 | Complete | 2026-03-16 | 2026-03-16 | -| 14. Performance Optimization | Perf | 4/4 | Complete | 2026-03-15 | +| 13. Library Views & Phantom Tracks | v1.1 | 2/2 | Complete | 2026-03-16 | +| 14. Performance Optimization | v1.1 | 4/4 | Complete | 2026-03-15 | +| 15. Schema Migration & Write Safety | v1.2 | 0/? | Not started | - | +| 16. Tag Writing & Database Sync | v1.2 | 0/? | Not started | - | +| 17. Single Track Edit | v1.2 | 0/? | Not started | - | +| 18. Batch Edit | v1.2 | 0/? | Not started | - | +| 19. OGG Vorbis Tag Writing | v1.2 | 0/? | Not started | - | --- *Roadmap created: 2026-02-27* -*Last updated: 2026-03-16 — v1.1 milestone complete (Phases 9-14 all done)* +*Last updated: 2026-03-16 — v1.2 Tag Editing milestone roadmap created (Phases 15-19)* diff --git a/.planning/STATE.md b/.planning/STATE.md index 6d8c36d..68d5693 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -2,13 +2,13 @@ gsd_state_version: 1.0 milestone: v1.2 milestone_name: Tag Editing -status: defining_requirements -last_updated: "2026-03-16T20:03:38.282Z" +status: roadmap_created +last_updated: "2026-03-16T21:00:00.000Z" progress: - total_phases: 6 - completed_phases: 6 - total_plans: 18 - completed_plans: 18 + total_phases: 5 + completed_phases: 0 + total_plans: 0 + completed_plans: 0 --- # YellowJacket — Project State @@ -22,53 +22,48 @@ See: .planning/PROJECT.md (updated 2026-03-16) ## Current Position -Phase: Not started (defining requirements) +Phase: Phase 15 — Schema Migration & Write Safety (not started) Plan: — -Status: Defining requirements for v1.2 Tag Editing -Last activity: 2026-03-16 — Milestone v1.2 started +Status: Roadmap created, ready for phase planning +Last activity: 2026-03-16 — v1.2 roadmap created (5 phases, 20 requirements mapped) ### Phase Overview | Phase | Status | |-------|--------| -| 9. Scan Cancellation & Keyboard Shortcuts | Complete (5/5 plans) ✅ | -| 10. Schema & Migration | Complete (2/2 plans) ✅ | -| 11. Per-Library Scan Pipeline | Complete (3/3 plans) ✅ | -| 12. Library CRUD & Data Integrity | Complete (2/2 plans) ✅ | -| 13. Library Views & Phantom Tracks | Complete (2/2 plans) ✅ | -| 14. Performance Optimization | Complete (4/4 plans) ✅ | +| 15. Schema Migration & Write Safety | Not started | +| 16. Tag Writing & Database Sync | Not started | +| 17. Single Track Edit | Not started | +| 18. Batch Edit | Not started | +| 19. OGG Vorbis Tag Writing | Not started | + +### v1.2 Requirement Coverage + +| Category | Requirements | Phase(s) | +|----------|-------------|----------| +| Schema & Safety | SCHEMA-01, SCHEMA-02 | Phase 15 | +| Tag Writing | WRITE-01, WRITE-02 | Phase 16 | +| Tag Writing | WRITE-03 | Phase 19 | +| Tag Writing | WRITE-04, WRITE-05, WRITE-06 | Phase 15, 16 | +| Database Sync | SYNC-01, SYNC-02, SYNC-03, SYNC-04 | Phase 16 | +| Single Track Edit | EDIT-01, EDIT-02, EDIT-03, EDIT-04 | Phase 17 | +| Batch Edit | BATCH-01, BATCH-02, BATCH-03, BATCH-04 | Phase 18 | ## Performance Metrics **v1.0 baseline:** 8 phases, 17 plans, 34 tasks in 6 days (107 commits) -**v1.1 scope:** Phase 9 complete (5 plans), Phases 10-13 pending (20 requirements across 4 phases) +**v1.1 baseline:** 6 phases, 18 plans in 10 days (~85 commits) +**v1.2 scope:** 5 phases, 20 requirements | Phase | Plan | Duration | Tasks | Files | |-------|------|----------|-------|-------| -| 09-01 | scan control backend | 16 min | 2 | 5 | -| 09-02 | keyboard shortcuts config & service | 35 min | 2 | 12 | -| 09-04 | keyboard shortcuts settings UI | 5 min | 2 | 2 | -| 09-03 | scan control UI | 2 min | 1 | 3 | -| 09-05 | integration testing & verification | 3 min | 2 | 1 | -| Phase 10-01 P01 | 11 min | 2 tasks | 10 files | -| Phase 10-02 P02 | 5 min | 2 tasks | 9 files | -| Phase 11-01 P01 | 7 min | 2 tasks | 11 files | -| Phase 11-03 P03 | 10 min | 1 task | 3 files | -| Phase 11-02 P02 | 4 min | 2 tasks | 2 files | -| Phase 12-01 P01 | 6 min | 2 tasks | 6 files | -| Phase 14-02 P02 | 1 min | 1 task | 1 file | -| Phase 14-03 P03 | 4 min | 2 tasks | 5 files | -| Phase 14-01 P01 | 3 min | 2 tasks | 7 files | -| Phase 14-04 P04 | 2 min | 2 tasks | 3 files | -| Phase 12-02 P02 | 38 min | 3 tasks | 19 files | -| Phase 13 P01 | 5 min | 2 tasks | 10 files | -| Phase 13 P02 | 9 min | 1 task | 12 files | +| (v1.2 plans not yet started) | | | | | ## Accumulated Context ### Key Decisions -Decisions from v1.0 are archived in PROJECT.md Key Decisions table. Key patterns to carry forward: +Decisions from v1.0 and v1.1 are archived in PROJECT.md Key Decisions table. Key patterns to carry forward: - Mutex-protected setter pattern (lock → write → release → callbacks) - SAFETY comment convention for hand-crafted SQL @@ -76,117 +71,51 @@ Decisions from v1.0 are archived in PROJECT.md Key Decisions table. Key patterns - Design tokens via `:host` scoped CSS custom properties - queueMicrotask coalescing for store notifications - `.renderItem` + `.keyFunction` (not `repeat()` children) for lit-virtualizer +- Upsert-and-relink for shared entities (never mutate shared artist/album/genre rows) +- ScanHooks/RemovalHooks/RescanHooks callback patterns for cross-package coordination -### v1.1 Roadmap Decisions +### v1.2 Roadmap Decisions | Decision | Rationale | |----------|-----------| -| Phase 9 = Scan Cancel + Shortcuts | Quick wins, validate context cancellation and config extension patterns | -| v1.1 restructured for multi-library | Tag editing, smart playlists, gapless, MusicBrainz, layout, plugins deferred to future milestones | -| Hybrid model (library_id on audio_files only) | Physical files belong to libraries; logical entities (artists, albums, genres) are global/shared | -| Libraries in DB, not TOML | CRUD through UI shouldn't require TOML manipulation; DB is source of truth | -| SET NULL for playlist_tracks FK | Phantom tracks preserve playlist structure when library removed | -| CASCADE for queue_tracks FK | Queue is ephemeral, not user-curated like playlists | -| Sequential scanning | SQLite single-writer makes parallel scans pointless | -| Backend filtering, not frontend | Don't load 150K tracks when viewing one library | -| 4 multi-library phases (10-13) | Natural delivery boundaries: schema → scan → CRUD → views, each phase delivers verifiable capability | - -### Phase 10 Decisions - -| Decision | Rationale | -|----------|-----------| -| Underscore prefix `_libraries.sql` for schema ordering | Go embed.FS ReadDir sorts alphabetically; `_` < `a` ensures libraries table created before audio_files FK | -| Sentinel library id=0 in test DB | Existing tests use DEFAULT library_id=0; sentinel row satisfies FK without modifying every test | -| TOML cleanup via generic map[string]any | Preserves all config sections when removing only DirectoryPath; no dependency on full Config struct | -| sql.NullInt64 for nullable playlist_tracks.audio_file_id | Phantom tracks have NULL audio_file_id; generated sqlc code requires sql.Null types | -| COALESCE fallback chain: live → phantom → empty | Playlist queries use 3-level COALESCE so callers always get usable string values | -| is_phantom computed column via CASE WHEN | Eliminates null-checking logic in callers; simple int64 boolean (0/1) | - -### Phase 11 Decisions - -| Decision | Rationale | -|----------|-----------| -| Library ID threaded via importResult | Explicit data flow through scan pipeline, avoids mutating shared Library struct state | -| scanInternal returns *ScanMetrics only | Called from goroutine in scan queue, error return impractical; errors logged + accumulated in Warnings | -| Auto worker count per library path | Each library may be on different storage (SSD/HDD), auto-detect per scan | -| Backward-compatible Scan() wrapper | Keeps handleConfigUpdate and FullRescan working without changes | -| Queue-aware cancel dialog scope choice | Two-option "Cancel This Library / Cancel All" only when queuedCount > 0; single-scan keeps existing pattern | -| handleScanComplete defers reset when queue draining | Prevents premature scanning=false before next library starts | -| FullRescan uses first library from GetAllLibraries | Per-library rescan deferred to Phase 12; preserves backward compatibility for config-page "Rescan" button | -| LibraryConfigChanged handler removed entirely | Multi-library model uses CRUD API (Phase 12), not event-driven config updates | -| Scan() wrapper deleted | Only callers were handleConfigUpdate (deleted) and FullRescan (updated to scanInternal) | - -### Phase 12 Decisions - -| Decision | Rationale | -|----------|-----------| -| Application-level name uniqueness check (iterate GetAllLibraries) | Avoids migration 7; rename is infrequent, check is simple | -| RemovalHooks callback struct (StopPlayback + CompactQueue) | Mirrors RescanHooks pattern; breaks circular dependency between library, player, queue packages | -| querySingleInt64 helper for hand-crafted SQL aggregates | DB type has QueryContext (returns *sql.Rows) but no QueryRowContext; helper wraps scan-close cycle | -| Sentinel errors for all validation per err113 | errLibraryNameEmpty, errLibraryNameTooLong, errLibraryNameDuplicate, errLibraryPathNotExist | -| Pre-populate phantom metadata BEFORE cascade delete | Avoids lost join data — playlist_tracks need track metadata after audio_files rows are gone | -| Selectable library checkboxes for scan targeting | User selects which libraries to scan instead of scan-all-or-nothing; Set model with select-all/indeterminate | -| Inline progress bar per library row | Each library row shows scan phase and percentage, replacing global-only indicator | -| Collapsible config sections with chevron dropdown | Keeps settings page organized as it grows; new config-section component | -| Library store invalidation on LibraryRemoved event | Ensures all data views refresh after library removal | - -### Phase 14 Decisions - -| Decision | Rationale | -|----------|-----------| -| Primary views cached, detail views ephemeral | Detail views depend on entity IDs that change; caching would show stale content | -| Inline style.display toggle over CSS class | Simpler, no specificity issues, empty string restores natural display value | -| viewCache bounded at 6 entries | One per primary view — negligible memory since data is already in store caches | -| contain: strict only on .main-panel | Has explicit dimensions (flex: 1, overflow: hidden); elsewhere use layout style to not break flex | -| will-change: transform only on scroll containers | Not on :host — avoids wasting GPU memory on non-scrolling elements | -| content-visibility: auto on album cards with contain-intrinsic-size | Prevents layout shift during scroll while skipping off-screen rendering | -| Event delegation via data-index + closest() for virtualizer items | Zero per-item closures in renderItem; all events delegated on virtualizer element | -| changeGeneration counter in library store | Simpler than typed subscriptions; skips requestUpdate on loading-only transitions | -| RAF throttle over debounce for scroll saves | Saves position once per frame during scrolling, not just after stop; prevents lost positions on quick navigation | -| Keep monkey-patch alongside overflow-anchor CSS | CSS overflow-anchor disables browser anchoring but not lit-virtualizer's internal _correctScrollError | - -### Phase 13 Decisions - -| Decision | Rationale | -|----------|-----------| -| IN-subquery pattern for album/artist/genre library filtering | Entities are global, tracks belong to libraries; subquery filters entity IDs by library membership | -| Empty slice return (not error) for library with no tracks | Empty library is valid state, not error condition; callers handle empty UI | -| 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 for consistency | -| ScanHooks callback for phantom resolution | Mirrors RemovalHooks/RescanHooks pattern; avoids circular dependency between library and playlist packages | -| phantom_file_path column on playlist_tracks | Stored at removal time for post-scan matching; enables automatic phantom resolution | -| M3U8-based phantom resolution over SQL-only | Reads playlist files to match by position and file path; handles both pre-existing and new phantoms | +| 5 phases (15-19) for 20 requirements | Natural clustering: foundation → writers → single edit → batch edit → stretch OGG | +| WRITE-05 in Phase 15 (not 16) | Atomic write utility is foundational infrastructure, not format-specific | +| Cover art embed (WRITE-04) in Phase 16 | Cover art embedding is format-specific writer work, shares test infrastructure with tag writing | +| Cover art UI (EDIT-03) in Phase 17 | Cover art selection UX is part of the single-track edit dialog | +| OGG as separate Phase 19 (stretch) | Custom OGG page rewriter is MEDIUM-HIGH risk; MP3+FLAC covers vast majority of libraries | +| SYNC-04 (scan pause during edits) in Phase 16 | Scan/edit mutual exclusion is part of the write pipeline, not the UI layer | +| Phase 18 depends on Phase 17 | Batch editing is N × single with UI complexity on top; pipeline must be solid first | +| Phase 19 depends on Phase 16 (not 17) | OGG writing is a backend writer addition; UI integration is format-transparent | ### Warnings (carry forward) -- Player lock ordering (`p.mu` before `speaker.Lock()`, goroutine dispatch in beep callback) — carry forward +- Player lock ordering (`p.mu` before `speaker.Lock()`, goroutine dispatch in beep callback) - modernc.org/libc version must match exactly when updating modernc.org/sqlite - `@lit-labs/signals` is experimental (v0.2.0) — not blocking but noted -- Scan cancellation: skip orphan cleanup on cancelled scans — Phase 9 ✅ (implemented in 09-01) -- Volume mutations (ChangeVolume/MuteToggle) must emit events + persist state — Phase 9 ✅ (fixed in 09-05) -- ALTER TABLE ADD COLUMN requires DEFAULT for NOT NULL — create libraries table first -- Table rebuild must audit ALL CASCADE FKs (playlist_tracks AND queue_tracks) -- FTS5 contentless can't DELETE rows — stale entries accumulate after library removal; consider contentless_delete migration +- FTS5 contentless can't DELETE rows — **SCHEMA-01 fixes this** with contentless_delete migration - Orphan cleanup must not delete shared entities across libraries (reference-counting bottom-up) -- Existing user migration must be seamless (TOML DirectoryPath to DB libraries table) - -### Deferred Improvements - -- **Bulk phantom matching performance** — `FindPhantomMatches` runs 3 sequential DB queries per phantom track (basename search, FTS filename, FTS keywords). With hundreds of phantoms this is O(n×3) round trips. Could batch the basename query (WHERE basename IN (...)), pre-load FTS results, or parallelize with goroutines. Not urgent now that the false-phantom bug is fixed (tracks appeared phantom due to empty library root, not actual missing files). Revisit if users import large playlists from external sources with genuinely unresolved paths. +- FLAC files require full rewrite for tag changes — atomic write-to-temp-then-rename mandatory +- Currently-playing file must be stopped before writing (WRITE-06) — Windows file locking is especially strict +- Shared entity fan-out — editing one track's artist must NOT mutate the shared artist_credit row ### Research Flags -- **Multi-library research complete** — see `.planning/research/` (STACK.md, FEATURES.md, ARCHITECTURE.md, PITFALLS.md, SUMMARY.md) +- **Phase 16:** go-flac libraries (44 stars) — verify round-trip with edge-case FLAC files early +- **Phase 19:** Custom OGG page rewriter — prototype before committing; consider dropping if too complex +- **Phase 16:** Album artist storage — not currently a separate entity; resolve during planning + +### Deferred Improvements + +- **Bulk phantom matching performance** — O(n×3) round trips per phantom. Revisit if large external playlist imports occur. ## Session Continuity ### Last Session **Date:** 2026-03-16 -**What happened:** Completed Plan 13-02 — library filter dropdown in top bar, all views wired to ByLibrary queries, phantom track auto-resolution via ScanHooks. Checkpoint APPROVED — all 7 requirements verified (VIEW-01–04, PLAY-01–03). Additional bugfixes: virtualizer event delegation race condition, phantom auto-resolution after re-scan. -**Where we stopped:** Completed 13-02-PLAN.md — Phase 13 complete (2/2 plans), v1.1 milestone complete -**Next action:** v1.1 milestone shipped — all phases (9-14) complete +**What happened:** Created v1.2 Tag Editing roadmap — 5 phases (15-19) covering all 20 requirements. Derived phases from requirement clustering: schema foundation → tag writers + DB sync → single-track UI → batch UI → OGG stretch. +**Where we stopped:** Roadmap created, ready for phase planning +**Next action:** `/gsd-plan-phase 15` — Schema Migration & Write Safety --- *State initialized: 2026-02-27* @@ -200,4 +129,4 @@ Decisions from v1.0 are archived in PROJECT.md Key Decisions table. Key patterns | 19 | fix phantom playlist tracks with multi-root path resolution | 2026-03-16 | 9144ded | [19-fix-phantom-playlist-tracks](./quick/19-fix-phantom-playlist-tracks/) | Last activity: 2026-03-16 - Completed quick task 19: fix phantom playlist tracks with multi-root path resolution -*Last updated: 2026-03-16 — Completed quick task 19* +*Last updated: 2026-03-16 — v1.2 roadmap created*