From 6028d78844b1aff664e039befa18c63c0485b54f Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Fri, 23 Oct 2026 12:34:39 -0400 Subject: [PATCH] docs: migration headers keep the before and after, without ids, quotes or one library's numbers (#6277) Applies rule 32's migration exception: each header still says what the schema was, what changes and what it prevents, but task, milestone, decision and rule ids are replaced by the practice they stood for, the operator quotes by the decision, single-install counts are dropped, and pointers to a deleted package and an external design doc are gone. golang-migrate keeps no checksum, so editing applied migrations' comments is safe. Co-Authored-By: Claude Opus 5.5 --- .../db/migrations/0002_core_library.up.sql | 4 +-- internal/db/migrations/0004_sessions.up.sql | 7 ++-- internal/db/migrations/0005_events.up.sql | 11 +++--- internal/db/migrations/0006_likes.up.sql | 5 ++- .../migrations/0007_contextual_likes.up.sql | 12 +++---- internal/db/migrations/0008_scrobble.up.sql | 6 ++-- internal/db/migrations/0009_similarity.up.sql | 2 +- internal/db/migrations/0010_lidarr.up.sql | 4 +-- .../migrations/0011_lidarr_quarantine.up.sql | 2 +- .../0012_artist_similarity_unmatched.up.sql | 2 +- .../0013_lidarr_metadata_profile.up.sql | 11 +++--- internal/db/migrations/0014_playlists.up.sql | 3 +- .../migrations/0015_system_playlists.up.sql | 6 ++-- .../migrations/0016_album_cover_source.up.sql | 4 +-- internal/db/migrations/0017_scan_runs.up.sql | 2 +- ...18_artist_art_and_provider_settings.up.sql | 5 ++- .../db/migrations/0019_scan_schedule.up.sql | 2 +- .../0020_relax_art_source_checks.up.sql | 2 +- .../db/migrations/0022_user_management.up.sql | 6 ++-- .../migrations/0023_user_auto_approve.up.sql | 6 ++-- .../db/migrations/0025_library_changes.up.sql | 11 +++--- .../db/migrations/0026_users_timezone.up.sql | 2 +- .../0027_system_playlist_rotation.up.sql | 2 +- .../0028_discovery_mix_variants.up.sql | 14 ++++---- ..._relax_art_source_check_to_nonempty.up.sql | 5 ++- .../0031_lidarr_add_confirmed_at.up.sql | 4 +-- .../db/migrations/0032_playback_errors.up.sql | 6 ++-- .../migrations/0033_drop_scan_schedule.up.sql | 2 +- .../db/migrations/0034_you_might_like.up.sql | 2 +- .../db/migrations/0035_taste_profile.up.sql | 6 ++-- .../db/migrations/0036_diagnostics.up.sql | 14 ++++---- .../0037_diagnostics_playback_kind.up.sql | 8 ++--- .../migrations/0038_for_you_pick_kind.up.sql | 4 +-- .../0039_pick_kind_vocabulary.up.sql | 10 +++--- .../0040_recommendation_tuning.up.sql | 11 +++--- .../0041_discover_taste_unheard.up.sql | 11 +++--- internal/db/migrations/0042_track_tags.up.sql | 2 +- .../0043_tag_provider_settings.up.sql | 7 ++-- ...044_taste_tuning_enriched_tag_scale.up.sql | 6 ++-- .../migrations/0045_taste_profile_eras.up.sql | 8 ++--- .../0046_context_time_weight.up.sql | 6 ++-- .../0047_taste_profile_moods.up.sql | 16 ++++----- .../0048_play_events_device_class.up.sql | 16 ++++----- .../migrations/0049_suggestion_snoozes.up.sql | 9 ++--- .../0050_candidate_artist_tags.up.sql | 7 ++-- .../db/migrations/0051_discover_tuning.up.sql | 16 ++++----- .../db/migrations/0052_session_ips.up.sql | 2 +- .../migrations/0053_network_settings.up.sql | 9 ++--- .../0054_track_tag_read_version.up.sql | 4 +-- .../0055_track_missing_since.up.sql | 2 +- .../0056_missing_reacquisition.up.sql | 19 +++++------ .../migrations/0057_songs_like_tuning.up.sql | 14 +++----- .../migrations/0058_track_fingerprints.up.sql | 9 +++-- .../migrations/0059_duplicate_groups.up.sql | 12 +++---- .../0060_play_events_track_index.up.sql | 4 +-- .../0061_fingerprint_settings.up.sql | 18 +++++----- .../migrations/0062_network_public_url.up.sql | 8 ++--- .../db/migrations/0063_api_token_hash.up.sql | 2 +- .../0064_clear_subsonic_password.up.sql | 8 ++--- .../db/migrations/0065_track_loudness.up.sql | 8 ++--- .../db/migrations/0066_album_loudness.up.sql | 3 +- .../0067_user_normalization_prefs.up.sql | 2 +- .../0068_leveled_stream_cache.up.sql | 5 ++- .../db/migrations/0069_acoustid_lookup.up.sql | 14 ++++---- .../0070_album_release_group_mbid.up.sql | 2 +- ...pen_false_reacquisition_completions.up.sql | 10 +++--- .../migrations/0072_similarity_cache.up.sql | 8 ++--- .../migrations/0073_user_notifications.up.sql | 4 +-- .../migrations/0074_notification_email.up.sql | 4 +-- .../migrations/0075_duplicate_resolve.up.sql | 34 +++++++++---------- .../db/migrations/0076_track_song_link.up.sql | 2 +- .../0077_track_source_verdict.up.sql | 9 +++-- .../0078_drop_duplicate_lidarr_state.up.sql | 2 +- .../0079_acquisition_services.up.sql | 10 +++--- .../migrations/0080_release_catalogue.up.sql | 2 +- internal/db/migrations/0081_wanted.up.sql | 20 +++++------ .../db/migrations/0082_search_and_grab.up.sql | 5 ++- internal/db/migrations/0083_import.up.sql | 2 +- .../migrations/0084_download_lifecycle.up.sql | 5 ++- .../db/migrations/0085_library_wants.up.sql | 8 ++--- .../0086_library_album_matches.up.sql | 12 +++---- .../db/migrations/0087_library_rematch.up.sql | 2 +- .../0088_library_matcher_version.up.sql | 2 +- .../0089_library_match_by_hand.up.sql | 2 +- internal/db/migrations/0090_upgrades.up.sql | 6 ++-- .../db/migrations/0091_remove_lidarr.up.sql | 2 +- .../0092_play_events_started_idx.up.sql | 2 +- .../migrations/0093_library_settings.up.sql | 2 +- .../migrations/0094_duplicate_distinct.up.sql | 6 ++-- .../migrations/0095_tracks_emptied_at.up.sql | 6 ++-- .../0096_similarity_relative_scores.up.sql | 8 ++--- .../0097_requeue_short_track_prints.up.sql | 10 +++--- .../0098_release_blocks_gone.up.sql | 2 +- 93 files changed, 301 insertions(+), 338 deletions(-) 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;