-- Recommendation tuning lab (milestone #127, Scribe #1250). -- -- 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. -- -- Rows are seeded by the recsettings service's boot reconcile (the -- coverart SettingsService pattern), not by this migration, so shipped -- defaults live in exactly one place (Go). CREATE TABLE recommendation_weight_profiles ( profile text PRIMARY KEY CONSTRAINT recommendation_weight_profiles_profile_check CHECK (profile IN ('radio', 'daily_mix')), base_weight double precision NOT NULL, like_boost double precision NOT NULL, recency_weight double precision NOT NULL, skip_penalty double precision NOT NULL, jitter_magnitude double precision NOT NULL, context_weight double precision NOT NULL, similarity_weight double precision NOT NULL, taste_weight double precision NOT NULL, updated_at timestamptz NOT NULL DEFAULT now() ); -- Taste-profile build knobs: engagement half-life + the completion→ -- engagement curve points (taste.Config's tunable subset). CREATE TABLE taste_tuning ( singleton boolean PRIMARY KEY DEFAULT true CONSTRAINT taste_tuning_singleton_check CHECK (singleton), half_life_days double precision NOT NULL, engagement_hard_skip double precision NOT NULL, engagement_neutral double precision NOT NULL, engagement_full double precision NOT NULL, updated_at timestamptz NOT NULL DEFAULT now() ); -- Every knob turn writes one audit row; the metrics trend view -- (#1251) 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, changed_at timestamptz NOT NULL DEFAULT now(), scope text NOT NULL CONSTRAINT recommendation_tuning_audit_scope_check CHECK (scope IN ('radio', 'daily_mix', 'taste')), action text NOT NULL CONSTRAINT recommendation_tuning_audit_action_check CHECK (action IN ('update', 'reset')), changes jsonb NOT NULL ); CREATE INDEX recommendation_tuning_audit_changed_at_idx ON recommendation_tuning_audit (changed_at DESC);