feat: enhance discovery, vibe sessions, and library enrichment
This commit is contained in:
@@ -510,4 +510,138 @@ export const MIGRATIONS: Migration[] = [
|
||||
ON pending_file_deletions(requested_at);
|
||||
`,
|
||||
},
|
||||
{
|
||||
// System E originally wrote graph_exploration claims without registering it
|
||||
// in source_trust, so the FK rejected every graph walk. It also lacked a
|
||||
// durable link from an acquired candidate to the track the scanner created.
|
||||
id: '20260801_acquisition_pipeline',
|
||||
sql: `
|
||||
INSERT INTO source_trust (key, trust, description)
|
||||
VALUES ('graph_exploration', 0.40,
|
||||
'Muzick graph traversal provenance for discovery candidates.')
|
||||
ON CONFLICT (key) DO NOTHING;
|
||||
|
||||
ALTER TABLE discovery_candidates
|
||||
ADD COLUMN IF NOT EXISTS acquired_track_id UUID
|
||||
REFERENCES tracks(id) ON DELETE SET NULL;
|
||||
ALTER TABLE discovery_candidates
|
||||
ADD COLUMN IF NOT EXISTS acquired_at TIMESTAMPTZ;
|
||||
ALTER TABLE discovery_candidates
|
||||
ADD COLUMN IF NOT EXISTS acquisition_attempts INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE discovery_candidates
|
||||
ADD COLUMN IF NOT EXISTS last_error TEXT;
|
||||
CREATE INDEX IF NOT EXISTS idx_discovery_candidates_status
|
||||
ON discovery_candidates (status, first_seen_at);
|
||||
`,
|
||||
},
|
||||
{
|
||||
// Separate portrait fetching from structural metadata so an operator can
|
||||
// re-enrich missing artist images without implicitly enabling MB writes.
|
||||
id: '20260801_artist_image_enrichment_toggle',
|
||||
sql: `
|
||||
INSERT INTO settings (key, value)
|
||||
VALUES ('enrich_artist_images', 'false')
|
||||
ON CONFLICT (key) DO NOTHING;
|
||||
`,
|
||||
},
|
||||
{
|
||||
// Audio analysis v1 stored an arbitrary linear energy scale, which made
|
||||
// nearly every mastered track look maximally energetic. Track the analysis
|
||||
// contract and source hash so bounded background jobs can safely refresh
|
||||
// stale results without repeatedly decoding unchanged files.
|
||||
id: '20260801_audio_analysis_v2',
|
||||
sql: `
|
||||
ALTER TABLE track_audio_features
|
||||
ADD COLUMN IF NOT EXISTS analysis_version SMALLINT NOT NULL DEFAULT 0;
|
||||
ALTER TABLE track_audio_features
|
||||
ADD COLUMN IF NOT EXISTS source_hash TEXT;
|
||||
ALTER TABLE track_audio_features
|
||||
ADD COLUMN IF NOT EXISTS analyzed_at TIMESTAMPTZ;
|
||||
|
||||
DO $mig$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint
|
||||
WHERE conrelid = 'track_audio_features'::regclass
|
||||
AND conname = 'track_audio_features_bpm_range'
|
||||
) THEN
|
||||
ALTER TABLE track_audio_features
|
||||
ADD CONSTRAINT track_audio_features_bpm_range
|
||||
CHECK (bpm IS NULL OR (bpm >= 30 AND bpm <= 300)) NOT VALID;
|
||||
END IF;
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint
|
||||
WHERE conrelid = 'track_audio_features'::regclass
|
||||
AND conname = 'track_audio_features_energy_range'
|
||||
) THEN
|
||||
ALTER TABLE track_audio_features
|
||||
ADD CONSTRAINT track_audio_features_energy_range
|
||||
CHECK (energy IS NULL OR (energy >= 0 AND energy <= 1)) NOT VALID;
|
||||
END IF;
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint
|
||||
WHERE conrelid = 'track_audio_features'::regclass
|
||||
AND conname = 'track_audio_features_danceability_range'
|
||||
) THEN
|
||||
ALTER TABLE track_audio_features
|
||||
ADD CONSTRAINT track_audio_features_danceability_range
|
||||
CHECK (danceability IS NULL OR (danceability >= 0 AND danceability <= 1)) NOT VALID;
|
||||
END IF;
|
||||
END $mig$;
|
||||
`,
|
||||
},
|
||||
{
|
||||
// tracks.release_date is a denormalised copy of the canonical
|
||||
// MusicBrainz release-group date on albums. Before this migration the
|
||||
// worker populated albums only, leaving every historical track invisible
|
||||
// to the novelty generator. The backfill and triggers make the canonical
|
||||
// album value win deterministically for both old and future rows.
|
||||
id: '20260801_track_release_dates_from_albums',
|
||||
sql: `
|
||||
-- The worker used to create this column lazily, but migration execution
|
||||
-- must not rely on worker startup order.
|
||||
ALTER TABLE albums ADD COLUMN IF NOT EXISTS release_date DATE;
|
||||
|
||||
-- Backfill only mismatches. IS DISTINCT FROM is null-safe and makes the
|
||||
-- statement safe to rerun manually for diagnostics/recovery.
|
||||
UPDATE tracks AS t
|
||||
SET release_date = al.release_date
|
||||
FROM albums AS al
|
||||
WHERE t.album_id = al.id
|
||||
AND t.release_date IS DISTINCT FROM al.release_date;
|
||||
|
||||
CREATE OR REPLACE FUNCTION sync_track_release_date_from_album()
|
||||
RETURNS TRIGGER AS $mig$
|
||||
BEGIN
|
||||
SELECT release_date INTO NEW.release_date
|
||||
FROM albums
|
||||
WHERE id = NEW.album_id;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$mig$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_tracks_sync_release_date ON tracks;
|
||||
CREATE TRIGGER trg_tracks_sync_release_date
|
||||
BEFORE INSERT OR UPDATE OF album_id, release_date ON tracks
|
||||
FOR EACH ROW EXECUTE FUNCTION sync_track_release_date_from_album();
|
||||
|
||||
CREATE OR REPLACE FUNCTION propagate_album_release_date_to_tracks()
|
||||
RETURNS TRIGGER AS $mig$
|
||||
BEGIN
|
||||
UPDATE tracks
|
||||
SET release_date = NEW.release_date
|
||||
WHERE album_id = NEW.id
|
||||
AND release_date IS DISTINCT FROM NEW.release_date;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$mig$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_albums_propagate_release_date ON albums;
|
||||
CREATE TRIGGER trg_albums_propagate_release_date
|
||||
AFTER UPDATE OF release_date ON albums
|
||||
FOR EACH ROW
|
||||
WHEN (OLD.release_date IS DISTINCT FROM NEW.release_date)
|
||||
EXECUTE FUNCTION propagate_album_release_date_to_tracks();
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user