You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Ok another small thing -- in the card catalog, it always seems to say 0 plays on the release popup window, e.g. dj.wxyc.org/dashboard/album/56696 (this release has 135 plays in search results)
The album detail modal reports 0 plays for every release. The same release in the search results reports its real count. This is BS#1489's defect, unfixed on a second endpoint.
Root cause
library.plays is a dead physical column that nothing maintains — it is 0 for every row. The real per-album count lives in the album_plays materialized view. BS#1489 fixed GET /library/query by joining the MV and sourcing the value from it:
getAlbumFromDB — the query behind GET /library/info — was not part of that change and still projects the dead column. Note it feeds five endpoints, not one: GET /library/info (library.controller.ts:1645), PATCH /library/:id (:1896, :1938), markMissing (:1996) and markFound (:2006). All five ship plays: 0 today, and all five are fixed by the same edit:
LIBRARY_VIEW_PROJECTION carries the same plays: library.plays — at library.service.ts:1763, not 3202 as this ticket first said. Leave it alone for this fix (investigated; see the comment below). It is a hand-written base-table mirror, not the view definition, and changing it means widening a satisfies Record<keyof …, Column> constraint that rejects an SQL expression, making the album_plays join unconditional, adding it to LIBRARY_VIEW_JOINS_RAW (which has none), and re-verifying the CTA arm's prepended joins. That is a different and larger change than the reported bug, and BS#1489 made exactly this argument in-repo (library-search.service.ts:100-103).
Desired end state
GET /library/info?album_id=56696 reports the same play count the search results report for the same release. No client change: dj-site already renders album.plays ?? 0 and will show the real number once the endpoint sends one.
Suggested approach
Add the album_plays LEFT JOIN to getAlbumFromDB and project COALESCE(album_plays.plays, 0) in place of library.plays. The join is on album_plays.album_id = library.id and is a left join on a unique key, so it cannot change the row count. getAlbumByLegacyId calls straight through to getAlbumFromDB, so the legacy-keyed permalink front door is fixed by the same edit.
Check the MV's refresh cadence (album-plays-refresh.service.ts) and state it in the PR — the detail modal will be as fresh as the search results and no fresher, which is fine, but it should be a known property rather than a surprise.
Constraints
Widening the value is not a contract change, and the spec is already on the fix's side: AlbumDetail.plays is declared at wxyc-shared/api.yaml:2591-2593 as "Station play count for this release" — optional, non-nullable, no bounds. Shipping 0 is the implementation violating that description; shipping the real count brings it into compliance. There is no runtime validator in either package to trip.
Consumers are safe. Only dj-site reads plays off a detail response — AlbumCard.tsx:226, {album.plays ?? 0} plays, no zero-branch and no sentinel. iOS and Android have zero readers (wxyc-ios-64 declares the field on generated models nothing references; the other three repos have no matches). The zero-special-casing that does exist (Results/Result.tsx:168,173, MobileResult.tsx:51) is on the search path and is not fed by /library/info.
Read-only change. No migration, no write path.
Acceptance criteria
GET /library/info?album_id=<id> and GET /library/query return the same plays for the same release.
Verified against release 56696 specifically (135 plays per Kate's report).
GET /library/info?legacy_release_id=<id> fixed by the same change (it routes through getAlbumFromDB).
Test covering a release with a non-zero count and one with none (COALESCE → 0, not null).
Decision recorded on whether LIBRARY_VIEW_PROJECTION.plays is changed or deliberately left. Investigated: leave it, track separately. See above.
AlbumCard.tsx:226 hardcodes the plural, so a genuine count of 1 renders "1 plays" — unreachable today, reachable the moment this ships. Fix it in dj-site alongside, or file it; do not let the first correct value be a grammar bug.
Note in the PR that this also repairs a pre-existing dj-site defect: patchSearchCaches.ts:137-146 splices the album-detail row wholesale into the search cache (:187-189), so classifying rotation from the modal currently inserts a plays: 0 row into a search page — rendering — at a wrong sort slot. The existing.plays ?? updated.plays guard at patchSearchResult.ts:59 does not cover that path.
Confirm the refresh job is actually running in prod.It is — album-plays-refresh last ran 2026-09-08 18:14 PDT, 32 min before the check. The modal will be at most ~1h stale and exactly as stale as the search results it is compared against. The design flaw stands (first refresh at +1 interval, not at boot, and nothing reads cronjob_runs) but is not currently mis-serving anyone — worth a separate hardening ticket, not a blocker here.
Problem
Kate Bailey, on the station Slack (2026-09-08):
The album detail modal reports
0 playsfor every release. The same release in the search results reports its real count. This is BS#1489's defect, unfixed on a second endpoint.Root cause
library.playsis a dead physical column that nothing maintains — it is 0 for every row. The real per-album count lives in thealbum_playsmaterialized view. BS#1489 fixedGET /library/queryby joining the MV and sourcing the value from it:Backend-Service/apps/backend/services/library-search.service.ts
Lines 94 to 106 in 8e6175e
getAlbumFromDB— the query behindGET /library/info— was not part of that change and still projects the dead column. Note it feeds five endpoints, not one:GET /library/info(library.controller.ts:1645),PATCH /library/:id(:1896,:1938),markMissing(:1996) andmarkFound(:2006). All five shipplays: 0today, and all five are fixed by the same edit:Backend-Service/apps/backend/services/library.service.ts
Line 3192 in 8e6175e
Route:
Backend-Service/apps/backend/routes/library.route.ts
Line 201 in 8e6175e
LIBRARY_VIEW_PROJECTIONcarries the sameplays: library.plays— atlibrary.service.ts:1763, not 3202 as this ticket first said. Leave it alone for this fix (investigated; see the comment below). It is a hand-written base-table mirror, not the view definition, and changing it means widening asatisfies Record<keyof …, Column>constraint that rejects an SQL expression, making thealbum_playsjoin unconditional, adding it toLIBRARY_VIEW_JOINS_RAW(which has none), and re-verifying the CTA arm's prepended joins. That is a different and larger change than the reported bug, and BS#1489 made exactly this argument in-repo (library-search.service.ts:100-103).Desired end state
GET /library/info?album_id=56696reports the same play count the search results report for the same release. No client change: dj-site already rendersalbum.plays ?? 0and will show the real number once the endpoint sends one.Suggested approach
Add the
album_playsLEFT JOIN togetAlbumFromDBand projectCOALESCE(album_plays.plays, 0)in place oflibrary.plays. The join is onalbum_plays.album_id = library.idand is a left join on a unique key, so it cannot change the row count.getAlbumByLegacyIdcalls straight through togetAlbumFromDB, so the legacy-keyed permalink front door is fixed by the same edit.Check the MV's refresh cadence (
album-plays-refresh.service.ts) and state it in the PR — the detail modal will be as fresh as the search results and no fresher, which is fine, but it should be a known property rather than a surprise.Constraints
AlbumDetail.playsis declared atwxyc-shared/api.yaml:2591-2593as "Station play count for this release" — optional, non-nullable, no bounds. Shipping0is the implementation violating that description; shipping the real count brings it into compliance. There is no runtime validator in either package to trip.playsoff a detail response —AlbumCard.tsx:226,{album.plays ?? 0} plays, no zero-branch and no sentinel. iOS and Android have zero readers (wxyc-ios-64declares the field on generated models nothing references; the other three repos have no matches). The zero-special-casing that does exist (Results/Result.tsx:168,173,MobileResult.tsx:51) is on the search path and is not fed by/library/info.Acceptance criteria
GET /library/info?album_id=<id>andGET /library/queryreturn the sameplaysfor the same release.GET /library/info?legacy_release_id=<id>fixed by the same change (it routes throughgetAlbumFromDB).COALESCE→ 0, not null).Decision recorded on whetherInvestigated: leave it, track separately. See above.LIBRARY_VIEW_PROJECTION.playsis changed or deliberately left.AlbumCard.tsx:226hardcodes the plural, so a genuine count of 1 renders "1 plays" — unreachable today, reachable the moment this ships. Fix it in dj-site alongside, or file it; do not let the first correct value be a grammar bug.patchSearchCaches.ts:137-146splices the album-detail row wholesale into the search cache (:187-189), so classifying rotation from the modal currently inserts aplays: 0row into a search page — rendering—at a wrong sort slot. Theexisting.plays ?? updated.playsguard atpatchSearchResult.ts:59does not cover that path.Confirm the refresh job is actually running in prod.It is —album-plays-refreshlast ran 2026-09-08 18:14 PDT, 32 min before the check. The modal will be at most ~1h stale and exactly as stale as the search results it is compared against. The design flaw stands (first refresh at +1 interval, not at boot, and nothing readscronjob_runs) but is not currently mis-serving anyone — worth a separate hardening ticket, not a blocker here.Related
LIBRARY_VIEW_PROJECTIONcascade this ticket deliberately defers."1 plays"plural this fix makes reachable. Land together or land that first.GET /library/query?sort=plays; the fix there is the template for this one.plays = 0; popularity ranking needs attribution-corrected play data #1486 — not the same defect, and not a blocker. The catalog-export leg was already fixed bybec7db3c("source catalog-export plays from album_plays MV"); Catalog export shipsplays = 0; popularity ranking needs attribution-corrected play data #1486 remains open as the Phase-2 attribution epic (the free-text tail and master collapse). If its body still says the export shipsplays = 0, that text is stale.