Import issue history in backlog.md

This commit is contained in:
Claudio Ortolina
2026-04-20 10:01:47 +01:00
parent 78487b0085
commit f995e01ca7
146 changed files with 4901 additions and 0 deletions
@@ -0,0 +1,18 @@
---
id: ML-123
title: Hide collected release input and details for wishlisted records
status: To Do
assignee: []
created_date: '2026-04-20 08:59'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/40'
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2025-05-07 · updated 2025-05-08 · closed 2025-05-08 · not planned_
<!-- SECTION:DESCRIPTION:END -->
@@ -0,0 +1,50 @@
---
id: ML-16
title: Inconsistent HTTP timeout configuration across API clients
status: To Do
assignee: []
created_date: '2026-04-20 08:50'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/163'
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-04-05 · updated 2026-04-09 · closed 2026-04-08 · not planned_
## Summary
HTTP timeout configuration varies significantly across API integrations with no documented rationale for the differences.
## Evidence
- **Last.fm**: Custom `pool_timeout: 10_000`, `receive_timeout: 2500`, `connect_timeout: 2500`
- **OpenAI GPT**: `receive_timeout: 10_000`, `connect_timeout: 2_500`
- **OpenAI Chat stream**: `receive_timeout: 60_000`, `connect_timeout: 5_000`
- **MusicBrainz, Discogs, Wikipedia, Brave Search**: Default timeouts only (no custom configuration)
## Why This Matters
- No consistency in how timeouts are configured
- Unclear whether defaults are appropriate for each API's characteristics
- Last.fm applied custom timeouts based on observed issues; other APIs may have similar unaddressed problems
## Suggested Fix
1. Audit each API's typical response times
2. Set explicit, documented timeout values for all API clients
3. Consider making timeouts configurable via each API's Config module
## Acceptance Criteria
<!-- AC:BEGIN -->
- All API clients have explicit, intentional timeout configuration
- Timeout choices are documented (e.g., in Config module comments)
<!-- SECTION:DESCRIPTION:END -->
- [ ] #1 All API clients have explicit, intentional timeout configuration
- [ ] #2 Timeout choices are documented (e.g., in Config module comments)
<!-- AC:END -->
@@ -0,0 +1,50 @@
---
id: ML-18
title: Three representations of artists without clear canonical source
status: To Do
assignee: []
created_date: '2026-04-20 08:50'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/161'
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-04-05 · updated 2026-04-09 · closed 2026-04-08 · not planned_
## Summary
Artist data exists in three different forms with no clear canonical source:
1. `Record` embeds `:artists` (embedded schema in record)
2. `ArtistRecord` — separate lookup table/view
3. `ArtistInfo` — external metadata store
## Why This Matters
- Unclear which representation is authoritative for a given use case
- Updates to one representation don't automatically propagate to others
- New developers must understand all three to work with artist data
## Affected Files
- `lib/music_library/records/record.ex` (embedded `:artists`)
- `lib/music_library/records/artist_records.ex`
- `lib/music_library/artists/artist_info.ex`
## Suggested Fix
Document the purpose and canonical use case for each representation. Consider whether `ArtistRecord` (DB view) can be simplified or whether the embedded artists on Record should be the single source for record-artist relationships.
## Acceptance Criteria
<!-- AC:BEGIN -->
- Clear documentation of each representation's purpose
- Reduced confusion about which to use in new features
<!-- SECTION:DESCRIPTION:END -->
- [ ] #1 Clear documentation of each representation's purpose
- [ ] #2 Reduced confusion about which to use in new features
<!-- AC:END -->
@@ -0,0 +1,47 @@
---
id: ML-19
title: Circular dependency between Records and Collection/Wishlist
status: To Do
assignee: []
created_date: '2026-04-20 08:50'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/160'
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-04-05 · updated 2026-04-09 · closed 2026-04-06 · not planned_
## Summary
`Collection` and `Wishlist` call `Records.search_records(base_search(), ...)`, creating a circular dependency where Records is called by modules that exist primarily as query filters on Records.
## Why This Matters
- The only difference between Collection and Wishlist is the base query filter (`purchased_at IS NOT NULL` vs `IS NULL`)
- These aren't independent contexts — they're query filters on Records
- The circular call pattern (Records <- Collection <- Records) makes the dependency graph confusing
## Affected Files
- `lib/music_library/collection.ex` (line 22)
- `lib/music_library/wishlist.ex` (line 20)
- `lib/music_library/records.ex`
## Suggested Fix
Make Collection and Wishlist sub-modules of Records (`Records.Collection`, `Records.Wishlist`) or simple functions within Records that accept a filter parameter.
## Acceptance Criteria
<!-- AC:BEGIN -->
- No circular dependency between context modules
- Collection/Wishlist filtering remains functional
<!-- SECTION:DESCRIPTION:END -->
- [ ] #1 No circular dependency between context modules
- [ ] #2 Collection/Wishlist filtering remains functional
<!-- AC:END -->
@@ -0,0 +1,48 @@
---
id: ML-20
title: Missing indexes on JSON extraction queries in ListeningStats
status: To Do
assignee: []
created_date: '2026-04-20 08:50'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/159'
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-04-05 · updated 2026-04-12 · closed 2026-04-12 · not planned_
## Summary
`ListeningStats` filters tracks by `artist.musicbrainz_id` and `album.musicbrainz_id` via JSON extraction, and `Artists` searches by `artist ->> '$.name'` with LIKE. These patterns benefit from expression-based indexes that don't currently exist.
## Evidence
- `lib/music_library/listening_stats.ex` lines 100-105: filtering by `artist.musicbrainz_id` (JSON extraction)
- `lib/music_library/listening_stats.ex` lines 474-478: filtering by `album.musicbrainz_id` and album title
- `lib/music_library/artists.ex` lines 92-96: LIKE search on `artist ->> '$.name'`
## Suggested Fix
Add expression-based indexes on the most frequently queried JSON paths:
```sql
CREATE INDEX idx_tracks_artist_mbid ON tracks(json_extract(artist, '$.musicbrainz_id'));
CREATE INDEX idx_tracks_album_mbid ON tracks(json_extract(album, '$.musicbrainz_id'));
```
Profile queries before and after to confirm impact.
## Acceptance Criteria
<!-- AC:BEGIN -->
- Expression-based indexes exist for hot JSON extraction paths
- Query plans show index usage for filtered ListeningStats queries
<!-- SECTION:DESCRIPTION:END -->
- [ ] #1 Expression-based indexes exist for hot JSON extraction paths
- [ ] #2 Query plans show index usage for filtered ListeningStats queries
<!-- AC:END -->
@@ -0,0 +1,47 @@
---
id: ML-23
title: StatsLive.Index runs 4+ sequential queries in mount
status: To Do
assignee: []
created_date: '2026-04-20 08:51'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/156'
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-04-05 · updated 2026-04-09 · closed 2026-04-09 · not planned_
## Summary
`StatsLive.Index` mount calls `get_latest_record()`, `count_records_by_artist()`, `count_records_by_genre()`, `count_records_by_release_year()`, and more as separate sequential queries with no batching or async loading.
## Why This Matters
- Each query is a separate database round-trip
- Mount blocks until all queries complete, delaying initial page render
- Lines 54-91 assign 9+ data structures simultaneously
## Affected Files
- `lib/music_library_web/live/stats_live/index.ex` (lines 54-91)
## Suggested Fix
Use `start_async` or `assign_async` to load stats data concurrently after mount, showing placeholder/loading states for each section.
## Acceptance Criteria
<!-- AC:BEGIN -->
- Stats page initial render is faster
- Data loads concurrently where possible
- Loading states are shown while data is fetched
<!-- SECTION:DESCRIPTION:END -->
- [ ] #1 Stats page initial render is faster
- [ ] #2 Data loads concurrently where possible
- [ ] #3 Loading states are shown while data is fetched
<!-- AC:END -->
@@ -0,0 +1,49 @@
---
id: ML-26
title: No retry/backoff strategy for non-Last.fm APIs
status: To Do
assignee: []
created_date: '2026-04-20 08:51'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/153'
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-04-05 · updated 2026-04-09 · closed 2026-04-08 · not planned_
## Summary
Only the Last.fm integration has structured error responses with retry classification (`{:snooze, seconds}`). All other API integrations (MusicBrainz, Discogs, Wikipedia, Brave Search, OpenAI) use simple `max_attempts: 3` with no backoff, causing immediate retries that can trigger upstream rate limits.
## Why This Matters
- Immediate retries on transient failures can cause thundering herd effects
- Rate limit errors are treated identically to other transient errors
- No exponential backoff means the same failure is likely to repeat
## Evidence
- `lib/last_fm/api/error_response.ex` — comprehensive error classification (70 lines)
- All other API modules return raw bodies on error with no classification
- `lib/music_library/worker/refresh_scrobbles.ex` (lines 26-32) — only worker with `{:snooze, seconds}`
## Suggested Fix
1. Add structured error response modules for MusicBrainz, Discogs, and Wikipedia
2. Implement backoff strategies in their respective workers
3. Classify rate-limit responses separately from transient errors
## Acceptance Criteria
<!-- AC:BEGIN -->
- API-specific workers handle rate-limit responses with appropriate snooze durations
- Transient errors use exponential backoff rather than immediate retry
<!-- SECTION:DESCRIPTION:END -->
- [ ] #1 API-specific workers handle rate-limit responses with appropriate snooze durations
- [ ] #2 Transient errors use exponential backoff rather than immediate retry
<!-- AC:END -->
@@ -0,0 +1,43 @@
---
id: ML-27
title: 11 copy-pasted Oban workers for refresh operations
status: To Do
assignee: []
created_date: '2026-04-20 08:51'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/152'
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-04-05 · updated 2026-04-12 · closed 2026-04-12 · not planned_
## Summary
Single-record refresh workers and their batch counterparts are nearly identical, differing only in the delegated function call. 11 workers share the same ~10-line structure.
## Affected Workers
Single-record (6): `ArtistRefreshDiscogsData`, `ArtistRefreshMusicBrainzData`, `ArtistRefreshWikipediaData`, `FetchArtistLastFmData`, `RecordRefreshMusicBrainzData`, `GenerateRecordEmbedding`
Batch (5): `ArtistRefreshAllDiscogsData`, `ArtistRefreshAllMusicBrainzData`, `ArtistRefreshAllWikipediaData`, `RecordRefreshAllMusicBrainzData`, `RecordGenerateAllEmbeddings`
## Suggested Fix
Consider a generic worker pattern or macro that accepts the delegated function as a parameter, reducing boilerplate while keeping each worker as a distinct module for queue routing.
## Acceptance Criteria
<!-- AC:BEGIN -->
- Boilerplate is reduced
- Each worker remains individually identifiable for Oban queue routing and monitoring
- No change in worker behaviour
<!-- SECTION:DESCRIPTION:END -->
- [ ] #1 Boilerplate is reduced
- [ ] #2 Each worker remains individually identifiable for Oban queue routing and monitoring
- [ ] #3 No change in worker behaviour
<!-- AC:END -->
@@ -0,0 +1,53 @@
---
id: ML-28
title: Records context has 8+ responsibilities
status: To Do
assignee: []
created_date: '2026-04-20 08:51'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/151'
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-04-05 · updated 2026-04-11 · closed 2026-04-11 · not planned_
## Summary
The `Records` context module handles search, genre management, cover art operations, color extraction, embeddings, MusicBrainz data refresh, artist metadata refresh, CRUD, and PubSub notifications — too many concerns in one module.
## Why This Matters
- Hard to navigate and understand at a glance
- Changes to one concern risk affecting others
- Testing is harder when responsibilities are interleaved
## Affected Files
- `lib/music_library/records.ex`
## Suggested Fix
Extract into focused sub-modules:
- `Records.Search` — search operations
- `Records.Metadata` — MusicBrainz sync, genre population
- `Records.Assets` — covers, colors
- `Records.Embeddings` — AI embedding generation
Keep the `Records` module as the public API that delegates to sub-modules.
## Acceptance Criteria
<!-- AC:BEGIN -->
- Each sub-module has a single clear responsibility
- Public API remains unchanged for callers
- No regression in functionality
<!-- SECTION:DESCRIPTION:END -->
- [ ] #1 Each sub-module has a single clear responsibility
- [ ] #2 Public API remains unchanged for callers
- [ ] #3 No regression in functionality
<!-- AC:END -->
@@ -0,0 +1,46 @@
---
id: ML-3
title: Document intentional async-only coupling from Records to Artists
status: To Do
assignee: []
created_date: '2026-04-20 08:44'
updated_date: '2026-04-20 08:44'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/181'
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-04-16 · updated 2026-04-19 · closed 2026-04-19 · not planned_
## Summary
`Records.create_record/1` calls `Artists.refresh_artist_info_async/1` and `Records.delete_record/1` calls `Artists.prune_artist_info_async/1`. Both are deliberately `*_async` (Oban enqueues) to avoid a runtime cycle with the embedding/genre regeneration path in `Artists`. The intent is not captured inline.
## Evidence
- `lib/music_library/records.ex:385``Artists.refresh_artist_info_async/1` call
- `lib/music_library/records.ex:415``Artists.prune_artist_info_async/1` call
No comment explaining why the async form is required. A future refactor that decides to "just inline the synchronous version" would reintroduce runtime coupling.
## Fix
Add a short inline comment above each call site explaining:
```elixir
# async to avoid a runtime cycle:
# Records.create/delete -> Artists.refresh/prune (sync) -> Records (embedding regeneration)
Artists.refresh_artist_info_async(artist)
```
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Inline comments added at records.ex:385 and :415
- [ ] #2 Comments explain the runtime-cycle motivation, not just "it's async"
<!-- AC:END -->
@@ -0,0 +1,39 @@
---
id: ML-66
title: Large modules unchanged since last audit
status: To Do
assignee: []
created_date: '2026-04-20 08:55'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/109'
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-03-12 · updated 2026-04-09 · closed 2026-03-16 · not planned_
## Description
The four modules flagged in #99 remain at the same size:
| File | LOC |
|------|-----|
| `lib/music_library_web/live/artist_live/show.ex` | 804 |
| `lib/music_library_web/components/record_components.ex` | 761 |
| `lib/music_library_web/components/record_form.ex` | 652 |
| `lib/music_brainz/api.ex` | 564 |
Not urgent — only refactor when new feature work naturally touches these files.
## Expected behavior
Monitor and consider extraction when adding new features to these modules.
## Source
From technical debt audit (2026-03-12). Continuation of #99.
<!-- SECTION:DESCRIPTION:END -->
@@ -0,0 +1,39 @@
---
id: ML-68
title: Random data in test fixtures
status: To Do
assignee: []
created_date: '2026-04-20 08:55'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/107'
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-03-12 · updated 2026-04-09 · closed 2026-03-16 · not planned_
## Description
Test fixtures use non-deterministic random values, which can make test failures harder to reproduce:
- `test/support/fixtures/music_library/records.ex:74``Enum.random(@artists)`
- `test/support/fixtures/music_library/records.ex:86``Enum.take_random(@genres, :rand.uniform(3))`
- `test/support/fixtures/music_library/records.ex:91``Enum.random(@titles)`
- `test/support/fixtures/music_library/records.ex:93``Enum.random(Record.formats())`
- `test/support/fixtures/music_library/records.ex:95``Enum.random(1969..2024)`
- `test/support/fixtures/scrobbled_tracks_fixtures.ex:16``Enum.random(0..86_400)`
Previously tracked in #85 (closed) but the randomness remains.
## Expected behavior
Use deterministic sequences (e.g., based on `System.unique_integer`) or seeded randomness for reproducible tests.
## Source
From technical debt audit (2026-03-12). Residual from #85.
<!-- SECTION:DESCRIPTION:END -->
@@ -0,0 +1,30 @@
---
id: ML-69
title: ArgumentError raises in ScrobbleActivity
status: To Do
assignee: []
created_date: '2026-04-20 08:55'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/106'
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-03-12 · updated 2026-04-09 · closed 2026-03-14 · not planned_
## Description
`lib/music_library/scrobble_activity.ex:19,61,116` raises `ArgumentError` when both `started_at` and `finished_at` are provided to scrobble functions. Per Elixir conventions, public API functions should return `{:error, _}` tuples for input validation rather than raising. These functions are called from LiveComponent event handlers where a crash would kill the LiveView process.
## Expected behavior
Return `{:error, :invalid_options}` (or similar) and handle upstream with a user-facing toast message.
## Source
From technical debt audit (2026-03-12).
<!-- SECTION:DESCRIPTION:END -->
@@ -0,0 +1,30 @@
---
id: ML-70
title: Bare catch blocks in telemetry storage
status: To Do
assignee: []
created_date: '2026-04-20 08:55'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/105'
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-03-12 · updated 2026-04-09 · closed 2026-03-16 · not planned_
## Description
`lib/music_library_web/telemetry/storage.ex:106-107,123-124` uses bare catch blocks that silently swallow all errors. While reasonable for telemetry resilience (ETS table may not exist during shutdown), they make debugging harder when the table is unexpectedly missing during normal operation.
## Expected behavior
Add `Logger.debug` calls inside the catch blocks to aid troubleshooting ETS table issues without affecting production resilience.
## Source
From technical debt audit (2026-03-12).
<!-- SECTION:DESCRIPTION:END -->
@@ -0,0 +1,39 @@
---
id: ML-76
title: Large modules approaching refactoring threshold
status: To Do
assignee: []
created_date: '2026-04-20 08:57'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/99'
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-03-05 · updated 2026-03-06 · closed 2026-03-06 · not planned_
## Priority: Low
## Description
Several modules are approaching or exceeding a reasonable size threshold and may benefit from extraction:
- `lib/music_library_web/live/artist_live/show.ex` — 804 LOC
- `lib/music_library_web/components/record_components.ex` — 761 LOC
- `lib/music_library_web/components/record_form.ex` — 649 LOC
- `lib/music_brainz/api.ex` — 564 LOC
Previously listed `lib/music_library/scrobble_rules.ex` was reduced from 819 to 543 LOC and no longer qualifies.
## Expected behavior
Monitor these modules and consider extraction when adding new features touches them. Not urgent — only refactor when it naturally fits the work being done.
## Source
From technical debt audit (2026-03-05), updated 2026-03-06.
<!-- SECTION:DESCRIPTION:END -->
@@ -0,0 +1,32 @@
---
id: ML-89
title: Random data in test fixtures
status: To Do
assignee: []
created_date: '2026-04-20 08:57'
labels: []
dependencies: []
references:
- 'https://github.com/cloud8421/music_library/issues/85'
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
_GitHub: created 2026-02-17 · updated 2026-03-06 · closed 2026-03-06 · not planned_
## Priority: Low
## Description
`test/support/fixtures/music_library/records.ex` uses `Enum.random/1` (lines 74, 91, 93) and `:rand.uniform/1` (line 86) for generating test data (artist names, titles, formats, genres). This can make test failures harder to reproduce.
## Expected behavior
Consider using deterministic sequences or seeded randomness for reproducible tests.
## Source
From technical debt audit (2026-02-17), item #12.
<!-- SECTION:DESCRIPTION:END -->