diff --git a/internal/db/migrations/0002_core_library.up.sql b/internal/db/migrations/0002_core_library.up.sql index f8e0cba8..80cc3fe0 100644 --- a/internal/db/migrations/0002_core_library.up.sql +++ b/internal/db/migrations/0002_core_library.up.sql @@ -1,5 +1,5 @@ --- Core library schema per spec §5. Dual-like, session, scrobble, and --- similarity tables land with their respective feature milestones. +-- Core library schema: artists, albums, tracks and users. Likes, sessions, +-- scrobbling and similarity get tables of their own in later migrations. CREATE EXTENSION IF NOT EXISTS "pgcrypto"; diff --git a/internal/db/migrations/0004_sessions.up.sql b/internal/db/migrations/0004_sessions.up.sql index ebffcece..cac6544a 100644 --- a/internal/db/migrations/0004_sessions.up.sql +++ b/internal/db/migrations/0004_sessions.up.sql @@ -1,10 +1,9 @@ -- Session tokens authenticate /api/* requests. We store sha256(token) so a -- read-only DB leak doesn't grant active sessions; the raw token lives only --- in the client's cookie (web) or bearer header (Flutter). +-- in the client's cookie (web) or bearer header (native clients). -- --- last_seen_at enables an "active sessions" UI later (not wired in this plan) --- without schema churn. user_agent is captured at issue time for the same --- reason — free metadata now, no migration later. +-- last_seen_at and user_agent (captured at issue time) are for an "active +-- sessions" list. CREATE TABLE sessions ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), diff --git a/internal/db/migrations/0005_events.up.sql b/internal/db/migrations/0005_events.up.sql index 8b17dd2f..bcfc20b0 100644 --- a/internal/db/migrations/0005_events.up.sql +++ b/internal/db/migrations/0005_events.up.sql @@ -1,11 +1,10 @@ --- M2 listening telemetry: play_sessions, play_events, skip_events. --- Note the table is named play_sessions (not sessions — that table already +-- Listening telemetry: play_sessions, play_events, skip_events. +-- The table is named play_sessions (not sessions — that table already -- exists from migration 0004 and holds HTTP auth sessions). Listening --- sessions are an entirely separate concept driven by spec §6. +-- sessions are an entirely separate concept. -- --- session_vector_at_play and contextual_likes ship as nullable now even --- though M2 writes NULL; M3 will populate them without a follow-up --- migration. +-- session_vector_at_play is nullable: plays are recorded before the +-- recommendation engine fills it. CREATE TABLE play_sessions ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), diff --git a/internal/db/migrations/0006_likes.up.sql b/internal/db/migrations/0006_likes.up.sql index fdbf33c8..9f34df31 100644 --- a/internal/db/migrations/0006_likes.up.sql +++ b/internal/db/migrations/0006_likes.up.sql @@ -1,6 +1,5 @@ --- M2 likes: three tables, one per entity type. Schema follows the spec §5 --- pattern (general_likes was originally track-only); this slice extends it --- to albums and artists for full Subsonic star/unstar support. +-- Likes: three tables, one per entity type. general_likes was track-only; +-- albums and artists get the same shape for Subsonic star/unstar. CREATE TABLE general_likes ( user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, diff --git a/internal/db/migrations/0007_contextual_likes.up.sql b/internal/db/migrations/0007_contextual_likes.up.sql index 93882b5d..69f14ae9 100644 --- a/internal/db/migrations/0007_contextual_likes.up.sql +++ b/internal/db/migrations/0007_contextual_likes.up.sql @@ -1,16 +1,12 @@ --- contextual_likes captures a per-session-context snapshot at the time of --- each like. The recommendation engine in M3 sub-plan #3 uses these rows --- to compute contextual_match_score per (current session, candidate track). +-- contextual_likes captures a per-session-context snapshot at the time of each +-- like. The recommendation engine uses these rows to compute +-- contextual_match_score per (current session, candidate track). -- -- Multiple rows per (user, track) are allowed and expected — each row -- represents a like in a specific session context. Soft-deleted rows -- remain in the table (deleted_at populated) but are filtered out of the -- engine's queries via the partial index. Re-liking a previously-unliked -- track adds a NEW row (does not undelete the old one). --- --- Note: the M2 events migration (0005) commented that contextual_likes --- "ships nullable now" but the actual CREATE TABLE was missed. This slice --- ships the table for the first time. CREATE TABLE contextual_likes ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), @@ -27,6 +23,6 @@ CREATE INDEX contextual_likes_active_idx ON contextual_likes (user_id, track_id) WHERE deleted_at IS NULL; --- Vector similarity queries from M3 sub-plan #3 will use this. +-- For the engine's vector similarity queries. CREATE INDEX contextual_likes_vector_idx ON contextual_likes USING gin (session_vector); diff --git a/internal/db/migrations/0008_scrobble.up.sql b/internal/db/migrations/0008_scrobble.up.sql index 536d268f..32f3e15a 100644 --- a/internal/db/migrations/0008_scrobble.up.sql +++ b/internal/db/migrations/0008_scrobble.up.sql @@ -1,8 +1,8 @@ --- M4a: outbound ListenBrainz scrobble worker. +-- Outbound ListenBrainz scrobble worker. -- Per-user LB config (plaintext token; users rotate via /settings if leaked). -- scrobble_queue is a work list, not a log: successfully-sent rows are --- DELETEd by the worker (the canonical record stays in play_events.scrobbled_at --- via M2's column). Only `pending` and `failed` states exist. +-- DELETEd by the worker (the canonical record stays in +-- play_events.scrobbled_at). Only `pending` and `failed` states exist. ALTER TABLE users ADD COLUMN listenbrainz_token TEXT NULL, diff --git a/internal/db/migrations/0009_similarity.up.sql b/internal/db/migrations/0009_similarity.up.sql index 8110a2bd..382631c2 100644 --- a/internal/db/migrations/0009_similarity.up.sql +++ b/internal/db/migrations/0009_similarity.up.sql @@ -1,4 +1,4 @@ --- M4b: ListenBrainz similarity ingest. Two symmetric tables — tracks and +-- ListenBrainz similarity ingest. Two symmetric tables — tracks and -- artists — both keyed by (a_id, b_id, source) so the schema reserves room -- for additional similarity sources (musicbrainz_tag, user_cooccurrence) -- alongside listenbrainz. M4b only writes 'listenbrainz' rows. diff --git a/internal/db/migrations/0010_lidarr.up.sql b/internal/db/migrations/0010_lidarr.up.sql index 8efb7b03..b2586ed1 100644 --- a/internal/db/migrations/0010_lidarr.up.sql +++ b/internal/db/migrations/0010_lidarr.up.sql @@ -1,6 +1,6 @@ --- M5a: Lidarr integration foundation. Two tables: +-- Lidarr integration. Two tables: -- --- lidarr_config — singleton (CHECK id=1) holding the operator's Lidarr +-- lidarr_config — singleton (CHECK id=1) holding the Lidarr -- connection. enabled=false is the unconfigured state. -- -- lidarr_requests — per-request lifecycle row created by users at diff --git a/internal/db/migrations/0011_lidarr_quarantine.up.sql b/internal/db/migrations/0011_lidarr_quarantine.up.sql index 8ff73259..1d2443c9 100644 --- a/internal/db/migrations/0011_lidarr_quarantine.up.sql +++ b/internal/db/migrations/0011_lidarr_quarantine.up.sql @@ -1,4 +1,4 @@ --- M5b: per-user track quarantines + admin action audit log. +-- Per-user track quarantines + admin action audit log. -- -- lidarr_quarantine — one row per (user, track) complaint. PK matches -- the general_likes pattern. Re-flagging the same track upserts. Deleted diff --git a/internal/db/migrations/0012_artist_similarity_unmatched.up.sql b/internal/db/migrations/0012_artist_similarity_unmatched.up.sql index 0d7cf1cb..b89b7bb1 100644 --- a/internal/db/migrations/0012_artist_similarity_unmatched.up.sql +++ b/internal/db/migrations/0012_artist_similarity_unmatched.up.sql @@ -1,4 +1,4 @@ --- M5c: persist unmatched-similar-artist MBIDs that the M4b worker would +-- Persist unmatched-similar-artist MBIDs that the M4b worker would -- otherwise discard. Mirrors artist_similarity shape: same composite PK -- with source, same (seed_id, score DESC) index, same source enum check. -- The candidate side is text + name (no FK) — that's the whole point. diff --git a/internal/db/migrations/0013_lidarr_metadata_profile.up.sql b/internal/db/migrations/0013_lidarr_metadata_profile.up.sql index 97ac5913..627d46f9 100644 --- a/internal/db/migrations/0013_lidarr_metadata_profile.up.sql +++ b/internal/db/migrations/0013_lidarr_metadata_profile.up.sql @@ -1,9 +1,8 @@ --- M6a follow-up: persist the operator's metadata-profile choice alongside --- quality profile + root folder. Lidarr POST /api/v1/artist requires --- metadataProfileId; without storing it we'd refetch and pick first on --- every Approve, denying the operator a choice. Nullable: existing rows --- (and operators upgrading from before this migration) get NULL and the --- service falls back to "fetch list and pick first" until they save. +-- Persist the chosen Lidarr metadata profile alongside quality profile + +-- root folder. Lidarr POST /api/v1/artist requires metadataProfileId; +-- without storing it every Approve would refetch and pick the first. +-- Nullable: existing rows get NULL and the service falls back to "fetch +-- list and pick first" until an admin saves. ALTER TABLE lidarr_config ADD COLUMN default_metadata_profile_id integer; diff --git a/internal/db/migrations/0014_playlists.up.sql b/internal/db/migrations/0014_playlists.up.sql index 21642891..6cf9d825 100644 --- a/internal/db/migrations/0014_playlists.up.sql +++ b/internal/db/migrations/0014_playlists.up.sql @@ -1,5 +1,4 @@ --- M7 #352 slice 1: playlists CRUD foundation. --- See docs/superpowers/specs/2026-05-03-m7-playlists-crud-design.md. +-- User playlists. CREATE TABLE playlists ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), diff --git a/internal/db/migrations/0015_system_playlists.up.sql b/internal/db/migrations/0015_system_playlists.up.sql index 32eb016f..7e138ec5 100644 --- a/internal/db/migrations/0015_system_playlists.up.sql +++ b/internal/db/migrations/0015_system_playlists.up.sql @@ -1,7 +1,7 @@ --- M7 #352 slice 2: system-generated playlists. +-- System-generated playlists. -- Adds kind/system_variant/seed_artist_id columns to playlists and a --- per-user run-tracking table. Backfills existing slice-1 playlists --- to kind='user'. +-- per-user run-tracking table. Backfills existing playlists to +-- kind='user'. -- Discriminator on existing playlists table. ALTER TABLE playlists diff --git a/internal/db/migrations/0016_album_cover_source.up.sql b/internal/db/migrations/0016_album_cover_source.up.sql index ff2f718f..b198c989 100644 --- a/internal/db/migrations/0016_album_cover_source.up.sql +++ b/internal/db/migrations/0016_album_cover_source.up.sql @@ -1,5 +1,5 @@ --- M7 #353: album cover origin attribution. --- 'embedded' — extracted from audio file (reserved for future slice). +-- Album cover origin attribution. +-- 'embedded' — extracted from audio file (reserved). -- 'sidecar' — found in the album directory (cover.jpg/folder.jpg/etc). -- 'mbcaa' — fetched from MusicBrainz Cover Art Archive. -- 'none' — tried, no cover available. Skip auto-retry; only manual. diff --git a/internal/db/migrations/0017_scan_runs.up.sql b/internal/db/migrations/0017_scan_runs.up.sql index e17f4048..ed3a528b 100644 --- a/internal/db/migrations/0017_scan_runs.up.sql +++ b/internal/db/migrations/0017_scan_runs.up.sql @@ -1,4 +1,4 @@ --- M7 #381: track library-scan / MBID-backfill / cover-enrich runs so the +-- Track library-scan / MBID-backfill / cover-enrich runs so the -- admin overview can show last-run results + manual trigger state. CREATE TABLE scan_runs ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), diff --git a/internal/db/migrations/0018_artist_art_and_provider_settings.up.sql b/internal/db/migrations/0018_artist_art_and_provider_settings.up.sql index 37dd3203..b1ba95ba 100644 --- a/internal/db/migrations/0018_artist_art_and_provider_settings.up.sql +++ b/internal/db/migrations/0018_artist_art_and_provider_settings.up.sql @@ -1,6 +1,5 @@ --- M7 cover-sources: schema for additional cover-art providers (TheAudioDB --- as the v1 implementation), artist art (thumb + fanart), and the --- recheck-on-source-change trigger. +-- Schema for additional cover-art providers (TheAudioDB first), artist art +-- (thumb + fanart), and the recheck-on-source-change trigger. -- (0) Extend the existing albums.cover_art_source CHECK to accept the -- new 'theaudiodb' value. The constraint was created in migration 0016 diff --git a/internal/db/migrations/0019_scan_schedule.up.sql b/internal/db/migrations/0019_scan_schedule.up.sql index 5d2b2e58..0d872edb 100644 --- a/internal/db/migrations/0019_scan_schedule.up.sql +++ b/internal/db/migrations/0019_scan_schedule.up.sql @@ -1,4 +1,4 @@ --- M7 recurring-scans: singleton table holding the operator's schedule +-- Recurring scans: singleton table holding the scan schedule -- config. mode is the discriminator: 'off' / 'interval' / 'daily' / 'weekly'. -- Per-mode fields are populated only when their mode is selected; others -- stay NULL. weekly_day uses ISO 8601: 1=Monday … 7=Sunday. diff --git a/internal/db/migrations/0020_relax_art_source_checks.up.sql b/internal/db/migrations/0020_relax_art_source_checks.up.sql index 272dfcc5..b663e06e 100644 --- a/internal/db/migrations/0020_relax_art_source_checks.up.sql +++ b/internal/db/migrations/0020_relax_art_source_checks.up.sql @@ -1,5 +1,5 @@ -- Relax cover-art and artist-art source CHECK constraints so the new --- Deezer and Last.fm providers (next slice) can persist their IDs as +-- Deezer and Last.fm providers can persist their IDs as -- accepted source values. Also add the -- last_registered_providers_hash column to cover_art_sources_meta for -- boot-time auto-bump detection (when the registered-provider set diff --git a/internal/db/migrations/0022_user_management.up.sql b/internal/db/migrations/0022_user_management.up.sql index f371d5b4..434a951d 100644 --- a/internal/db/migrations/0022_user_management.up.sql +++ b/internal/db/migrations/0022_user_management.up.sql @@ -1,14 +1,14 @@ --- User management foundation (U1 of #376): +-- User management: -- -- - users.display_name: optional human-readable name distinct from username. -- - user_invites: admin-generated tokens; user redeems during signup. -- - registration_settings (singleton): controls whether registration is -- 'invite_only' (default after first admin) or 'open' (admin can --- re-enable). The handler in U1-T2 also has a special-case for the +-- re-enable). The registration handler also has a special-case for the -- "no users yet" state where it ignores invites and lets the first -- register as admin. -- - audit_log: append-only record of admin-driven user mutations. --- No retention policy in v1. +-- No retention policy. ALTER TABLE users ADD COLUMN IF NOT EXISTS display_name text; diff --git a/internal/db/migrations/0023_user_auto_approve.up.sql b/internal/db/migrations/0023_user_auto_approve.up.sql index 9dadaae3..76f0b333 100644 --- a/internal/db/migrations/0023_user_auto_approve.up.sql +++ b/internal/db/migrations/0023_user_auto_approve.up.sql @@ -1,7 +1,5 @@ --- Adds users.auto_approve_requests for the per-user auto-approve toggle --- (#355 sub-feature surface). The handler that honors this flag in --- the request submission flow is sibling work (U2.5 follow-up); this --- migration just lands the schema + the column default. +-- Adds users.auto_approve_requests for the per-user auto-approve toggle: +-- the request submission flow approves that user's requests at once. -- -- Default false: existing users keep manual approval, opt-in via the -- admin user-management UI. diff --git a/internal/db/migrations/0025_library_changes.up.sql b/internal/db/migrations/0025_library_changes.up.sql index 770c2c8a..f4252cc4 100644 --- a/internal/db/migrations/0025_library_changes.up.sql +++ b/internal/db/migrations/0025_library_changes.up.sql @@ -1,13 +1,10 @@ -- Append-only change log for library entities. Every mutation on -- artists/albums/tracks/likes/playlists/playlist_tracks writes a row --- in the same transaction as the mutation itself. Powers the Flutter --- delta-sync endpoint (#357) — clients pass back the last-seen `id` --- as a cursor. +-- in the same transaction as the mutation itself. Powers the delta-sync +-- endpoint — clients pass back the last-seen `id` as a cursor. -- --- Retention: not capped here. If the table grows unbounded a follow-up --- adds a periodic compactor (collapse contiguous upserts on the same --- entity, drop pre-delete history). Cursor reset path (410) covers the --- compactor's edge. +-- Retention: not capped here. A cursor older than what the table keeps gets +-- the reset path (410). CREATE TABLE library_changes ( id BIGSERIAL PRIMARY KEY, diff --git a/internal/db/migrations/0026_users_timezone.up.sql b/internal/db/migrations/0026_users_timezone.up.sql index 52edc73d..ec14c39f 100644 --- a/internal/db/migrations/0026_users_timezone.up.sql +++ b/internal/db/migrations/0026_users_timezone.up.sql @@ -1,5 +1,5 @@ -- 0026_users_timezone.up.sql — per-user timezone for the system-playlist --- scheduler (#392 Half B). Default 'UTC' so existing rows + brand-new +-- scheduler. Default 'UTC' so existing rows + brand-new -- users have a safe value before their first client check-in via -- PUT /api/me/timezone. The scheduler reads this column to decide -- which IANA zone to anchor each user's daily 03:00 build to. diff --git a/internal/db/migrations/0027_system_playlist_rotation.up.sql b/internal/db/migrations/0027_system_playlist_rotation.up.sql index 3314a4ea..5c604801 100644 --- a/internal/db/migrations/0027_system_playlist_rotation.up.sql +++ b/internal/db/migrations/0027_system_playlist_rotation.up.sql @@ -1,5 +1,5 @@ -- 0027_system_playlist_rotation.up.sql — sample-history dedup for --- system playlists (Fable #415). Two parts: +-- system playlists. Two parts: -- -- 1. play_events.source: which system playlist a play originated -- from ('for_you' | 'discover' | future kinds), or NULL for any diff --git a/internal/db/migrations/0028_discovery_mix_variants.up.sql b/internal/db/migrations/0028_discovery_mix_variants.up.sql index b50d92fb..e2eee325 100644 --- a/internal/db/migrations/0028_discovery_mix_variants.up.sql +++ b/internal/db/migrations/0028_discovery_mix_variants.up.sql @@ -1,11 +1,9 @@ --- #411 R3 bugfix: the five discovery mixes (deep_cuts, rediscover, --- new_for_you, on_this_day, first_listens) were added as producers + --- registry entries but their system_variant values were never added --- to the two playlists CHECK constraints. Result: insertSystemPlaylist --- for any new mix fails with playlists_kind_variant_consistent --- (SQLSTATE 23514), which — because BuildSystemPlaylists is one --- all-or-nothing transaction — aborts the WHOLE build, so For-You / --- Discover refresh 500s and the lazy daily build fails too. +-- Before: the five discovery mixes (deep_cuts, rediscover, new_for_you, +-- on_this_day, first_listens) had producers and registry entries but no +-- system_variant values in the two playlists CHECK constraints, so inserting +-- any of them failed playlists_kind_variant_consistent (SQLSTATE 23514) — +-- and because BuildSystemPlaylists is one all-or-nothing transaction, that +-- aborts the WHOLE build. After: the constraints accept them. -- -- All five are seedless (like for_you/discover): seed_artist_id IS -- NULL. Drop + re-add both constraints with the expanded set, same diff --git a/internal/db/migrations/0030_relax_art_source_check_to_nonempty.up.sql b/internal/db/migrations/0030_relax_art_source_check_to_nonempty.up.sql index 35ca702c..7b6dd3fd 100644 --- a/internal/db/migrations/0030_relax_art_source_check_to_nonempty.up.sql +++ b/internal/db/migrations/0030_relax_art_source_check_to_nonempty.up.sql @@ -2,9 +2,8 @@ -- provider's registry ID. coverart.Register makes the provider set -- extensible, so a fixed IN-list CHECK (0016 → 0018 → 0020) required a -- schema migration for every new provider and rejected any --- out-of-list value (e.g. test stub providers). Same brittleness --- class as the discovery-mix CHECK incident (#433): a fixed-value --- CHECK fighting an extensible value set. Relax to "NULL or any +-- out-of-list value (e.g. test stub providers): a fixed-value CHECK +-- fighting an extensible value set. Relax to "NULL or any -- non-empty string" — the registry, not the schema, is the source of -- truth for valid provider IDs. diff --git a/internal/db/migrations/0031_lidarr_add_confirmed_at.up.sql b/internal/db/migrations/0031_lidarr_add_confirmed_at.up.sql index 37b05a72..b5468d98 100644 --- a/internal/db/migrations/0031_lidarr_add_confirmed_at.up.sql +++ b/internal/db/migrations/0031_lidarr_add_confirmed_at.up.sql @@ -2,7 +2,7 @@ -- for an approved request. NULL = not yet sent/confirmed; the -- reconciler idempotently (re)sends the add until this is set, then -- watches for the import. Decouples the durable admin approval from the --- best-effort Lidarr call so a transient Lidarr outage no longer forces --- a manual re-approve. +-- best-effort Lidarr call, so a transient Lidarr outage does not force a +-- manual re-approve. ALTER TABLE lidarr_requests ADD COLUMN lidarr_add_confirmed_at timestamptz; diff --git a/internal/db/migrations/0032_playback_errors.up.sql b/internal/db/migrations/0032_playback_errors.up.sql index 5f4b8d7e..12de900c 100644 --- a/internal/db/migrations/0032_playback_errors.up.sql +++ b/internal/db/migrations/0032_playback_errors.up.sql @@ -1,6 +1,6 @@ -- Client-reported playback errors. Populated by clients when a track -- fails to play (zero duration, decode error, etc.); surfaced in the --- admin /admin/playback-errors inbox so the operator can hide / delete +-- admin /admin/playback-errors inbox so an admin can hide / delete -- / re-request the offending track. -- -- resolved_at + resolved_by + resolution are NULL until an admin acts @@ -10,8 +10,8 @@ -- unresolved-queue lookup fast even as resolved history accumulates. -- -- CHECK constraints on kind/resolution gate the enum values so client --- typos can't pollute the data (per the standing rule about --- enum-CHECK whitelists needing migrations). +-- typos can't pollute the data; a new value means swapping the constraint +-- in the same migration as the code that writes it. CREATE TABLE playback_errors ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), diff --git a/internal/db/migrations/0033_drop_scan_schedule.up.sql b/internal/db/migrations/0033_drop_scan_schedule.up.sql index ffaecb1e..346ed9ca 100644 --- a/internal/db/migrations/0033_drop_scan_schedule.up.sql +++ b/internal/db/migrations/0033_drop_scan_schedule.up.sql @@ -1,4 +1,4 @@ -- Retire the configurable scan scheduler. New-file pickup is now handled by -- the fsnotify watcher + a hardcoded safety-net delta walk (cmd/minstrel), --- so the operator-configurable schedule (and its admin UI) is gone. +-- so the configurable schedule (and its admin UI) is gone. DROP TABLE IF EXISTS scan_schedule; diff --git a/internal/db/migrations/0034_you_might_like.up.sql b/internal/db/migrations/0034_you_might_like.up.sql index dc9cf162..6be652a2 100644 --- a/internal/db/migrations/0034_you_might_like.up.sql +++ b/internal/db/migrations/0034_you_might_like.up.sql @@ -1,4 +1,4 @@ --- 0034_you_might_like.up.sql — "You might like" Home rows (#790). +-- 0034_you_might_like.up.sql — "You might like" Home rows. -- -- Per-user ranked lists of IN-LIBRARY albums/artists the listener -- doesn't actively spin but is predicted to enjoy, derived from the diff --git a/internal/db/migrations/0035_taste_profile.up.sql b/internal/db/migrations/0035_taste_profile.up.sql index 595375a3..47084fa5 100644 --- a/internal/db/migrations/0035_taste_profile.up.sql +++ b/internal/db/migrations/0035_taste_profile.up.sql @@ -1,11 +1,11 @@ --- 0035_taste_profile.up.sql — per-user taste profile (#796 phase 1). +-- 0035_taste_profile.up.sql — per-user taste profile. -- -- A persistent, decaying model of each user's taste, rebuilt daily from -- graded play engagement (completion + skip position) plus explicit -- likes. Two facet tables: weighted artists and weighted genre tags. -- Weight is a signed float — positive = drawn to, negative = (passively) --- avoided; magnitude reflects decayed engagement. Phase 1 only BUILDS --- these; consumption (candidate sourcing + scoring) lands in phase 2. +-- avoided; magnitude reflects decayed engagement. Candidate sourcing and +-- scoring read them. -- -- CASCADE on user/artist deletes keeps them clean. Indexed by -- (user_id, weight DESC) to serve "top taste artists/tags" reads cheaply. diff --git a/internal/db/migrations/0036_diagnostics.up.sql b/internal/db/migrations/0036_diagnostics.up.sql index 37876893..afcab288 100644 --- a/internal/db/migrations/0036_diagnostics.up.sql +++ b/internal/db/migrations/0036_diagnostics.up.sql @@ -1,4 +1,4 @@ --- Device diagnostics / debug-reporting subsystem (M9). +-- Device diagnostics / debug-reporting subsystem. -- -- When an account has users.debug_mode_enabled = true, that account's -- client(s) stream a timeseries of device state here: connectivity + @@ -8,12 +8,12 @@ -- /admin/diagnostics view filters by account + device + time window and -- exports the slice as JSON for offline analysis. -- --- `kind` is a COARSE category gated by a CHECK whitelist; the finer --- event discriminator + all event-specific fields live in the `payload` --- jsonb. This keeps the enum small and stable so new event variants --- don't require a migration (per the enum-CHECK-whitelist rule, only a --- new *category* would). If you add a category here, mirror it in the --- handler whitelist in internal/api/diagnostics.go. +-- `kind` is a COARSE category gated by a CHECK whitelist; the finer event +-- discriminator + all event-specific fields live in the `payload` jsonb. This +-- keeps the enum small and stable so new event variants don't require a +-- migration (only a new *category* would: a new CHECK value means swapping the +-- constraint in the same migration). If you add a category here, mirror it in +-- the handler whitelist in internal/api/diagnostics.go. -- -- Two timestamps on purpose: occurred_at is the client device clock -- (when the event happened on the phone — may be skewed/wrong, e.g. in diff --git a/internal/db/migrations/0037_diagnostics_playback_kind.up.sql b/internal/db/migrations/0037_diagnostics_playback_kind.up.sql index 722d1dd6..290ee688 100644 --- a/internal/db/migrations/0037_diagnostics_playback_kind.up.sql +++ b/internal/db/migrations/0037_diagnostics_playback_kind.up.sql @@ -1,14 +1,14 @@ --- Add 'playback' to the diagnostic_events kind whitelist (M9 follow-up). +-- Add 'playback' to the diagnostic_events kind whitelist. -- -- `route` and `player_state` events fire for EVERY output route (phone -- speaker, Bluetooth, UPnP/Sonos), so bucketing them under 'upnp_sync' --- was misleading — an operator on Bluetooth earbuds saw "upnp_sync" +-- was misleading — a listener on Bluetooth earbuds produced "upnp_sync" -- rows. 'upnp_sync' now means genuinely UPnP/Sonos-specific signal -- (drops, and future resync/desync); general playback + route telemetry -- moves to the new 'playback' kind. -- --- Per the enum-CHECK-whitelist rule, the new value lands by dropping and --- re-adding the constraint in the same change. Existing rows tagged +-- The new value lands by dropping and re-adding the constraint in the same +-- migration. Existing rows tagged -- 'upnp_sync' stay as-is (debug telemetry on a 30-day retention). ALTER TABLE diagnostic_events DROP CONSTRAINT diagnostic_events_kind_check; diff --git a/internal/db/migrations/0038_for_you_pick_kind.up.sql b/internal/db/migrations/0038_for_you_pick_kind.up.sql index fdf86a39..29b582f6 100644 --- a/internal/db/migrations/0038_for_you_pick_kind.up.sql +++ b/internal/db/migrations/0038_for_you_pick_kind.up.sql @@ -1,9 +1,9 @@ --- For You exploration attribution (milestone #127 step 2, Scribe #1249). +-- For You exploration attribution. -- -- For You is deliberately composed of two populations: a head of -- top-scored similarity matches ("taste" picks) and a tail sampled from -- deeper in the candidate ranking ("fresh" picks — the freshness --- injection). Until now the metrics could only judge For You as one +-- injection). Before this the metrics could only judge For You as one -- blob, so a high skip rate couldn't be read as "the taste engine is -- missing" vs "the freshness tax is too high". -- diff --git a/internal/db/migrations/0039_pick_kind_vocabulary.up.sql b/internal/db/migrations/0039_pick_kind_vocabulary.up.sql index 36f619d2..c1502d73 100644 --- a/internal/db/migrations/0039_pick_kind_vocabulary.up.sql +++ b/internal/db/migrations/0039_pick_kind_vocabulary.up.sql @@ -1,6 +1,6 @@ --- Provenance as standard (milestone #127, Scribe #1270). +-- Provenance as standard. -- --- #1249 introduced pick_kind as a For You one-off ('taste'/'fresh'). +-- 0038 introduced pick_kind as a For You one-off ('taste'/'fresh'). -- The mechanism — stamp WHY a track is in the snapshot at build time, -- freeze it onto the play at ingestion, break it down in metrics — is -- now the standard for every system mix, so the CHECK vocabulary @@ -11,9 +11,9 @@ -- Discover's three candidate buckets — makes -- the 40/30/30 allocation measurable instead -- of a guess --- tier1 / tier2 / tier3 the tiered-eligibility ladder (project rule --- #131): tier 1 pins the mix's exact desire, --- each higher tier steps back a little. Tier +-- tier1 / tier2 / tier3 the tiered-eligibility ladder: tier 1 pins the +-- mix's exact desire, each higher tier steps +-- back a little. Tier -- provenance is how we measure what the -- step-back trades away. -- diff --git a/internal/db/migrations/0040_recommendation_tuning.up.sql b/internal/db/migrations/0040_recommendation_tuning.up.sql index 7aba7c30..3d3705d0 100644 --- a/internal/db/migrations/0040_recommendation_tuning.up.sql +++ b/internal/db/migrations/0040_recommendation_tuning.up.sql @@ -1,11 +1,10 @@ --- Recommendation tuning lab (milestone #127, Scribe #1250). +-- Recommendation tuning lab. -- -- The scoring weights move out of YAML (radio profile) and out of the -- systemMixWeights hard-code (daily_mix profile) into DB-backed --- settings with live effect — the defaults-discovery lab (decision --- #1247): the operator turns knobs here to FIND good values, which --- then get baked into shipped defaults; end users and other operators --- should never need this card. +-- settings with live effect — a lab for FINDING good values, which then +-- get baked into shipped defaults; nobody should need this card to get +-- good recommendations. -- -- Rows are seeded by the recsettings service's boot reconcile (the -- coverart SettingsService pattern), not by this migration, so shipped @@ -39,7 +38,7 @@ CREATE TABLE taste_tuning ( ); -- Every knob turn writes one audit row; the metrics trend view --- (#1251) annotates these on its timeline so cause→effect is visible +-- annotates these on its timeline so cause→effect is visible -- after a change. changes is a jsonb array of {field, old, new}. CREATE TABLE recommendation_tuning_audit ( id bigserial PRIMARY KEY, diff --git a/internal/db/migrations/0041_discover_taste_unheard.up.sql b/internal/db/migrations/0041_discover_taste_unheard.up.sql index c2cee8bd..4f8934d1 100644 --- a/internal/db/migrations/0041_discover_taste_unheard.up.sql +++ b/internal/db/migrations/0041_discover_taste_unheard.up.sql @@ -1,16 +1,13 @@ --- Discover taste-targeted novelty bucket (milestone #127, Scribe #1252/#1488). +-- Discover taste-targeted novelty bucket. -- --- The 2-week metrics review found Discover beating the manual baseline on --- skip rate — which for a discovery surface means it is playing it safe: --- on a single-user server it is effectively dormant + crude-random, and --- the per-user taste profile (n_tags, #796) went unused. This adds a +-- Without it, Discover on a single-user server is effectively dormant + +-- crude-random, and the per-user taste profile's tags go unused. This adds a -- fourth Discover candidate bucket that ranks unheard tracks by the -- user's taste-profile tag weights ("novelty that fits your vibe"), so -- its picks need a new pick_kind provenance value. -- -- Postgres CHECK whitelists can't be altered in place: DROP + re-ADD with --- the expanded list, in the same migration (family rule — new CHECK-enum --- value needs a same-change migration). +-- the expanded list, in the same migration. ALTER TABLE playlist_tracks DROP CONSTRAINT playlist_tracks_pick_kind_check; ALTER TABLE playlist_tracks diff --git a/internal/db/migrations/0042_track_tags.up.sql b/internal/db/migrations/0042_track_tags.up.sql index 212da925..8c49156d 100644 --- a/internal/db/migrations/0042_track_tags.up.sql +++ b/internal/db/migrations/0042_track_tags.up.sql @@ -1,4 +1,4 @@ --- Track-level folksonomy tags for taste enrichment (milestone #160, #1490). +-- Track-level folksonomy tags for taste enrichment. -- -- A global (not per-user) cache of style/mood tags fetched from -- MusicBrainz (keyless) + Last.fm (when a key is configured), keyed by diff --git a/internal/db/migrations/0043_tag_provider_settings.up.sql b/internal/db/migrations/0043_tag_provider_settings.up.sql index a9438bf1..f7a87cd8 100644 --- a/internal/db/migrations/0043_tag_provider_settings.up.sql +++ b/internal/db/migrations/0043_tag_provider_settings.up.sql @@ -1,4 +1,4 @@ --- Tag-enrichment provider settings (milestone #160, #1490 Step 2). +-- Tag-enrichment provider settings. -- -- Mirrors the cover-art provider-settings substrate (migration 0018) so -- adding a new tag source later is "register a provider + it auto-upserts @@ -28,8 +28,9 @@ CREATE TABLE tag_sources_meta ( INSERT INTO tag_sources_meta (id, current_version) VALUES (true, 1); -- Seed the v1 providers. MusicBrainz is the keyless always-on default; --- Last.fm ships disabled and is opt-in once the operator supplies a key --- (Step 4 Settings UI), per the no-coercive-settings rule (#26). The +-- Last.fm ships disabled and is opt-in once an admin supplies a key in +-- Settings: a feature that needs a third-party account is never on by +-- default. The -- SettingsService also upserts these at boot, so the seed is belt-and- -- suspenders / documents the intended defaults. INSERT INTO tag_provider_settings (provider_id, enabled, display_order) VALUES diff --git a/internal/db/migrations/0044_taste_tuning_enriched_tag_scale.up.sql b/internal/db/migrations/0044_taste_tuning_enriched_tag_scale.up.sql index 4b605c32..c02818b4 100644 --- a/internal/db/migrations/0044_taste_tuning_enriched_tag_scale.up.sql +++ b/internal/db/migrations/0044_taste_tuning_enriched_tag_scale.up.sql @@ -1,6 +1,6 @@ --- Expose EnrichedTagScale in the tuning lab (#1520). Adds the enriched-tag --- weight knob to the taste_tuning singleton so operators can dial how much --- folksonomy tags (#1490) count vs raw ID3 genre. DEFAULT 0.5 backfills the +-- Expose EnrichedTagScale in the tuning lab. Adds the enriched-tag +-- weight knob to the taste_tuning singleton so an admin can dial how much +-- folksonomy tags count vs raw ID3 genre. DEFAULT 0.5 backfills the -- existing row and matches taste.DefaultConfig().EnrichedTagScale. ALTER TABLE taste_tuning ADD COLUMN enriched_tag_scale double precision NOT NULL DEFAULT 0.5; diff --git a/internal/db/migrations/0045_taste_profile_eras.up.sql b/internal/db/migrations/0045_taste_profile_eras.up.sql index 54acd866..b4cdbfe1 100644 --- a/internal/db/migrations/0045_taste_profile_eras.up.sql +++ b/internal/db/migrations/0045_taste_profile_eras.up.sql @@ -1,5 +1,5 @@ --- 0045_taste_profile_eras.up.sql — era/decade taste facet (#1530, --- milestone #160 Opt 2). A third taste facet alongside artists + tags: +-- 0045_taste_profile_eras.up.sql — era/decade taste facet. A third taste +-- facet alongside artists + tags: -- signed weights over decade buckets ("1990s") derived from -- albums.release_date, rebuilt daily by internal/taste next to the -- artist/tag facets. Weight is a signed float — positive = drawn to @@ -18,8 +18,8 @@ CREATE TABLE taste_profile_eras ( CREATE INDEX taste_profile_eras_user_weight_idx ON taste_profile_eras (user_id, weight DESC); --- Era-facet build knob, tunable in the lab (mirrors enriched_tag_scale, --- #1520): scales how strongly a decade-play imprints on the profile. +-- Era-facet build knob, tunable in the lab (mirrors enriched_tag_scale): +-- scales how strongly a decade-play imprints on the profile. -- DEFAULT 0.5 backfills the existing singleton and matches -- taste.DefaultConfig().EraScale; 0 disables the era facet entirely. ALTER TABLE taste_tuning diff --git a/internal/db/migrations/0046_context_time_weight.up.sql b/internal/db/migrations/0046_context_time_weight.up.sql index 6b7b7388..f592279a 100644 --- a/internal/db/migrations/0046_context_time_weight.up.sql +++ b/internal/db/migrations/0046_context_time_weight.up.sql @@ -1,5 +1,5 @@ --- 0046_context_time_weight.up.sql — time-of-day/weekday context conditioning --- (#1531, milestone #160 Opt 3). Adds a per-profile scoring weight for the new +-- 0046_context_time_weight.up.sql — time-of-day/weekday context conditioning. +-- Adds a per-profile scoring weight for the new -- context-affinity term: how strongly a candidate is boosted when its artist's -- play history concentrates in the current daypart × weekday-type cell (in the -- user's local timezone). Mirrors the other ScoringWeights columns. @@ -7,6 +7,6 @@ -- DEFAULT 1.0 backfills both existing profile rows to a modest on-value; the -- Go ShippedRadioWeights/ShippedDailyMixWeights carry the same 1.0 so fresh -- installs seed identically. Reconcile only seeds MISSING rows (ON CONFLICT DO --- NOTHING), so existing rows rely on this DEFAULT until an operator resets. +-- NOTHING), so existing rows rely on this DEFAULT until an admin resets. ALTER TABLE recommendation_weight_profiles ADD COLUMN context_time_weight double precision NOT NULL DEFAULT 1.0; diff --git a/internal/db/migrations/0047_taste_profile_moods.up.sql b/internal/db/migrations/0047_taste_profile_moods.up.sql index 3d16c380..5fd5ffbb 100644 --- a/internal/db/migrations/0047_taste_profile_moods.up.sql +++ b/internal/db/migrations/0047_taste_profile_moods.up.sql @@ -1,11 +1,11 @@ --- 0047_taste_profile_moods.up.sql — mood taste facet (#1534, milestone #160 --- Opt 2b). A fourth taste facet alongside artists + tags + eras: signed weights --- over canonical mood buckets (melancholic / energetic / chill / …) derived --- from a track's enriched folksonomy tags (#1490) via the internal/mood --- vocabulary, rebuilt daily. Weight is a signed float — positive = drawn to --- that mood, negative = passively avoided. Coverage is partial (grows with tag --- enrichment), so it's a supplement, not a foundation. Mirrors --- taste_profile_tags: CASCADE on user delete, indexed by (user_id, weight DESC). +-- 0047_taste_profile_moods.up.sql — mood taste facet. A fourth taste facet +-- alongside artists + tags + eras: signed weights over canonical mood buckets +-- (melancholic / energetic / chill / …) derived from a track's enriched +-- folksonomy tags via the internal/mood vocabulary, rebuilt daily. Weight is a +-- signed float — positive = drawn to that mood, negative = passively avoided. +-- Coverage is partial (grows with tag enrichment), so it's a supplement, not a +-- foundation. Mirrors taste_profile_tags: CASCADE on user delete, indexed by +-- (user_id, weight DESC). CREATE TABLE taste_profile_moods ( user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, mood text NOT NULL, diff --git a/internal/db/migrations/0048_play_events_device_class.up.sql b/internal/db/migrations/0048_play_events_device_class.up.sql index d8072d25..6177f1a7 100644 --- a/internal/db/migrations/0048_play_events_device_class.up.sql +++ b/internal/db/migrations/0048_play_events_device_class.up.sql @@ -1,9 +1,9 @@ --- 0048_play_events_device_class.up.sql — device-class context conditioning --- (#1551, milestone #160 Opt 3b). Records which class of device a play came --- from (mobile / web / …), sent by the client on play-start and normalized --- server-side. Extends the #1531 time-of-day/weekday affinity cell to --- (daypart × weekday × device) for the radio path. Nullable + no CHECK: values --- are normalized in Go (avoids a CHECK-migration each time a client class is --- added, rule #36), and historical rows stay NULL → excluded from the device --- dimension (a graceful cold-start; the facet ramps as new plays land). +-- 0048_play_events_device_class.up.sql — device-class context conditioning. +-- Records which class of device a play came from (mobile / web / …), sent by +-- the client on play-start and normalized server-side. Extends the +-- time-of-day/weekday affinity cell (0046) to (daypart × weekday × device) for +-- the radio path. Nullable + no CHECK: values are normalized in Go, so a new +-- client class needs no migration, and historical rows stay NULL → excluded +-- from the device dimension (a graceful cold-start; the facet ramps as new +-- plays land). ALTER TABLE play_events ADD COLUMN device_class text; diff --git a/internal/db/migrations/0049_suggestion_snoozes.up.sql b/internal/db/migrations/0049_suggestion_snoozes.up.sql index 02b0907f..9b55ef27 100644 --- a/internal/db/migrations/0049_suggestion_snoozes.up.sql +++ b/internal/db/migrations/0049_suggestion_snoozes.up.sql @@ -1,14 +1,15 @@ -- 0049_suggestion_snoozes.up.sql — time-boxed "not right now" on a Discover --- artist suggestion (#2374, milestone #268 slice 3). +-- artist suggestion. -- --- This is NOT a dislike. Rule #101 forbids a "Not for me" / thumbs-down / --- exclusion UI, and a snooze deliberately isn't one: it records no verdict on +-- This is NOT a dislike. Minstrel has no "Not for me" / thumbs-down / +-- exclusion control — taste is learned from listening — and a snooze +-- deliberately isn't one: it records no verdict on -- the music, expires on its own, and MUST NEVER feed the taste profile. It is -- acquisition triage — "I don't want to request this right now" — so the same -- candidate is free to return once snoozed_until passes. Anything that reads -- this table as negative preference signal is a bug. -- --- Per-user (rule #47), never global: one household member parking a +-- Per-user, never global: one household member parking a -- suggestion must not remove it from anyone else's deck. -- -- candidate_mbid is text with NO foreign key, on purpose. Suggestions come diff --git a/internal/db/migrations/0050_candidate_artist_tags.up.sql b/internal/db/migrations/0050_candidate_artist_tags.up.sql index 660a3da7..72896af5 100644 --- a/internal/db/migrations/0050_candidate_artist_tags.up.sql +++ b/internal/db/migrations/0050_candidate_artist_tags.up.sql @@ -1,18 +1,17 @@ -- 0050_candidate_artist_tags.up.sql — folksonomy tags for OUT-OF-LIBRARY --- artists (#2376, milestone #268 slice 5). +-- artists. -- -- track_tags (0042) cannot hold these: it is FK'd to tracks(id), and a -- Discover candidate has no local row by definition. So this is a parallel -- cache keyed by the candidate's MusicBrainz MBID — the only stable identity -- available for an artist we don't have. -- --- Purpose is slice 6: rank suggestions by overlap between these tags and the +-- Purpose: rank Discover suggestions by overlap between these tags and the -- user's taste_profile_tags, turning "neighbour of an artist you play" into -- "matches the sound you like". -- -- GLOBAL, not per-user: a candidate's tags are a property of the artist, not --- of anyone's taste. Nothing here is user-scoped, so rule #47 has nothing to --- scope — the per-user part lives entirely in slice 6's ranking. +-- of anyone's taste. The per-user part lives entirely in the ranking. -- -- weight is a normalized folksonomy strength in [0,1], same scale as -- track_tags, so the two can be compared without a conversion step. diff --git a/internal/db/migrations/0051_discover_tuning.up.sql b/internal/db/migrations/0051_discover_tuning.up.sql index 54bc2be0..e0cdacfe 100644 --- a/internal/db/migrations/0051_discover_tuning.up.sql +++ b/internal/db/migrations/0051_discover_tuning.up.sql @@ -1,14 +1,14 @@ -- 0051_discover_tuning.up.sql — tunable knobs for the Discover request --- surface (#2377, milestone #268 slice 6). +-- surface. -- -- A FOURTH tuning scope alongside radio / daily_mix / taste. Its own scope -- rather than extra columns on taste_tuning, for a reason that matters: -- snooze_days lives here, and a snooze must never be read as taste signal --- (#2374). Filing it under 'taste' would put it one careless join away from --- exactly the leak that design forbids. +-- (0049). Filing it under 'taste' would put it one careless join away from +-- exactly that leak. -- --- Per rule #25 these are DB-backed and editable in the admin UI with no --- restart — the shipped values below are defaults, not settings. +-- These are DB-backed and editable in the admin UI with no restart — the +-- shipped values below are defaults, not settings. CREATE TABLE discover_tuning ( singleton boolean PRIMARY KEY DEFAULT true CONSTRAINT discover_tuning_singleton_check CHECK (singleton), @@ -17,10 +17,10 @@ CREATE TABLE discover_tuning ( -- [0,1]. So 0 disables the feature outright and leaves pure similarity -- ranking, 1.0 lets a perfectly-matching candidate double its score, and -- a candidate with no cached tags is unchanged rather than penalised - -- (rule #131 — tag coverage is permanently partial, see #2376). + -- (tag coverage is permanently partial, so a missing tag is no signal). tag_overlap_weight double precision NOT NULL, - -- Default snooze duration in days. Was a Go constant in - -- internal/api/suggestions.go; moved here per rule #25. + -- Default snooze duration in days. Before this a Go constant in + -- internal/api/suggestions.go; now an admin setting. snooze_days double precision NOT NULL, updated_at timestamptz NOT NULL DEFAULT now() ); diff --git a/internal/db/migrations/0052_session_ips.up.sql b/internal/db/migrations/0052_session_ips.up.sql index 1da0bf48..b461d925 100644 --- a/internal/db/migrations/0052_session_ips.up.sql +++ b/internal/db/migrations/0052_session_ips.up.sql @@ -1,4 +1,4 @@ --- Session provenance for the active-sessions surface (#370). +-- Session provenance for the active-sessions surface. -- -- TWO addresses, not one, and the pair is the point: a session created at -- home and now being used from somewhere else is the shape of a stolen diff --git a/internal/db/migrations/0053_network_settings.up.sql b/internal/db/migrations/0053_network_settings.up.sql index d53649ff..a995af37 100644 --- a/internal/db/migrations/0053_network_settings.up.sql +++ b/internal/db/migrations/0053_network_settings.up.sql @@ -1,4 +1,4 @@ --- Trusted reverse-proxy depth for client-IP extraction (#2453). +-- Trusted reverse-proxy depth for client-IP extraction. -- -- X-Forwarded-For grows left-to-right: each proxy APPENDS the peer it -- received the request from. For client -> CDN -> own-proxy -> Minstrel the @@ -11,14 +11,15 @@ -- -- Default 1: a publicly reachable Minstrel needs a TLS terminator in front of -- it, and recording that terminator's own address for every session makes the --- active-sessions surface (#370) useless — created_ip and last_ip would both +-- active-sessions surface useless — created_ip and last_ip would both -- be the proxy, so the "address changed" signal could never fire. -- -- The cost, stated on the admin card rather than buried: hops >= 1 DECLARES -- that a proxy exists. If one doesn't, a client can forge X-Forwarded-For and -- choose what its own session row shows, which defeats exactly the compromise --- detection #370 exists for. That is inherent to the trusted-hop model, which --- is why 0 is a first-class setting and not a hidden escape hatch. +-- detection the active-sessions surface exists for. That is inherent to the +-- trusted-hop model, which is why 0 is a first-class setting and not a hidden +-- escape hatch. -- -- Upper bound 10 guards a typo turning into "trust the whole header"; no real -- deployment chains ten proxies. diff --git a/internal/db/migrations/0054_track_tag_read_version.up.sql b/internal/db/migrations/0054_track_tag_read_version.up.sql index a66580b8..e9b0f3a8 100644 --- a/internal/db/migrations/0054_track_tag_read_version.up.sql +++ b/internal/db/migrations/0054_track_tag_read_version.up.sql @@ -1,11 +1,11 @@ -- Records which version of the scanner's tag-extraction logic last wrote a --- track's tag-derived columns (#2499). +-- track's tag-derived columns. -- -- DEFAULT 0 is the point of this migration: every existing row lands below the -- scanner's current library.tagReadVersion, so the next scan re-reads its tags -- instead of short-circuiting on the mtime check. That repairs genre values the -- old reader welded together ("Alternative Rock" + "Rock" -> "Alternative --- RockRock") without asking the operator to wipe and rebuild the library. +-- RockRock") without anyone wiping and rebuilding the library. -- -- Bump library.tagReadVersion in Go — not this default — whenever a tag -- extraction fix needs to reach already-indexed files. That makes tag repairs a diff --git a/internal/db/migrations/0055_track_missing_since.up.sql b/internal/db/migrations/0055_track_missing_since.up.sql index 11952ade..64cdea80 100644 --- a/internal/db/migrations/0055_track_missing_since.up.sql +++ b/internal/db/migrations/0055_track_missing_since.up.sql @@ -1,4 +1,4 @@ --- Marks a track whose file the scanner could no longer find (#2523). +-- Marks a track whose file the scanner could no longer find. -- -- NULL means present. A timestamp means the file was absent as of that scan, -- and is the point from which "how long has this been gone" is measured — which diff --git a/internal/db/migrations/0056_missing_reacquisition.up.sql b/internal/db/migrations/0056_missing_reacquisition.up.sql index a4e73b4b..175b576f 100644 --- a/internal/db/migrations/0056_missing_reacquisition.up.sql +++ b/internal/db/migrations/0056_missing_reacquisition.up.sql @@ -1,14 +1,12 @@ --- Auto re-acquisition of missing files via Lidarr (#2527 slice 3, milestone --- #290). A file going missing has been a dead end until now: reconcile marks --- it (#2523), every selection path skips it, the admin surface lists it, and --- there it sits. +-- Auto re-acquisition of missing files via Lidarr. Before this a missing +-- file was a dead end: reconcile marks it, every selection path skips it, +-- the admin surface lists it, and there it sits. -- -- The unit here is the ALBUM, not the track, and that is the design decision -- carrying most of the safety. Lidarr acquires releases; there is no -- meaningful "fetch me one track" operation, and a track-kind request needs a -- recording MBID plenty of files simply don't have. Grouping by album means --- the case that produced #2523 -- three reorganised albums, ~40 missing files --- -- becomes three requests instead of forty. +-- a reorganised album with dozens of missing files becomes one request. CREATE TABLE missing_reacquisitions ( album_id uuid PRIMARY KEY REFERENCES albums (id) ON DELETE CASCADE, @@ -36,12 +34,13 @@ CREATE INDEX missing_reacquisitions_due_idx WHERE gave_up_at IS NULL; -- Settings, singleton in the style of network_settings (0053). Every value an --- operator might want to tune lives here rather than in YAML (rule #25). +-- admin might want to tune lives here rather than in YAML, changed without a +-- restart. CREATE TABLE reacquisition_settings ( id boolean PRIMARY KEY DEFAULT true, - -- Master switch. Default true: the operator asked for this to happen by - -- itself, and a feature that ships switched off is a feature nobody finds. + -- Master switch. Default true: this is meant to happen by itself, and a + -- feature that ships switched off is a feature nobody finds. enabled boolean NOT NULL DEFAULT true, -- How long a file must have been missing before the FIRST attempt. This @@ -49,7 +48,7 @@ CREATE TABLE reacquisition_settings ( -- transiently -- an unmounted volume, a container that started before its -- media mount attached, a NAS mid-reboot -- and every one of those -- resolves itself well inside a day at no cost. tracks.missing_since is - -- never re-stamped (#2523), so it is a true "gone since" clock to measure + -- never re-stamped, so it is a true "gone since" clock to measure -- against. grace_hours int NOT NULL DEFAULT 24, diff --git a/internal/db/migrations/0057_songs_like_tuning.up.sql b/internal/db/migrations/0057_songs_like_tuning.up.sql index cf8d6c1e..c8071e66 100644 --- a/internal/db/migrations/0057_songs_like_tuning.up.sql +++ b/internal/db/migrations/0057_songs_like_tuning.up.sql @@ -1,13 +1,9 @@ --- 0057_songs_like_tuning.up.sql — a THIRD weight profile, for Songs-like --- (Scribe #3881, milestone #398). +-- 0057_songs_like_tuning.up.sql — a THIRD weight profile, for Songs-like. -- --- Songs-like shared the `daily_mix` profile with For-You, and that is the bug. --- The two surfaces want opposite things: For-You is a broad "what will they --- enjoy today", Songs-like answers "what sounds like THIS", and under one set --- of weights the broad answer wins. Operator, 2026-09-10: "I'm expecting to --- get a consistent sound and style from the experience... I was getting a --- seeming wide variety of music from each one when I was hoping to stay in a --- certain neighborhood." +-- Before: Songs-like shared the `daily_mix` profile with For-You. The two +-- surfaces want opposite things: For-You is a broad "what will they enjoy +-- today", Songs-like answers "what sounds like THIS" and should stay in one +-- neighbourhood of sound, and under one set of weights the broad answer wins. -- -- Under the shared daily_mix weights, an UNRELATED track the user had liked -- and not played recently scored 1.0 + 2.0 + 1.0 = 4.0 before taste, while a diff --git a/internal/db/migrations/0058_track_fingerprints.up.sql b/internal/db/migrations/0058_track_fingerprints.up.sql index aa0f8c9c..a42e6151 100644 --- a/internal/db/migrations/0058_track_fingerprints.up.sql +++ b/internal/db/migrations/0058_track_fingerprints.up.sql @@ -1,5 +1,4 @@ --- 0058_track_fingerprints.up.sql — an acoustic identity per track (Scribe --- milestone #400: #3905, #3906). +-- 0058_track_fingerprints.up.sql — an acoustic identity per track. -- -- A table of its own rather than columns on tracks, for the hot path's sake: -- tracks is read with SELECT * by eight queries, among them ListTracksByAlbum, @@ -16,9 +15,9 @@ -- A failure that says nothing about the file — a timeout, a cancelled scan, a -- missing binary — writes no row at all, so the backfill tries again. CREATE TABLE track_fingerprints ( - -- CASCADE is right here, unlike for the likes and play history M400's - -- merge has to carry across: a fingerprint describes one file's bytes and - -- means nothing once that file's row is gone. + -- CASCADE is right here, unlike for the likes and play history the + -- duplicate merge has to carry across: a fingerprint describes one file's + -- bytes and means nothing once that file's row is gone. track_id uuid PRIMARY KEY REFERENCES tracks (id) ON DELETE CASCADE, -- SHA-256 of the ENCODED audio packets (ffmpeg -c:a copy -f hash), not of -- decoded samples. internal/library/fingerprint.go says why. diff --git a/internal/db/migrations/0059_duplicate_groups.up.sql b/internal/db/migrations/0059_duplicate_groups.up.sql index 17a112b5..67073cd6 100644 --- a/internal/db/migrations/0059_duplicate_groups.up.sql +++ b/internal/db/migrations/0059_duplicate_groups.up.sql @@ -1,9 +1,9 @@ -- 0059_duplicate_groups.up.sql — proposed duplicates and the sweeps that find --- them (Scribe milestone #400: #3910). +-- them. -- --- The sweep compares fingerprints (track_fingerprints, 0058) and proposes groups --- of tracks that hold one recording. Nothing here merges anything: a group is a --- proposal the operator reviews, and the merge (#3911) is a separate act. +-- The sweep compares fingerprints (track_fingerprints, 0058) and proposes +-- groups of tracks that hold one recording. Nothing here merges anything: a +-- group is a proposal an admin reviews, and the merge is a separate act. -- One row per sweep. Lets the report tell "the sweep has never run" apart from -- "it ran and found nothing", and gives the in-flight guard something to check, @@ -49,6 +49,6 @@ CREATE TABLE duplicate_group_members ( CREATE INDEX duplicate_group_members_track_idx ON duplicate_group_members (track_id); -- The sweep streams candidates in (duration_ms, id) order, keyset-paged, so it --- only ever holds a few seconds' worth of durations in memory. Without this each --- page would sort the whole library again. +-- only ever holds a few seconds' worth of durations in memory. Without this +-- each page would sort the whole library again. CREATE INDEX tracks_duration_id_idx ON tracks (duration_ms, id); diff --git a/internal/db/migrations/0060_play_events_track_index.up.sql b/internal/db/migrations/0060_play_events_track_index.up.sql index 35036982..2c19bd36 100644 --- a/internal/db/migrations/0060_play_events_track_index.up.sql +++ b/internal/db/migrations/0060_play_events_track_index.up.sql @@ -1,8 +1,8 @@ --- 0060_play_events_track_index.up.sql — play_events by track (Scribe #3912, #3911). +-- 0060_play_events_track_index.up.sql — play_events by track. -- -- play_events is indexed by (user_id, started_at) and (user_id, track_id), both -- led by user. Nothing reached it by track alone until the duplicates report, -- which shows each copy's play count — a scan of the whole table per copy — and --- the merge (#3911), which repoints a duplicate's play history onto the copy +-- the duplicate merge, which repoints a duplicate's play history onto the copy -- being kept. Both ask "every play of this track", whoever played it. CREATE INDEX play_events_track_idx ON play_events (track_id); diff --git a/internal/db/migrations/0061_fingerprint_settings.up.sql b/internal/db/migrations/0061_fingerprint_settings.up.sql index 021f85af..89911d69 100644 --- a/internal/db/migrations/0061_fingerprint_settings.up.sql +++ b/internal/db/migrations/0061_fingerprint_settings.up.sql @@ -1,20 +1,18 @@ --- 0061_fingerprint_settings.up.sql — fingerprinting's knobs, in admin Settings --- (Scribe #3913, milestone #400). Rule 25: anything an operator might tune is a --- database row, changed without a restart. Singleton in the style of --- reacquisition_settings (0056). +-- 0061_fingerprint_settings.up.sql — fingerprinting's knobs, in admin +-- Settings: anything an admin might tune is a database row, changed without a +-- restart. Singleton in the style of reacquisition_settings (0056). CREATE TABLE fingerprint_settings ( id boolean PRIMARY KEY DEFAULT true, - -- Fingerprinting new files, the backfill, and the duplicate sweep. Off stops - -- the decode work entirely — the reason to turn it off is a slow NAS, and - -- that is the operator's call. On by default: a library that cannot tell its - -- duplicates apart is what milestone #400 exists to end. + -- Fingerprinting new files, the backfill, and the duplicate sweep. Off + -- stops the decode work entirely — the reason to turn it off is a slow NAS. + -- On by default: without it the library cannot tell its duplicates apart. enabled boolean NOT NULL DEFAULT true, -- Seconds of audio fpcalc fingerprints. Chromaprints taken at different -- lengths cannot be compared, which is why track_fingerprints records the - -- length each row was taken at (below): change this and every chromaprint is - -- re-derived, and until then only rows at the new length are compared. + -- length each row was taken at (below): change this and every chromaprint + -- is re-derived, and until then only rows at the new length are compared. chromaprint_length_sec integer NOT NULL DEFAULT 120, -- The most disagreement two aligned fingerprints may show and still be diff --git a/internal/db/migrations/0062_network_public_url.up.sql b/internal/db/migrations/0062_network_public_url.up.sql index 0d1f6dac..4eb7dee8 100644 --- a/internal/db/migrations/0062_network_public_url.up.sql +++ b/internal/db/migrations/0062_network_public_url.up.sql @@ -1,8 +1,8 @@ -- The address users reach Minstrel at, e.g. https://music.example.com. -- --- Password-reset links used to be built from the request's Host header, --- which the requester controls: a forgot-password call with a forged Host +-- Before: password-reset links were built from the request's Host header, +-- which the requester controls, so a forgot-password call with a forged Host -- would email the victim a real reset token on a link to the attacker's --- server. Links are now built only from this operator-set value, and none --- are sent while it is empty (M462 #4981). +-- server. After: links are built only from this admin-set value, and none +-- are sent while it is empty. ALTER TABLE network_settings ADD COLUMN public_url text NOT NULL DEFAULT ''; diff --git a/internal/db/migrations/0063_api_token_hash.up.sql b/internal/db/migrations/0063_api_token_hash.up.sql index fd161a19..646d6b18 100644 --- a/internal/db/migrations/0063_api_token_hash.up.sql +++ b/internal/db/migrations/0063_api_token_hash.up.sql @@ -2,7 +2,7 @@ -- same way session tokens are. A leaked database row or backup no longer -- hands out working keys. Existing keys are hashed in place, so every -- Subsonic client keeps working; the key itself can no longer be shown --- again, only replaced (M462 #4983). +-- again, only replaced. ALTER TABLE users ADD COLUMN api_token_hash text; UPDATE users SET api_token_hash = encode(sha256(convert_to(api_token, 'UTF8')), 'hex'); ALTER TABLE users ALTER COLUMN api_token_hash SET NOT NULL; diff --git a/internal/db/migrations/0064_clear_subsonic_password.up.sql b/internal/db/migrations/0064_clear_subsonic_password.up.sql index ab83c2b4..bcb885bc 100644 --- a/internal/db/migrations/0064_clear_subsonic_password.up.sql +++ b/internal/db/migrations/0064_clear_subsonic_password.up.sql @@ -1,9 +1,9 @@ --- `minstrel admin reset-password` used to copy the new login password into +-- Before: `minstrel admin reset-password` copied the new login password into -- subsonic_password, so every account recovered through the CLI had its login -- password stored in plain text, and a later password change in Settings left --- that copy behind (M462 #5026). The CLI no longer writes this column; a --- Subsonic password is now generated separately in Settings and is never the --- login password. Clearing every value here removes the copies already made. +-- that copy behind. After: the CLI does not write this column; a Subsonic +-- password is generated separately in Settings and is never the login +-- password. Clearing every value here removes the copies already made. -- -- Accounts whose Subsonic client signs in with t/s stop working until the user -- generates a Subsonic password (or switches the client to an API key). diff --git a/internal/db/migrations/0065_track_loudness.up.sql b/internal/db/migrations/0065_track_loudness.up.sql index ae039dc6..17b46ace 100644 --- a/internal/db/migrations/0065_track_loudness.up.sql +++ b/internal/db/migrations/0065_track_loudness.up.sql @@ -1,5 +1,5 @@ -- 0065_track_loudness.up.sql — measured loudness per track, for loudness --- normalization (Scribe milestone #464, #4995). +-- normalization. -- -- Measured with ffmpeg's EBU R128 filter rather than read from ReplayGain tags: -- tags in the wild are written against four different reference levels, and @@ -32,9 +32,9 @@ CREATE TABLE track_loudness ( loudness_range_lu real, -- How many 400 ms gating blocks fell in each 0.1 LU bin. Bin i holds blocks -- measuring -70.0 + (block_hist_start + i) / 10 LUFS; the array is trimmed - -- to the first and last non-empty bins. Album loudness is the gated loudness - -- of every block on the album, so it is computed from these exactly, with no - -- second decode (#4996). + -- to the first and last non-empty bins. Album loudness is the gated + -- loudness of every block on the album, so it is computed from these + -- exactly, with no second decode. block_hist_start smallint, block_hist integer[], unreadable boolean NOT NULL DEFAULT false, diff --git a/internal/db/migrations/0066_album_loudness.up.sql b/internal/db/migrations/0066_album_loudness.up.sql index 9243dcb9..0e04820f 100644 --- a/internal/db/migrations/0066_album_loudness.up.sql +++ b/internal/db/migrations/0066_album_loudness.up.sql @@ -1,5 +1,4 @@ --- 0066_album_loudness.up.sql — album loudness for album-mode normalization --- (Scribe milestone #464, #4996). +-- 0066_album_loudness.up.sql — album loudness for album-mode normalization. -- -- An album's loudness is the gated loudness of every 400 ms block on the album, -- not an average of its tracks' values: a quiet interlude and a loud single diff --git a/internal/db/migrations/0067_user_normalization_prefs.up.sql b/internal/db/migrations/0067_user_normalization_prefs.up.sql index 30163933..4f767df7 100644 --- a/internal/db/migrations/0067_user_normalization_prefs.up.sql +++ b/internal/db/migrations/0067_user_normalization_prefs.up.sql @@ -1,4 +1,4 @@ --- Each user's loudness-normalization preference (M464 #4998). It is kept on +-- Each user's loudness-normalization preference. It is kept on -- the server, not per device, so the web player, the Android app and the -- leveled stream for UPnP/Sonos all apply the same one. -- diff --git a/internal/db/migrations/0068_leveled_stream_cache.up.sql b/internal/db/migrations/0068_leveled_stream_cache.up.sql index 941bd128..84293517 100644 --- a/internal/db/migrations/0068_leveled_stream_cache.up.sql +++ b/internal/db/migrations/0068_leveled_stream_cache.up.sql @@ -1,11 +1,10 @@ -- 0068_leveled_stream_cache.up.sql — the size cap of the leveled-stream render --- cache (Scribe milestone #464, #5001). +-- cache. -- -- Sonos and other UPnP renderers fetch a track's URL themselves, so the phone -- cannot level what they play. The server renders a gain-applied FLAC for them -- instead and keeps it on disk, oldest-used first out once the cache is over --- this size. An operator setting (rule 25), on the loudness settings row it --- belongs with. +-- this size. An admin setting, on the loudness settings row it belongs with. ALTER TABLE loudness_settings ADD COLUMN leveled_cache_mb integer NOT NULL DEFAULT 2048, ADD CONSTRAINT loudness_settings_leveled_cache_range diff --git a/internal/db/migrations/0069_acoustid_lookup.up.sql b/internal/db/migrations/0069_acoustid_lookup.up.sql index 7610c8f4..ecd56c99 100644 --- a/internal/db/migrations/0069_acoustid_lookup.up.sql +++ b/internal/db/migrations/0069_acoustid_lookup.up.sql @@ -1,11 +1,11 @@ -- 0069_acoustid_lookup.up.sql — recording MBIDs looked up through AcoustID --- for tracks whose tags carry none (Scribe milestone #401). +-- for tracks whose tags carry none. -- -- A track with no recording MBID is invisible to the ListenBrainz similarity -- arm in both directions, so this fills the ids the tags leave empty. --- Where tracks.mbid came from. The file's own tag always outranks a lookup --- (M401 D4): the scan's upsert keeps a looked-up id while the tag carries +-- Where tracks.mbid came from. The file's own tag always outranks a lookup: +-- the scan's upsert keeps a looked-up id while the tag carries -- none, and replaces it the moment the tag carries one. ALTER TABLE tracks ADD COLUMN mbid_source text; -- Every id stored so far was read from a tag (the scan or the tag backfill), @@ -25,7 +25,7 @@ ALTER TABLE tracks -- after disambiguating by title and length; recording_mbid is it -- ambiguous several recordings cleared it and nothing told them apart. -- Nothing is written: a wrong MBID would feed similarity the --- wrong neighbours, which is worse than none (M401 D5) +-- wrong neighbours, which is worse than none -- no_match AcoustID linked nothing at or above the threshold -- failed fpcalc rejected the file, or AcoustID rejected its print -- Every state is settled until the file changes. A failure that says nothing @@ -50,12 +50,12 @@ CREATE TABLE track_acoustid_lookups ( ); CREATE INDEX track_acoustid_lookups_state ON track_acoustid_lookups (state); --- AcoustID's knobs, in admin Settings (rule 25). Singleton in the style of +-- AcoustID's knobs, in admin Settings. Singleton in the style of -- loudness_settings (0065). CREATE TABLE acoustid_settings ( id boolean PRIMARY KEY DEFAULT true, - -- Off by default (rule 26): AcoustID needs a key the operator registers, - -- and the lookup sends fingerprints to a third party. + -- Off by default: AcoustID needs a key an admin registers, and the lookup + -- sends fingerprints to a third party. enabled boolean NOT NULL DEFAULT false, api_key text, -- The score a recording needs before it is considered at all. AcoustID diff --git a/internal/db/migrations/0070_album_release_group_mbid.up.sql b/internal/db/migrations/0070_album_release_group_mbid.up.sql index 08a85f2e..e6068030 100644 --- a/internal/db/migrations/0070_album_release_group_mbid.up.sql +++ b/internal/db/migrations/0070_album_release_group_mbid.up.sql @@ -1,4 +1,4 @@ --- MusicBrainz release-group id for an album (#5241). albums.mbid holds the +-- MusicBrainz release-group id for an album. albums.mbid holds the -- *release* id (Picard's musicbrainz_albumid: one edition), but Lidarr names -- albums by release group, so re-acquisition and request completion need -- this one. Not unique: several releases of one group can each be an album diff --git a/internal/db/migrations/0071_reopen_false_reacquisition_completions.up.sql b/internal/db/migrations/0071_reopen_false_reacquisition_completions.up.sql index dd240a0b..e7e18c49 100644 --- a/internal/db/migrations/0071_reopen_false_reacquisition_completions.up.sql +++ b/internal/db/migrations/0071_reopen_false_reacquisition_completions.up.sql @@ -1,10 +1,10 @@ --- #5263: the reconciler completed album and track requests as soon as the album --- had any track on disk. Re-acquisition targets albums with only SOME tracks --- missing, so those requests completed the moment Lidarr accepted the add, --- with nothing downloaded. +-- Before: the reconciler completed album and track requests as soon as the +-- album had any track on disk. Re-acquisition targets albums with only SOME +-- tracks missing, so those requests completed the moment Lidarr accepted the +-- add, with nothing downloaded. -- -- Reopen every completed album/track request whose matched album does not meet --- the corrected test (see albumForRequest in internal/lidarrrequests): a track +-- the corrected test: a track -- that arrived after the request, or no track that was already missing at the -- request still missing. The reconciler then judges them again; the Lidarr add -- stays confirmed, so nothing is re-sent. Requests that genuinely completed diff --git a/internal/db/migrations/0072_similarity_cache.up.sql b/internal/db/migrations/0072_similarity_cache.up.sql index c23ca242..f11866f0 100644 --- a/internal/db/migrations/0072_similarity_cache.up.sql +++ b/internal/db/migrations/0072_similarity_cache.up.sql @@ -1,7 +1,7 @@ --- #5296: keep ListenBrainz's whole similar-recordings answer, and record every +-- Keep ListenBrainz's whole similar-recordings answer, and record every -- fetch, so the library match is made locally and can be made again. -- --- Until now the worker kept only the recordings already in the library (at most +-- Before: the worker kept only the recordings already in the library (at most -- 20 of LB's 50) and threw the rest away. Two things followed: -- -- * A recording that reached the library later — a Lidarr import, or an MBID @@ -9,8 +9,8 @@ -- fetched again. -- * A seed with no in-library match wrote nothing at all, and freshness was -- read from track_similarity, so that seed was never fresh. With the queue --- ordered by id, 25 such seeds held its head and were re-asked every hour --- while 2,449 others waited (#3879). +-- ordered by id, such seeds held its head and were re-asked every hour +-- while the rest waited. -- -- The listenbrainz rows in track_similarity are now derived from this cache by -- ResolveListenBrainzTrackEdges. diff --git a/internal/db/migrations/0073_user_notifications.up.sql b/internal/db/migrations/0073_user_notifications.up.sql index 945bb0d1..ea6fdf9e 100644 --- a/internal/db/migrations/0073_user_notifications.up.sql +++ b/internal/db/migrations/0073_user_notifications.up.sql @@ -1,11 +1,11 @@ --- M489: a persistent, per-user notifications inbox (#726). +-- A persistent, per-user notifications inbox. -- -- The event bus is fire-and-forget: a client that is not connected when a -- request completes, or when the scanner marks tracks missing, never hears of -- it. A row here is the durable record; the bus only nudges open clients to -- come and read it. -- --- `kind` is CHECK-gated (rule 36). A new kind adds its value here, in the +-- `kind` is CHECK-gated. A new kind adds its value here, in the -- same migration as the code that produces it. The list is mirrored in -- internal/notifications/kinds.go, and both CHECKs below must agree with it. diff --git a/internal/db/migrations/0074_notification_email.up.sql b/internal/db/migrations/0074_notification_email.up.sql index f1129e66..917fd02a 100644 --- a/internal/db/migrations/0074_notification_email.up.sql +++ b/internal/db/migrations/0074_notification_email.up.sql @@ -1,11 +1,11 @@ --- M489 #5346: notifications by email, grouped. Nothing is emailed per event. +-- Notifications by email, grouped. Nothing is emailed per event. -- -- New music (request_completed) goes out as at most one summary a day, at a set -- hour in each user's own timezone. Everything else is batched: a batch opens -- at the first un-emailed item and one email goes out a window later, holding -- whatever accumulated. internal/notifications/digest.go is the one sender. --- The two knobs, in admin Settings (rule 25). Singleton in the style of +-- The two knobs, in admin Settings. Singleton in the style of -- fingerprint_settings (0061). CREATE TABLE notification_email_settings ( id boolean PRIMARY KEY DEFAULT true, diff --git a/internal/db/migrations/0075_duplicate_resolve.up.sql b/internal/db/migrations/0075_duplicate_resolve.up.sql index 29431611..95b958da 100644 --- a/internal/db/migrations/0075_duplicate_resolve.up.sql +++ b/internal/db/migrations/0075_duplicate_resolve.up.sql @@ -1,30 +1,30 @@ --- 0075_duplicate_resolve.up.sql — duplicates resolve themselves (Scribe --- milestone #498: #5435, #5436, #5437). +-- 0075_duplicate_resolve.up.sql — duplicates resolve themselves. -- --- The sweep (0059) proposes groups and, until now, every proposal waited for the --- operator. The resolver classifies each pending group and acts on the classes --- that are safe. What makes deletion safe is Lidarr's view of the file: Lidarr --- maps one file to each track of the release it monitors, so deleting a mapped --- file opens a hole it fills by downloading again. Only a copy Lidarr does not --- map may go. +-- Before: the sweep (0059) proposed groups and every proposal waited for an +-- admin. After: the resolver classifies each pending group and acts on the +-- classes that are safe. What makes deletion safe is Lidarr's view of the file: +-- Lidarr maps one file to each track of the release it monitors, so deleting a +-- mapped file opens a hole it fills by downloading again. Only a copy Lidarr +-- does not map may go. -- What kind of duplicate a group is. NULL until the resolver has classified it. -- same_release every copy is on one album -- cross_release the same song on different releases (a single and its album) -- mismatch identical audio under different titles on different albums: -- a file imported as the wrong song --- review anything else: the operator decides --- Rule 36: a new value swaps this constraint in the same migration. +-- review anything else: an admin decides +-- A new value swaps this constraint in the same migration. ALTER TABLE duplicate_groups ADD COLUMN class text; ALTER TABLE duplicate_groups ADD CONSTRAINT duplicate_groups_class_check CHECK (class IN ('same_release', 'cross_release', 'mismatch', 'review')); --- Why the resolver left a group for the operator, in words the report shows --- ("Lidarr tracks every copy as its own track"). NULL when it had nothing to say. +-- Why the resolver left a group for an admin, in words the report shows +-- ("Lidarr tracks every copy as its own track"). NULL when it had nothing to +-- say. ALTER TABLE duplicate_groups ADD COLUMN resolve_note text; -- True when the resolver merged the group itself, so the report can tell its --- merges from the operator's. +-- merges from an admin's. ALTER TABLE duplicate_groups ADD COLUMN resolved_automatically boolean NOT NULL DEFAULT false; -- Lidarr's view of each copy, as of the resolver's last pass: @@ -36,12 +36,12 @@ ALTER TABLE duplicate_group_members ADD COLUMN lidarr_state text; ALTER TABLE duplicate_group_members ADD CONSTRAINT duplicate_group_members_lidarr_state_check CHECK (lidarr_state IN ('tracked', 'unmapped')); --- Rule 25: the operator can turn the resolver off. On by default: the operator --- asked for duplicates to resolve themselves (2026-10-09). +-- An admin can turn the resolver off. On by default: duplicates are meant to +-- resolve themselves. ALTER TABLE fingerprint_settings ADD COLUMN auto_resolve boolean NOT NULL DEFAULT true; --- The resolver tells admins what it did (rule 36: the kind CHECKs of 0073 are --- swapped to carry the new value). Postgres named both from their columns. +-- The resolver tells admins what it did (the kind CHECKs of 0073 are swapped +-- to carry the new value). Postgres named both from their columns. ALTER TABLE user_notifications DROP CONSTRAINT user_notifications_kind_check; ALTER TABLE user_notifications ADD CONSTRAINT user_notifications_kind_check CHECK (kind IN ('request_approved', 'request_rejected', 'request_completed', diff --git a/internal/db/migrations/0076_track_song_link.up.sql b/internal/db/migrations/0076_track_song_link.up.sql index b314512c..94789e09 100644 --- a/internal/db/migrations/0076_track_song_link.up.sql +++ b/internal/db/migrations/0076_track_song_link.up.sql @@ -1,5 +1,5 @@ -- 0076_track_song_link.up.sql — the same song on several releases counts as --- one song (Scribe milestone #498, #5438; operator decision D-b). +-- one song. -- -- A single and the album it came from each hold the song, and Lidarr keeps -- both: each fulfils its own release. Minstrel keeps both files and links diff --git a/internal/db/migrations/0077_track_source_verdict.up.sql b/internal/db/migrations/0077_track_source_verdict.up.sql index c1361d5f..529b5d3e 100644 --- a/internal/db/migrations/0077_track_source_verdict.up.sql +++ b/internal/db/migrations/0077_track_source_verdict.up.sql @@ -1,13 +1,12 @@ -- 0077_track_source_verdict.up.sql — video rips are held back from mixes and --- radio until the operator says otherwise (Scribe milestone #498, #5439; --- operator decision D-d). +-- radio until an admin says otherwise. -- --- A file whose name carries a video-rip marker ("(Official Video)", "[HD]", --- #5410) holds the audio of an upload, which can carry intros, skits or a +-- A file whose name carries a video-rip marker ("(Official Video)", "[HD]") +-- holds the audio of an upload, which can carry intros, skits or a -- different take. The duplicate resolver sets 'suspect' on such a present -- track each pass, and clears it when the name no longer matches. 'suspect' -- keeps the track out of radio and the system mixes; it still plays when --- chosen. 'fine' is the operator's "this one is fine" and is never +-- chosen. 'fine' is an admin's "this one is fine" and is never -- overwritten by the resolver. ALTER TABLE tracks ADD COLUMN source_verdict text CONSTRAINT tracks_source_verdict_check CHECK (source_verdict IN ('suspect', 'fine')); diff --git a/internal/db/migrations/0078_drop_duplicate_lidarr_state.up.sql b/internal/db/migrations/0078_drop_duplicate_lidarr_state.up.sql index a7c2637d..943423a1 100644 --- a/internal/db/migrations/0078_drop_duplicate_lidarr_state.up.sql +++ b/internal/db/migrations/0078_drop_duplicate_lidarr_state.up.sql @@ -1,5 +1,5 @@ -- 0078_drop_duplicate_lidarr_state.up.sql — the duplicate resolver works --- without Lidarr (Scribe milestone #502, #5517). +-- without Lidarr. -- -- 0075 recorded Lidarr's view of each duplicate copy (tracked or unmapped) -- because a copy Lidarr mapped could not be removed without Lidarr downloading diff --git a/internal/db/migrations/0079_acquisition_services.up.sql b/internal/db/migrations/0079_acquisition_services.up.sql index b3b1bc66..b4487dac 100644 --- a/internal/db/migrations/0079_acquisition_services.up.sql +++ b/internal/db/migrations/0079_acquisition_services.up.sql @@ -1,11 +1,9 @@ -- 0079_acquisition_services.up.sql — the indexers and download clients the --- operator adds in Admin → Integrations (Scribe milestone #502, #5518). +-- admin adds in Admin → Integrations. -- -- Minstrel replaces Lidarr, so it searches and downloads itself. Nothing is --- seeded and nothing is copied from Lidarr: a connection is a row the operator --- adds, from a list of what Minstrel can speak. Ported from Fabled --- Librarian's acquisition_services (its migrations 0044, 0055, 0062, 0064, --- 0070), which settled the shape: +-- seeded and nothing is copied from Lidarr: a connection is a row an admin +-- adds, from a list of what Minstrel can speak: -- -- kind which list the row appears in: search (Indexers) or download -- (Download clients). @@ -32,7 +30,7 @@ CREATE TABLE acquisition_services ( label text NOT NULL, -- A new row is off: it has no address yet. enabled boolean NOT NULL DEFAULT false, - -- The address as the operator's browser opens it. For a Torznab feed it + -- The address as an admin's browser opens it. For a Torznab feed it -- is the whole feed URL up to and including /api. base_url text NOT NULL DEFAULT '', api_key text NOT NULL DEFAULT '', diff --git a/internal/db/migrations/0080_release_catalogue.up.sql b/internal/db/migrations/0080_release_catalogue.up.sql index 7f010aea..88e3086c 100644 --- a/internal/db/migrations/0080_release_catalogue.up.sql +++ b/internal/db/migrations/0080_release_catalogue.up.sql @@ -1,5 +1,5 @@ -- 0080_release_catalogue.up.sql — Minstrel's own copy of what artists have --- released, read from MusicBrainz (Scribe milestone #502, #5519). +-- released, read from MusicBrainz. -- -- With Lidarr gone, the wanted list, search and import need to know an -- artist's discography and each release's track list. MusicBrainz allows one diff --git a/internal/db/migrations/0081_wanted.up.sql b/internal/db/migrations/0081_wanted.up.sql index 0c52f6ab..d358ab3d 100644 --- a/internal/db/migrations/0081_wanted.up.sql +++ b/internal/db/migrations/0081_wanted.up.sql @@ -1,4 +1,4 @@ --- 0081_wanted.up.sql — the wanted list (Scribe milestone #502, #5520). +-- 0081_wanted.up.sql — the wanted list. -- -- With Lidarr gone, Minstrel keeps its own record of what it is looking for: -- @@ -19,7 +19,7 @@ -- lidarr_requests becomes requests with no Lidarr columns: approving a request -- now records a want or a follow instead of sending an add to Lidarr. The -- missing-file sweeper's attempt bookkeeping goes too; retrying belongs to the --- search schedule (#5521), which works per want. +-- search schedule, which works per want. CREATE TABLE release_type_profiles ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), @@ -36,8 +36,8 @@ CREATE TABLE release_type_profiles ( CREATE UNIQUE INDEX release_type_profiles_one_default ON release_type_profiles (is_default) WHERE is_default; --- Lidarr's "Standard" metadata profile, which every followed artist used --- (note #5527): albums, EPs and singles, studio releases only. +-- Lidarr's "Standard" metadata profile, which followed artists used: albums, +-- EPs and singles, studio releases only. INSERT INTO release_type_profiles (name, primary_types, secondary_types, is_default) VALUES ('Standard', ARRAY['Album', 'EP', 'Single'], '{}', true); @@ -53,7 +53,7 @@ CREATE TABLE follows ( baseline_at timestamptz, -- Whether the baseline also wants what the artist had already released -- that the profile passes and the library lacks: an approved artist - -- request, or the operator ticking "also want…". Cleared once applied. + -- request, or an admin ticking "also want…". Cleared once applied. want_existing boolean NOT NULL DEFAULT false, -- The discography read this follow last applied. checked_at timestamptz, @@ -111,7 +111,7 @@ ALTER INDEX lidarr_requests_status_idx RENAME TO requests_status_idx; ALTER INDEX lidarr_requests_artist_mbid_idx RENAME TO requests_artist_mbid_idx; ALTER INDEX lidarr_requests_album_mbid_idx RENAME TO requests_album_mbid_idx; --- Requests made before #5241 name albums by release id. A want is keyed by +-- Older requests may name albums by release id. A want is keyed by -- release group, so repoint every request the library can translate. UPDATE requests r SET album_mbid = a.release_group_mbid, @@ -133,10 +133,10 @@ SELECT DISTINCT ON (r.album_mbid) ORDER BY r.album_mbid, r.requested_at ON CONFLICT (release_group_mbid) DO NOTHING; --- The library's album artists start out followed on the default profile --- (operator, 2026-10-09: "we already have artists in minstrel those start as --- the wanted artists"). Various Artists and MusicBrainz's bracketed --- placeholders ([unknown], [anonymous]…) are not artists anyone follows. +-- The library's album artists start out followed on the default profile: the +-- artists already in the library are the wanted artists. Various Artists and +-- MusicBrainz's bracketed placeholders ([unknown], [anonymous]…) are not +-- artists anyone follows. INSERT INTO follows (artist_mbid, artist_name, profile_id) SELECT ar.mbid, ar.name, p.id FROM artists ar diff --git a/internal/db/migrations/0082_search_and_grab.up.sql b/internal/db/migrations/0082_search_and_grab.up.sql index 4c20ef3f..962586a1 100644 --- a/internal/db/migrations/0082_search_and_grab.up.sql +++ b/internal/db/migrations/0082_search_and_grab.up.sql @@ -1,5 +1,4 @@ --- 0082_search_and_grab.up.sql — searching for wants and grabbing releases --- (Scribe milestone #502, #5521). +-- 0082_search_and_grab.up.sql — searching for wants and grabbing releases. -- -- acquisition_settings one row: whether the schedule takes the best release -- by itself, how often a want is searched again, which @@ -16,7 +15,7 @@ CREATE TABLE acquisition_settings ( -- 0 switches the schedule off. search_interval_hours integer NOT NULL DEFAULT 24 CONSTRAINT acquisition_settings_interval_check CHECK (search_interval_hours BETWEEN 0 AND 720), - -- Lidarr's "Any" profile (note #5527): trash lossy to lossless; unknown off. + -- As Lidarr's "Any" profile: trash lossy to lossless; unknown off. allowed_qualities text[] NOT NULL DEFAULT ARRAY['lossless_hires', 'lossless', 'lossy_high', 'lossy_standard', 'lossy_low', 'lossy_poor', 'lossy_trash'], last_pass_at timestamptz, diff --git a/internal/db/migrations/0083_import.up.sql b/internal/db/migrations/0083_import.up.sql index eb30d143..0cf4d5bb 100644 --- a/internal/db/migrations/0083_import.up.sql +++ b/internal/db/migrations/0083_import.up.sql @@ -1,4 +1,4 @@ --- Import with verification (M502 #5522): a finished download is checked +-- Import with verification: a finished download is checked -- against the album it was grabbed for before anything reaches the library. -- -- grabs (columns) how far the download has got, and what the import diff --git a/internal/db/migrations/0084_download_lifecycle.up.sql b/internal/db/migrations/0084_download_lifecycle.up.sql index 067bf26f..eedda40a 100644 --- a/internal/db/migrations/0084_download_lifecycle.up.sql +++ b/internal/db/migrations/0084_download_lifecycle.up.sql @@ -1,6 +1,5 @@ --- Download lifecycle (M502 #5523), ported from Fabled Librarian's System 104: --- a download that stalls or fails is given up and its album searched again; --- an imported download seeds, then is removed. +-- Download lifecycle: a download that stalls or fails is given up and its album +-- searched again; an imported download seeds, then is removed. -- -- download_interventions what was seen wrong with a download or a client, -- what Minstrel did about it, and whether it diff --git a/internal/db/migrations/0085_library_wants.up.sql b/internal/db/migrations/0085_library_wants.up.sql index 9cf69406..9f7b87ce 100644 --- a/internal/db/migrations/0085_library_wants.up.sql +++ b/internal/db/migrations/0085_library_wants.up.sql @@ -1,7 +1,7 @@ --- The library's albums go on the wanted list as held, once (M502 #5567, --- operator 2026-10-10). After that the list is the truth: an album taken --- off it stays off, and one dropped into the library folder by hand is not --- added. Re-acquisition fetches back only albums on the list. +-- The library's albums go on the wanted list as held, once. After that the list +-- is the truth: an album taken off it stays off, and one dropped into the +-- library folder by hand is not added. Re-acquisition fetches back only albums +-- on the list. -- -- wants.reason library: on the list because the library -- held it when the list began. diff --git a/internal/db/migrations/0086_library_album_matches.up.sql b/internal/db/migrations/0086_library_album_matches.up.sql index 712850ee..fd7986da 100644 --- a/internal/db/migrations/0086_library_album_matches.up.sql +++ b/internal/db/migrations/0086_library_album_matches.up.sql @@ -1,10 +1,8 @@ --- Every library album is matched to MusicBrainz before it goes on the list --- (#5569, operator 2026-10-10: "they should all exist in music brainz we --- need to use their meta data or a search system to link them up"). The --- listing in 0085 took only albums whose tags already named their release --- group and artist; the rest are now matched by the release their tags --- name, the artist's discography, or a search, and checked against their --- own tracks. +-- Every library album is matched to MusicBrainz before it goes on the list, so +-- its metadata comes from MusicBrainz. The listing in 0085 took only albums +-- whose tags already named their release group and artist; the rest are now +-- matched by the release their tags name, the artist's discography, or a +-- search, and checked against their own tracks. -- -- library_album_matches one row per album the listing has decided: -- listed (with how it was matched) or unmatched diff --git a/internal/db/migrations/0087_library_rematch.up.sql b/internal/db/migrations/0087_library_rematch.up.sql index 7347fff3..cc278ed9 100644 --- a/internal/db/migrations/0087_library_rematch.up.sql +++ b/internal/db/migrations/0087_library_rematch.up.sql @@ -1,4 +1,4 @@ --- The first matching (#5569) required every track's length to fit as well +-- The first matching (0086) required every track's length to fit as well -- as its title, and refused singles and near-complete albums the library -- plainly holds. Matching now takes the titles alone under the album's own -- name: the albums it refused are matched again. diff --git a/internal/db/migrations/0088_library_matcher_version.up.sql b/internal/db/migrations/0088_library_matcher_version.up.sql index 112d7d6b..568921f5 100644 --- a/internal/db/migrations/0088_library_matcher_version.up.sql +++ b/internal/db/migrations/0088_library_matcher_version.up.sql @@ -1,4 +1,4 @@ --- Which version of the matching (#5569) decided an album. A refusal by an +-- Which version of the matching decided an album. A refusal by an -- older matcher is tried again by a newer one, so improving the matching -- reaches the albums it refused without a migration each time. ALTER TABLE library_album_matches ADD COLUMN matcher integer NOT NULL DEFAULT 0; diff --git a/internal/db/migrations/0089_library_match_by_hand.up.sql b/internal/db/migrations/0089_library_match_by_hand.up.sql index c093c221..8d8e47ca 100644 --- a/internal/db/migrations/0089_library_match_by_hand.up.sql +++ b/internal/db/migrations/0089_library_match_by_hand.up.sql @@ -1,4 +1,4 @@ --- The Collection's Unmatched section (#5579): a person matches an album the +-- The Collection's Unmatched section: a person matches an album the -- listing could not, by a MusicBrainz link or a search (method 'manual'), -- or leaves it off the list for good (outcome 'dismissed': a local band's -- album MusicBrainz will never have, a mix that is no album). diff --git a/internal/db/migrations/0090_upgrades.up.sql b/internal/db/migrations/0090_upgrades.up.sql index ffa3ad5f..c64734d4 100644 --- a/internal/db/migrations/0090_upgrades.up.sql +++ b/internal/db/migrations/0090_upgrades.up.sql @@ -1,11 +1,11 @@ --- Upgrades (M502 #5524): a held album whose copy is below the quality the --- operator settles for, or that holds a video rip, stays wanted for a better +-- Upgrades: a held album whose copy is below the quality an admin settles +-- for, or that holds a video rip, stays wanted for a better -- copy. The import replaces the old tracks with the new ones. -- -- acquisition_settings.upgrade_until the tier a held album is upgraded to; -- '' switches quality upgrades off. -- Lidarr's "Any" profile upgraded up to --- High Quality Lossy (note #5527). +-- High Quality Lossy. -- wants.upgrade why a held album is wanted again: -- quality (below upgrade_until) or rip -- (a track is a video rip); '' not. diff --git a/internal/db/migrations/0091_remove_lidarr.up.sql b/internal/db/migrations/0091_remove_lidarr.up.sql index 6d41e6fc..b8e64723 100644 --- a/internal/db/migrations/0091_remove_lidarr.up.sql +++ b/internal/db/migrations/0091_remove_lidarr.up.sql @@ -1,4 +1,4 @@ --- Minstrel no longer talks to Lidarr (M502 #5525): it finds, downloads, +-- Minstrel does not talk to Lidarr: it finds, downloads, -- checks and files music itself. -- -- lidarr_config the connection's settings: dropped. diff --git a/internal/db/migrations/0092_play_events_started_idx.up.sql b/internal/db/migrations/0092_play_events_started_idx.up.sql index c72abf73..d5404011 100644 --- a/internal/db/migrations/0092_play_events_started_idx.up.sql +++ b/internal/db/migrations/0092_play_events_started_idx.up.sql @@ -1,4 +1,4 @@ --- The admin Overview (M504) reads plays across every user by time: the ones +-- The admin Overview reads plays across every user by time: the ones -- in progress and the last day's count. The only index on play_events leads -- with user_id, so without this every Overview load scanned the table. CREATE INDEX play_events_started_idx ON play_events (started_at); diff --git a/internal/db/migrations/0093_library_settings.up.sql b/internal/db/migrations/0093_library_settings.up.sql index 6c4cf946..4bf3473d 100644 --- a/internal/db/migrations/0093_library_settings.up.sql +++ b/internal/db/migrations/0093_library_settings.up.sql @@ -1,4 +1,4 @@ --- The library's folders, set in Settings (#6129). Until someone saves them +-- The library's folders, set in Settings. Until someone saves them -- there, scan_paths is NULL and MINSTREL_LIBRARY_SCAN_PATHS names the folders, -- as it always has; from the first save the row is what the server reads. CREATE TABLE library_settings ( diff --git a/internal/db/migrations/0094_duplicate_distinct.up.sql b/internal/db/migrations/0094_duplicate_distinct.up.sql index 4f685da8..856757e5 100644 --- a/internal/db/migrations/0094_duplicate_distinct.up.sql +++ b/internal/db/migrations/0094_duplicate_distinct.up.sql @@ -1,11 +1,11 @@ --- 0094_duplicate_distinct.up.sql — duplicates settle themselves (Scribe --- milestone #498, operator 2026-10-19: "this needs to be automated away"). +-- 0094_duplicate_distinct.up.sql — duplicates settle themselves, without +-- waiting on an admin. -- -- distinct: one album holds two versions of a song that sound alike ("Lovesick" -- and "Lovesick (Instrumental)"). They are not duplicates, and the resolver -- dismisses them itself. mismatch now also covers one album holding the same -- audio in several track slots, the resolver's wrong-file case. --- Rule 36: the new value swaps the constraint in the same migration. +-- The new value swaps the constraint in the same migration. ALTER TABLE duplicate_groups DROP CONSTRAINT duplicate_groups_class_check; ALTER TABLE duplicate_groups ADD CONSTRAINT duplicate_groups_class_check CHECK (class IN ('same_release', 'cross_release', 'mismatch', 'distinct', 'review')); diff --git a/internal/db/migrations/0095_tracks_emptied_at.up.sql b/internal/db/migrations/0095_tracks_emptied_at.up.sql index a4d0bac8..3ab7883a 100644 --- a/internal/db/migrations/0095_tracks_emptied_at.up.sql +++ b/internal/db/migrations/0095_tracks_emptied_at.up.sql @@ -1,12 +1,12 @@ -- 0095_tracks_emptied_at.up.sql — a slot the duplicate resolver emptied --- waits for a copy fetched again (Scribe milestone #498). +-- waits for a copy fetched again. -- -- When the resolver removes a file filed under another song's title and the -- album holds no copy of that song as long as the song is, the row is marked -- missing and stamped here with the same time. Missing-pair folding then pairs -- it by title only with a track added after that time, a copy fetched again, --- and not with one already on the album: on 2026-10-20 an emptied "Open the --- Gateway" was folded into a file tagged so that holds another song. +-- and not with one already on the album, which under that title holds +-- another song. -- -- The stamp counts only while it equals missing_since: a row whose file came -- back and later went missing again is an ordinary missing row. diff --git a/internal/db/migrations/0096_similarity_relative_scores.up.sql b/internal/db/migrations/0096_similarity_relative_scores.up.sql index 5a9a912b..111e110e 100644 --- a/internal/db/migrations/0096_similarity_relative_scores.up.sql +++ b/internal/db/migrations/0096_similarity_relative_scores.up.sql @@ -1,7 +1,7 @@ --- #6177: ListenBrainz similarity scores were stored raw. They are counts (16 to --- 1445 for recordings, 16 to 10,875 for artists on the operator's library), and --- every reader treats similarity as 0 to 1. Rescale the stored rows relative to --- each seed's best score, as the worker now writes them. +-- Before: ListenBrainz similarity scores were stored raw. They are counts, into +-- the thousands, and every reader treats similarity as 0 to 1. After: the +-- stored rows are rescaled relative to each seed's best score, as the worker +-- now writes them. -- -- A seed's best is taken over everything stored for it: the cached LB answer -- and its edges for tracks, matched and unmatched rows for artists. A seed diff --git a/internal/db/migrations/0097_requeue_short_track_prints.up.sql b/internal/db/migrations/0097_requeue_short_track_prints.up.sql index 55022235..47288f43 100644 --- a/internal/db/migrations/0097_requeue_short_track_prints.up.sql +++ b/internal/db/migrations/0097_requeue_short_track_prints.up.sql @@ -1,8 +1,8 @@ --- #6178: the image's fpcalc exits 3 ("Error decoding audio frame (End of --- file)") on every file shorter than the span it is asked for, so every track --- under two minutes failed both prints. The failure was stored as a verdict, --- and a verdict is not retried until the file changes. fpcalcReadToEnd now --- keeps those prints; clear the failures so the workers take them again. +-- The image's fpcalc exits 3 ("Error decoding audio frame (End of file)") on +-- every file shorter than the span it is asked for, so every track under two +-- minutes failed both prints. Before: the failure was stored as a verdict, and +-- a verdict is not retried until the file changes. After: fpcalcReadToEnd keeps +-- those prints; clear the failures so the workers take them again. -- The AcoustID lookup's queue takes a track with no lookup row. DELETE FROM track_acoustid_lookups diff --git a/internal/db/migrations/0098_release_blocks_gone.up.sql b/internal/db/migrations/0098_release_blocks_gone.up.sql index b07959e5..1b2a2cfe 100644 --- a/internal/db/migrations/0098_release_blocks_gone.up.sql +++ b/internal/db/migrations/0098_release_blocks_gone.up.sql @@ -1,4 +1,4 @@ --- #6176: gone, a release its download client would not let Minstrel follow: +-- gone: a release its download client would not let Minstrel follow: -- held outside Minstrel's category, or leaving the client twice. Held back -- until expires_at, as stalled is. ALTER TABLE release_blocks DROP CONSTRAINT release_blocks_reason_check;