Migrate StatsLive.Index, ScrobbledTracksLive.Index, and TopAlbums to
consume matching_records list instead of scalar record IDs. Remove the
derive_legacy_record_ids bridge function. TopAlbums now uses a dropdown
for multi-record badges with play count display.
Replace scalar collected_record_id/wishlisted_record_id subqueries with
a single matching_records JSON array grouped by release group
(records.musicbrainz_id). Add parse_matching_records/1 for JSON parsing
and derive_legacy_record_ids/2 as a temporary bridge for existing
templates. Update tests to use shared musicbrainz_id fixtures.
Replaces collected_record_id and wishlisted_record_id correlated
subqueries in tracks_with_record_info_query with a single
matching_records json_group_array subquery that returns all records
sharing the same release ID. Adds parse_matching_records/1 for JSON
deserialization. Updates recent_activity/2 and list_tracks/1 to parse
the JSON at the boundary. Derives legacy collected_record_id and
wishlisted_record_id from matching_records so existing LiveViews keep
working. The get_top_albums test is temporarily skipped pending the
top_albums_attach_metadata update.
The Chat component kept stale messages and chat references
when patching to a different artist/record because update/2
only refreshed the chat list, not the active chat state.
The delta branch in decode_responses_event now explicitly returns
:ok after invoking the callback, so a callback returning {:error, _}
cannot be mistaken for a stream-level error by decode_events.
The record_count now reflects the number of grouped catalog lines
the model sees, not raw DB rows. Two copies of the same album on
different formats count as 1 catalog entry.
Mix aliases should only compose mix tasks. Shell tool orchestration
(shellcheck, fd) belongs in mise scripts. This removes the mix cmd
escape hatch and makes the boundary clear: mix.exs for pure mix
composition, scripts/ for cross-toolchain orchestration.
High priority:
- Auto-format on edit (.claude/hooks/format-elixir.sh): runs mix format
synchronously after every Edit/Write on .ex, .exs, and .heex files.
Ensures code is formatted before the next read or tool call.
- mix test permissions: added Bash(mix test) (bare) and Bash(mix test *)
(with path/flags) to the allow list. Previously only Bash(mix test:*)
was allowed, which didn't match common invocations.
- Slash commands: /precommit runs the full mix precommit pipeline and
reports failures with fix suggestions. /oban-status queries Oban job
state via Tidewave SQL, grouped by state and worker.
Medium priority:
- Chrome DevTools permissions: added 8 new tools (select_page,
list_network_requests, get_network_request, list_console_messages,
get_console_message, hover, press_key, wait_for) for debugging API
traffic, JS hook errors, hover states, and keyboard navigation.
- Gettext extraction hook (.claude/hooks/gettext-extract.sh): runs
mix gettext.extract --merge asynchronously after editing .heex files.
Async because it's project-wide and doesn't need to block the next edit.
- Sobelow permission: added Bash(mix sobelow:*) to the allow list for
independent security checks.