-- 0049_suggestion_snoozes.up.sql — time-boxed "not right now" on a Discover -- artist suggestion (#2374, milestone #268 slice 3). -- -- 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 -- 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 -- suggestion must not remove it from anyone else's deck. -- -- candidate_mbid is text with NO foreign key, on purpose. Suggestions come -- from artist_similarity_unmatched and are out-of-library BY DEFINITION, so -- there is no artists row to reference — the MBID is the only stable identity -- available. candidate_name is denormalized for the same reason: the manage / -- un-snooze list has nowhere else to resolve a display name from. CREATE TABLE suggestion_snoozes ( user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, candidate_mbid text NOT NULL, candidate_name text NOT NULL, snoozed_until timestamptz NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (user_id, candidate_mbid) ); -- Supports the gc sweep's unqualified `WHERE snoozed_until < now()` scan. The -- composite PK already covers every per-user read, so this is the only extra -- index worth its write cost at household-scale row counts (same reasoning as -- lidarr_quarantine in 0011). CREATE INDEX suggestion_snoozes_expiry_idx ON suggestion_snoozes (snoozed_until);