Files
yellowjacket/.planning/research/PITFALLS.md
T
yonlu cdf8bff01a docs: restructure v1.1 milestone for multi-library support
- Rename milestone from 'Features & Extensibility' to 'Multi-Library Support'
- Replace old phases 10-14 (TAG/SMRT/GAP/MB/LAYOUT/PLUG) with 20 multi-library requirements (LIB/LSCAN/VIEW/PLAY/DATA)
- Move deferred features to Future Requirements
- Update research files with multi-library research (Stack, Features, Architecture, Pitfalls)
- Architecture: hybrid model (library_id on audio_files only), libraries in DB, sequential scanning, phantom tracks
2026-03-08 11:54:37 -04:00

4.3 KiB

Pitfalls Research: Multi-Library Support

Researched: 2026-03-08 Confidence: HIGH

Critical Pitfalls

P1: SQLite ALTER TABLE ADD COLUMN with NOT NULL Requires DEFAULT

SQLite requires NOT NULL columns added via ALTER TABLE to have a default. Create libraries table and insert default library BEFORE adding library_id to audio_files. Use DEFAULT {id} where id is the auto-created library's ID.

Phase: Schema & Migration

P2: Table Rebuild for CASCADE to SET NULL Must Audit ALL Tables

Both playlist_tracks AND queue_tracks have ON DELETE CASCADE on audio_file_id. Decision: playlist_tracks -> SET NULL (phantom support), queue_tracks -> keep CASCADE (queue is ephemeral). Must explicitly document this choice.

Phase: Schema & Migration

P3: FTS5 Contentless Table Cannot Delete Individual Rows

After removing a library with 10K tracks, 10K stale FTS5 entries remain. Current JOIN filtering handles this, but FTS5 scoring is affected. Consider migrating to contentless_delete=1 (SQLite 3.43.0+). Alternative: full rebuild after library removal.

Phase: Schema & Migration

P4: Orphan Cleanup Through Entity Graph Is Complex

Reference-counting deletes must handle shared entities. Two libraries with same artist — removing one must not delete the artist if the other still references it. Use NOT IN (SELECT ...) or LEFT JOIN ... WHERE ... IS NULL pattern. Single transaction required.

Phase: Backend API / Library CRUD

P5: Existing User Migration Must Be Seamless

First launch after update: migration 6 reads TOML DirectoryPath, creates library row, backfills audio_files.library_id. Test on real user database snapshot, not just fresh DB.

Phase: Schema & Migration

Moderate Pitfalls

P6: Frontend Memory Pressure with Multiple Large Libraries

libraryStore.eagerFetch() loads ALL data. 150K tracks x ~500 bytes = 75MB. Use backend filtering (pass library_id to queries). When "All Libraries" is selected, this is unavoidable for now — pagination is a future optimization.

Phase: Frontend

P7: Scan Coordination — No Concurrent Scans

Single scanActive bool, single entity cache, single writer SQLite. Enforce one-scan-at-a-time globally with scan coordinator. Track which library is scanning for UI display.

Phase: Backend Scan Pipeline

P8: Phantom Track Resolution with Multiple Library Roots

LibraryDirProvider returns single string. M3U8 path resolution checks one root. With multi-library, try all library roots for phantom resolution. Store phantom_file_path as absolute path to avoid ambiguity.

Phase: Frontend / Playlist Integration

P9: Queue and Now-Playing During Library Removal

If currently playing track belongs to removed library: stop playback, advance to next non-removed track. Check queue and player state before proceeding with removal.

Phase: Backend API / Library CRUD

P10: Cross-Library Entity Deduplication

Same artist in two libraries -> one artists row (UNIQUE constraint handles this). Removing one library's audio_files must NOT delete shared artist. Reference-counting cleanup handles this correctly.

Phase: Backend API / Library CRUD

Minor Pitfalls

P11: Config Migration — TOML to DB Split Creates Two Sources of Truth

Move ALL library-related config to DB. TOML only for app-level settings (theme, shortcuts, window). TOML [Library] section is migration source only.

Phase: Schema & Migration

P12: Library Filter State Interacting with Everything

Single filter state in libraryStore. All data-fetching functions accept the filter. Trigger invalidate+refetch on filter change.

Phase: Frontend

P13: Scan-While-Remove Race Condition

Before removing a library, cancel any active scan on it and wait for completion. Serialize scan and remove operations.

Phase: Backend API

P14: Cover Art Files Not Library-Scoped

Cover art stored by content hash (shared). Removing a library: only delete cover_art DB rows that are truly orphaned (no remaining release_groups reference them). Then delete corresponding files.

Phase: Backend API

P15: Testing Gaps

Create "two-library fixture" test helper. Test: add two libraries with overlapping artists -> remove one -> verify other is intact. Test migration on pre-multi-library DB snapshot.

Phase: All phases (accompanying tests)