--- gsd_state_version: 1.0 milestone: v1.2 milestone_name: Tag Editing status: in-progress last_updated: "2026-03-18T15:18:33Z" progress: total_phases: 5 completed_phases: 3 total_plans: 7 completed_plans: 7 --- # YellowJacket — Project State ## Project Reference See: .planning/PROJECT.md (updated 2026-03-16) **Core value:** The music player works reliably and feels solid — every interaction is correct, responsive, and trustworthy. **Current focus:** v1.2 Tag Editing ## Current Position Phase: Phase 17 — Single Track Edit (complete) Plan: 2 of 2 complete Status: Phase 17 complete — all single-track edit requirements fulfilled Last activity: 2026-03-18 — Completed 17-02 (save flow, cover art editing, error handling, Wails deserialization fixes) ### Phase Overview | Phase | Status | |-------|--------| | 15. Schema Migration & Write Safety | Complete (2/2 plans) | | 16. Tag Writing & Database Sync | Complete (3/3 plans) | | 17. Single Track Edit | Complete (2/2 plans) | | 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 baseline:** 6 phases, 18 plans in 10 days (~85 commits) **v1.2 scope:** 5 phases, 20 requirements | Phase | Plan | Duration | Tasks | Files | |-------|------|----------|-------|-------| | 15 | 01 | 15min | 2 | 5 | | 15 | 02 | 16min | 2 | 2 | | 16 | 01 | 28min | 2 | 14 | | 16 | 02 | 20min | 2 | 6 | | 16 | 03 | 9min | 2 | 7 | | 17 | 01 | 11min | 2 | 11 | | 17 | 02 | 25min | 2 | 8 | ## Accumulated Context ### Key Decisions 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 - AST-based codegen for cross-language constant sync - 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.2 Execution Decisions | Decision | Rationale | |----------|-----------| | Inlined migration 8 SQL rather than calling DB struct methods | `runMigrations` receives raw `*sql.DB`, not `*DB` — cannot call receiver methods | | Deterministic `.yj-tmp` suffix for temp files | Enables reliable orphan cleanup without directory scanning | | `*slog.Logger` as first param for AtomicWrite | Matches codebase convention — all packages accept logger as first arg | | go-flac `WriteTo(io.Writer)` for AtomicWrite integration | Pipes directly into temp file callback; avoids `Save(path)` file path conflicts | | `replaceVorbisComment` as filter+add pattern | flacvorbis has no Set/Replace — must remove existing entries then Add new value | | id3v2 WriteTo + manual audio copy for AtomicWrite | `tag.Save()` writes to original file; use `WriteTo(tmp)` + seek past tag + `io.Copy` audio data | | Snapshot tag size before `id3v2.Open()` | `originalSize` is unexported; read 10-byte ID3v2 header and decode synchsafe size ourselves | | PlayerStopper interface for tagwriter→player decoupling | Breaks import cycle; playerAdapter in app.go wraps *player.Player | | pipelineMu sync.Mutex for scan/write mutual exclusion | Simple mutex on Library; both scan and write pipelines acquire at start, release at end | | FTS5 delete+insert within DB transaction | Execute directly on *sql.Tx for atomicity with entity relink | | Global genre orphan cleanup via DELETE WHERE id NOT IN | Simpler than tracking old genre IDs; safe because genres only referenced via recording_genres | | Manually added Wails TypeScript bindings for new Go methods | Wails binding generator runs at `wails dev`/`wails build` time, not via `go generate`; manual addition matches existing pattern exactly | | Track Details opens for first selected track in multi-select | `filePaths[0]` is consistent across all 4 views; avoids blocking the menu item unnecessarily | | ReadFile Go method on FrontendUtil for cover art bytes | Native file dialog returns path; frontend needs bytes for blob preview + save payload | | asInt/asBytes helpers for Wails JSON deserialization | Wails sends JS numbers as float64 and []byte as base64; direct type assertions silently fail | | Cover art DB sync with content-hash dedup + thumbnail generation | Saves to covers cache dir, upserts cover_art row, updates release_groups.cover_art_id | ### v1.2 Roadmap Decisions | Decision | Rationale | |----------|-----------| | 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) - modernc.org/libc version must match exactly when updating modernc.org/sqlite - `@lit-labs/signals` is experimental (v0.2.0) — not blocking but noted - ~~FTS5 contentless can't DELETE rows~~ — **RESOLVED: SCHEMA-01 completed** — contentless_delete=1 migration applied - Orphan cleanup must not delete shared entities across libraries (reference-counting bottom-up) - 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 - ~~**Phase 16:** go-flac libraries (44 stars) — verify round-trip with edge-case FLAC files early~~ — **RESOLVED: 16-02 completed** — 7 round-trip tests pass, dhowden/tag reads what go-flac writes - **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~~ — **RESOLVED: 16-CONTEXT.md** — Album artist stays as text field on audio_files, no new entity table ### 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-18 **What happened:** Completed Phase 17 Plan 02 — Save flow with diff-only TagChanges, cover art replace/remove via native file picker, inline error handling. Fixed 4 bugs during verification: stale dialog data, Wails float64 deserialization, cover art DB sync, cover art URL refresh. **Where we stopped:** Completed 17-02-PLAN.md — Phase 17 complete **Next action:** Plan or execute Phase 18 (Batch Edit) --- *State initialized: 2026-02-27* ### Quick Tasks Completed | # | Description | Date | Commit | Directory | |---|-------------|------|--------|-----------| | 16 | add ctrl+a hotkey to multi-select views to select all items | 2026-03-07 | 043c74c | [16-add-ctrl-a-hotkey-to-multi-select-views-](./quick/16-add-ctrl-a-hotkey-to-multi-select-views-/) | | 17 | refactor playlist view to use subpages | 2026-03-08 | 955cd68 | [17-refactor-playlist-view-to-use-subpages-l](./quick/17-refactor-playlist-view-to-use-subpages-l/) | | 18 | add multi-column metadata display to playlist-details | 2026-03-08 | ce23177 | [18-add-multi-column-metadata-display-to-pla](./quick/18-add-multi-column-metadata-display-to-pla/) | | 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-18 — Completed 17-02 (save flow, cover art editing, Phase 17 complete)*