Files
yellowjacket/.planning/quick/10-diagnose-duplicate-album-merging-bug-alb/10-SUMMARY.md
T
yonlu 1b33923980 docs(quick-10): complete fix duplicate album merging bug plan
- Add 10-SUMMARY.md with full execution details
- Update STATE.md with quick task 10 entry and session info
2026-03-05 10:26:57 -05:00

4.2 KiB

phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
quick-10 10 database, library
bugfix
schema-migration
sqlite
entity-cache
requires provides affects
composite-unique-release-groups
migration-5
release_groups
library-scan
cover-grid
added patterns
composite-unique-constraint
table-rebuild-migration
composite-cache-key
created modified
backend/database/sql/schemas/release_groups.sql
backend/database/sql/queries/release_groups.sql
backend/database/sql/sqlcgen/release_groups.sql.go
backend/database/database.go
backend/library/library.go
backend/library/scan_test.go
Rename GetReleaseGroupByName to GetReleaseGroupByNameAndArtist (no callers outside generated code)
Use null byte separator in composite cache key for safety
Drop and recreate track_metadata VIEW during migration to avoid SQLite VIEW dependency error
duration completed tasks_completed tasks_total
5m25s 2026-03-05 2 2

Quick Task 10: Fix Duplicate Album Merging Bug

Composite unique constraint on (name, album_artist_credit_id) for release_groups, with migration 5 to rebuild existing tables and entity cache fix to key by album+artist.

Task Summary

# Task Commit Key Changes
1 Fix schema, queries, and regenerate sqlc 999ab96 UNIQUE(name, album_artist_credit_id) in schema; ON CONFLICT updated; GetReleaseGroupByNameAndArtist
2 Add migration 5 and fix entity cache d43ba7b Migration 5 rebuilds table; cache keys by album+artistID; VIEW drop/recreate

What Changed

Schema (release_groups.sql)

  • Removed UNIQUE from name TEXT NOT NULL UNIQUE
  • Added table-level UNIQUE(name, album_artist_credit_id) — SQLite treats NULL as unique, so albums without an artist won't collide

Queries (release_groups.sql)

  • UpsertReleaseGroup: ON CONFLICT(name)ON CONFLICT(name, album_artist_credit_id)
  • GetReleaseGroupByNameGetReleaseGroupByNameAndArtist with two params (name + album_artist_credit_id)

Migration 5 (database.go)

  • Disables FK checks temporarily
  • Drops track_metadata VIEW (depends on release_groups)
  • Creates release_groups_new with composite unique constraint
  • Copies data, drops old, renames new
  • Recreates both indexes and the track_metadata VIEW
  • Re-enables FK checks, bumps user_version to 5

Entity Cache (library.go)

  • Cache key changed from tags.Album to fmt.Sprintf("%s\x00%d", tags.Album, artistID) where artistID is -1 when no album artist credit exists
  • All 3 cache access points updated (lookup, cover art update, store after upsert)

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] track_metadata VIEW blocking table rename

  • Found during: Task 2
  • Issue: SQLite refuses to rename release_groups_new to release_groups when the track_metadata VIEW references the old table
  • Fix: Drop the VIEW before table rebuild, recreate it after rename (VIEW definition matches embedded schema exactly)
  • Files modified: backend/database/database.go
  • Commit: d43ba7b

2. [Rule 1 - Bug] Tests using old cache key format

  • Found during: Task 2
  • Issue: TestResolveReleaseGroup and TestResolveReleaseGroup_CacheHit use bare album name as cache key
  • Fix: Updated test assertions to use composite cache key format (albumName\x00artistCreditID)
  • Files modified: backend/library/scan_test.go
  • Commit: d43ba7b

Verification

  • go build ./... — passes
  • go test ./... — all 14 test packages pass
  • go vet ./... — no issues
  • Schema has UNIQUE(name, album_artist_credit_id)
  • UpsertReleaseGroup uses ON CONFLICT(name, album_artist_credit_id)
  • Migration 5 exists and rebuilds table ✓
  • Entity cache key includes artist credit ID ✓

Notes

  • Existing databases: Migration 5 changes the constraint but does NOT split already-merged albums. Users must trigger a full library rescan after upgrading.
  • NULL handling: SQLite's UNIQUE treats each NULL as distinct, so albums without album_artist_credit_id will each get their own row — this is desired behavior.