feat(api): Weekly Rotation rolls over Wednesday and stays stable once shared - #1034
Merged
Conversation
…at the rollover Two changes so the mix can be linked and revisited. Rollover moves from the ISO week boundary (Monday 00:00 UTC) to Wednesday 00:00 UTC. Product call: Monday already belongs to the other weekly surfaces and Friday is Spotify's day. The period is still identified by an ISO (year, week) pair -- it is the ISO week shifted forward two days -- so cache keys, the seed, and share links keep their shape. The listener's history (plays, saves, reposts, follows) is now read as of the period's start instant rather than the request. Before, playing a track from the mix met the played-exclusion and dropped it on the next cache miss, so a mix shared on Wednesday was a different, shorter list by Thursday. A shareable artifact has to survive being listened to. The candidate pool and engagement counts still drift mid-week; freezing those needs a stored snapshot. Adds a pure test for the period math (rollover instant, year boundary, non-UTC input, a full year of round trips) and a regression test that a track played inside the current period stays in the mix. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Sep 8, 2026
dylanjeffers
added a commit
to AudiusProject/apps
that referenced
this pull request
Sep 8, 2026
## What
A Weekly Rotation can now be shared. Share produces
`audius.co/explore/weekly-rotation/:handle`, which shows that listener's
mix to anyone (the endpoint is public, no auth), and unfurls with a 2x2
collage of the mix's first four tracks rendered by
`og.audius.co/weekly-rotation/:handle?week=YYYY-WW`.
The bare `/explore/weekly-rotation` stays the signed-in user's own mix.
Under `/explore` rather than `/:handle/weekly-rotation` because
`/:handle/:slug` is the track permalink pattern.
## By package
**common**
- `ShareType` / `ShareModalRequest` / `ShareContent` gain
`weeklyRotation`. The content is just the listener (`User`): there's no
entity behind the mix, the link is a function of the handle.
- `useWeeklyRotation({ userId })` takes an optional target user;
defaults to the viewer.
- `Share` / `ShareToTwitter` analytics get a `weeklyRotation` kind.
- `WEEKLY_ROTATION_USER_PAGE` + `weeklyRotationPage(handle)`. Copy says
Wednesday: the rollover moved (api PR).
**web**
- Share button next to Play. The share modal builds the link from the
handle and copies it directly with the same `Share` event the sagas
emit, there being no social saga for a non-entity.
- Handle route registered in both router trees. The page resolves the
handle via `useUserByHandle`, titles as "{name}'s Weekly Rotation" for
someone else's mix, and only the handle route gets `og:image` +
canonical (the bare route is per-viewer and shouldn't be indexed as
anyone's).
- New Vike SSR entry `ssr/weekly-rotation`
(`/explore/weekly-rotation/@handle`): fetches the user for the name,
emits the collage URL as `og:image`, `summary_large_image`. The bare
route still falls to the explore handler, which now has a
`weekly-rotation` entry in `exploreMap`.
- `utils/weeklyRotationPeriod.ts`: dependency-free mirror of the API's
Wednesday-shifted ISO week, used by SSR (which avoids
`@audius/common/utils`) and the client. Same test cases as the Go side
so they can't drift apart. 7 vitest cases.
**mobile**
- Share icon in the top bar. Drawer offers DM, X, Copy Link, and the
system sheet. No Instagram/TikTok/Snapchat story rows: those need a
single streamable mp3 for the FFmpeg background video, which a mix
doesn't have.
- `WeeklyRotationScreen` takes an optional `handle` param;
`explore/weekly-rotation/:handle?` added to the explore deep-link map.
## Verified
- `tsc` clean in common, web, mobile. ESLint clean on every touched
file.
- `weeklyRotationPeriod.test.ts` passes.
- The collage endpoint was rendered locally against prod (see og PR).
## Not verified
- Mobile share drawer and deep link compile but were not exercised on a
simulator.
## Depends on
- AudiusProject/og#6 for the collage route (until deployed, `og:image`
500s and unfurlers show no image).
- AudiusProject/api#1034 for the Wednesday rollover and week-stable
history. Without it the shared mix can shrink mid-week as the owner
listens.
## Product note
Michael's Weekly Rotation thread describes the mix as new music from
artists you follow / have liked. The shipped ranking does the opposite
on purpose. Not changed here; flagged on the api PR.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Weekly Rotation is becoming shareable (
audius.co/explore/weekly-rotation/:handle: AudiusProject/apps#14587 and AudiusProject/og#6). Two things on the API side had to change for a shared link to mean anything.What
Rollover moves to Wednesday 00:00 UTC (was the ISO week boundary, Monday). Product call from Michael's Weekly Rotation thread: Monday already belongs to the other weekly surfaces and Friday is Spotify's. The period is still identified by an ISO
(year, week)pair, it's the ISO week shifted forward two days, so cache keys, the jitter seed and share links keep their shape.weeklyRotationPeriod/weeklyRotationPeriodStartare inverses and tested as such.Listener history is frozen at the rollover instant. Plays, saves, reposts and follows are now read as of the period's start (
@periodStart) rather than the request. Before this, playing a track from the mix met the played-exclusion and removed it on the next cache miss, so a mix shared on Wednesday was a different, shorter list by Thursday. The mix has to survive being listened to.Still drifts mid-week: the trending candidate pool and engagement counts. Freezing those needs a stored snapshot table; not in scope here.
Tests
TestWeeklyRotationPeriod: rollover instant, one second before it, Monday/Tuesday belonging to the previous period, non-UTC input, the 2026/2027 year boundary, and a round trip over every hour of 2026.TestV1UsersWeeklyRotationKeepsTracksPlayedThisPeriod: a play dated now leaves the track in the mix; a play from 8 days ago still excludes.All eight weekly-rotation tests pass against the compose DB.
Not in this PR
Michael's thread also describes the mix as new music from artists you follow / have liked. The shipped ranking does the opposite on purpose (followed artists demoted, For You is the in-network surface). Needs a product decision before anyone touches the scoring.
🤖 Generated with Claude Code