feat(metrics): provenance as standard — pick_kind for all system mixes
The #1249 mechanism (stamp WHY a track is in the snapshot at build time, freeze it onto the play at ingestion, break it down in metrics) generalizes from a For You one-off to the standard for every system mix (#1270): - Migration 0039 widens both pick_kind CHECKs (drop + re-add in the same change) to taste/fresh + Discover's dormant/cross_user/random + tier1-3 for the rule-#131 eligibility ladders. - GetForYouPickKindForTrack becomes GetSystemPickKindForTrack (user, variant, track); ingestion stamps any systemPlaylistSources play from its own variant's live snapshot, live + offline paths. - Discover stamps its candidate bucket on discoverTrack before the interleave, making the 40/30/30 allocation measurable; dedup keeps the taking bucket's stamp. - Metrics replace the for_you special-case with one pick-kind vocabulary — any family with attributed plays gets a breakdown, future stamping mixes need no metrics change. - Web: breakdown sub-rows are now toggled per surface (collapsed by default) so eight stamping mixes don't swamp the card. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TsF3cNoKrqCYsU78cXC8U6
This commit is contained in:
@@ -168,6 +168,48 @@ func TestBucketMetricsResponse_ForYouBreakdown(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBucketMetricsResponse_DiscoverBucketBreakdown(t *testing.T) {
|
||||
// Provenance is standard (#1270): Discover's bucket stamps surface as
|
||||
// a breakdown exactly like For You's taste/fresh — this is what makes
|
||||
// the 40/30/30 allocation judgeable instead of a guess.
|
||||
src := func(s string) *string { return &s }
|
||||
kind := func(s string) *string { return &s }
|
||||
rows := []dbq.RecommendationSourceMetricsForUserRow{
|
||||
{Source: src("discover"), PickKind: kind("dormant"), Plays: 8, Skips: 2,
|
||||
CompletionN: 8, AvgCompletion: 0.8},
|
||||
{Source: src("discover"), PickKind: kind("cross_user"), Plays: 6, Skips: 3,
|
||||
CompletionN: 6, AvgCompletion: 0.6},
|
||||
{Source: src("discover"), PickKind: kind("random"), Plays: 4, Skips: 3,
|
||||
CompletionN: 4, AvgCompletion: 0.4},
|
||||
// NULL pick_kind = plays that predate bucket stamping.
|
||||
{Source: src("discover"), Plays: 2, Skips: 0, CompletionN: 2, AvgCompletion: 0.9},
|
||||
}
|
||||
resp := bucketMetricsResponse(recMetricsDefaultDays, rows)
|
||||
|
||||
d := findSurface(resp, "discover")
|
||||
if d == nil {
|
||||
t.Fatal("discover family missing")
|
||||
}
|
||||
if d.Plays != 20 || d.Skips != 8 {
|
||||
t.Errorf("discover plays/skips = %d/%d, want 20/8", d.Plays, d.Skips)
|
||||
}
|
||||
wantKeys := []string{
|
||||
"discover_dormant", "discover_cross_user", "discover_random",
|
||||
"discover_unattributed",
|
||||
}
|
||||
if len(d.Breakdown) != len(wantKeys) {
|
||||
t.Fatalf("breakdown rows = %d, want %d", len(d.Breakdown), len(wantKeys))
|
||||
}
|
||||
for i, k := range wantKeys {
|
||||
if d.Breakdown[i].Key != k {
|
||||
t.Errorf("breakdown[%d].key = %s, want %s", i, d.Breakdown[i].Key, k)
|
||||
}
|
||||
}
|
||||
if b := d.Breakdown[0]; b.Label != "Dormant artists" || b.Plays != 8 {
|
||||
t.Errorf("dormant row = %+v, want label=Dormant artists plays=8", b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecommendationMetrics_BucketsWithBaseline(t *testing.T) {
|
||||
if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" {
|
||||
t.Skip("MINSTREL_TEST_DATABASE_URL not set")
|
||||
|
||||
Reference in New Issue
Block a user