fb4431207d
Milestone #127 step 2 (#1249). For You deliberately blends two populations — a head of top-scored taste picks and a tail sampled from deeper ranking (the freshness injection) — but the metrics judged it as one blob, so its skip rate couldn't distinguish "the taste engine is missing" from "the freshness tax is too high". That number decides the exploration share before we tune it. - Migration 0038: nullable pick_kind ('taste'|'fresh') on both playlist_tracks (stamped at snapshot build) and play_events (frozen at play-ingestion — the snapshot rebuilds daily, so attribution cannot be reconstructed at read time). - Builder: pickHeadAndTail marks head=taste / tail=fresh; the small-pool fallback is all taste (top-N-by-score IS the taste mechanism). Other variants persist NULL. - Ingestion: for_you plays (live + offline replay) look the track up in the user's current snapshot; not found → unattributed, never guessed. - Metrics: For You's row gains a breakdown (taste / fresh / earlier unattributed plays), parent row stays the sum; web card renders the sub-rows indented with the same baseline deltas + low-data dimming. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TsF3cNoKrqCYsU78cXC8U6
35 lines
1.6 KiB
SQL
35 lines
1.6 KiB
SQL
-- For You exploration attribution (milestone #127 step 2, Scribe #1249).
|
|
--
|
|
-- 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
|
|
-- blob, so a high skip rate couldn't be read as "the taste engine is
|
|
-- missing" vs "the freshness tax is too high".
|
|
--
|
|
-- Two denormalized columns, both nullable:
|
|
--
|
|
-- 1. playlist_tracks.pick_kind — stamped by the system-playlist builder
|
|
-- at snapshot build time. NULL for manual playlists and for system
|
|
-- variants that don't split (Discover, Songs like X, discovery
|
|
-- mixes). System snapshots are atomically replaced daily, so the
|
|
-- column never goes stale within a snapshot.
|
|
--
|
|
-- 2. play_events.pick_kind — stamped at play-ingestion time by looking
|
|
-- the track up in the user's CURRENT For You snapshot. Attribution
|
|
-- must be frozen at play time: For You rebuilds daily, so a 30-day
|
|
-- metrics window spans ~30 snapshots and a join-at-read against the
|
|
-- live snapshot would misattribute nearly everything. Plays that
|
|
-- predate this feature (or whose track already rotated out, e.g. a
|
|
-- late offline replay) stay NULL → reported as unattributed.
|
|
|
|
ALTER TABLE playlist_tracks
|
|
ADD COLUMN pick_kind text
|
|
CONSTRAINT playlist_tracks_pick_kind_check
|
|
CHECK (pick_kind IN ('taste', 'fresh'));
|
|
|
|
ALTER TABLE play_events
|
|
ADD COLUMN pick_kind text
|
|
CONSTRAINT play_events_pick_kind_check
|
|
CHECK (pick_kind IN ('taste', 'fresh'));
|