feat(metrics): provenance as standard — pick_kind for all system mixes
test-go / test (push) Successful in 31s
test-web / test (push) Successful in 38s
test-go / integration (push) Successful in 4m36s

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:
2026-07-02 23:26:52 -04:00
co-authored by Claude Fable 5
parent fb4431207d
commit 5faa57634b
14 changed files with 410 additions and 156 deletions
+14 -3
View File
@@ -198,7 +198,7 @@ describe('Settings page — Recommendation metrics card', () => {
...over
});
test('renders the For You taste/fresh breakdown rows (#1249)', async () => {
test('breakdown rows are collapsed by default and expand on toggle (#1249/#1270)', async () => {
setupPage();
metricsMock.data = {
window_days: 30,
@@ -225,13 +225,23 @@ describe('Settings page — Recommendation metrics card', () => {
]
};
render(SettingsPage);
await waitFor(() => expect(screen.getByText('For You')).toBeInTheDocument());
const toggle = await screen.findByRole('button', { name: /for you/i });
// Collapsed by default: every stamping mix now carries a breakdown,
// so always-open sub-rows would swamp the surface-level view.
expect(toggle).toHaveAttribute('aria-expanded', 'false');
expect(screen.queryByText(/Taste picks/)).not.toBeInTheDocument();
await fireEvent.click(toggle);
expect(toggle).toHaveAttribute('aria-expanded', 'true');
expect(screen.getByText(/Taste picks/)).toBeInTheDocument();
expect(screen.getByText(/Fresh picks/)).toBeInTheDocument();
expect(screen.getByText(/Earlier plays/)).toBeInTheDocument();
await fireEvent.click(toggle);
expect(screen.queryByText(/Taste picks/)).not.toBeInTheDocument();
});
test('surfaces without a breakdown render no sub-rows', async () => {
test('surfaces without a breakdown render no toggle and no sub-rows', async () => {
setupPage();
metricsMock.data = {
window_days: 30,
@@ -246,6 +256,7 @@ describe('Settings page — Recommendation metrics card', () => {
};
render(SettingsPage);
await waitFor(() => expect(screen.getByText('Radio')).toBeInTheDocument());
expect(screen.queryByRole('button', { name: /radio/i })).not.toBeInTheDocument();
expect(screen.queryByText(/Taste picks/)).not.toBeInTheDocument();
});
});