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
Two of the three Phase 0 D3 successor pages were reverted off websitemain on 2026-08-26 because they render historical DJ identity that can hold DJs' real names. WXYC/website#230 decided on 2026-08-27 to accept that gap through the tubafrenzy turndown rather than slip Milestone 1 or ship the pages with identity stripped.
That decision settled the cutover. It did not schedule the rollforward, and #230 closed with the restoration recorded only as "reopen this issue then, or file a fresh one." This is that fresh one. Nothing else on org Project #36 currently represents the intent to bring these surfaces back.
wxyc.org/playlist (#211, succeeding /playlists/recent at ~224/day) is unaffected and stays up — it serves only recent entries, all written after the write-path fix.
Traffic figures are from the Phase 0 access-log measurement, bot traffic netted out. So the largest listener surface on wxyc.info after recentEntries is currently retiring with no replacement, and the smallest keeps one.
Timing — deliberately deferred, not stalled
The fixed rollforward is deferred until after tubafrenzy is switched off (Milestone 1, 2026-09-07, WXYC/wiki#93). Decided 2026-08-29. This ticket is After 9/7 — chain-gated on Project #36 and should not be pulled into the cutover.
Two reasons, both structural rather than scheduling preference:
Milestone 1's own gate is DJ-account migration, not engineering (24 scheduled DJs still lack a usable Backend account — WXYC/wiki#125). Adding a public-surface restoration to that week competes for the same attention with no listener benefit until the scrub lands anyway.
Root cause (the exposure is in the data, not in these pages)
flowsheet.dj_name is a denormalized snapshot taken at play time, and projectFlowsheetEntry passes it through raw, so dj_join / dj_leave marker rows still carry whatever was resolved at write time. Show headings are exposed by a different route: resolveShowDjName's third step is shows.legacy_dj_name, backfilled from tubafrenzy and already holding real names for historical shows. resolveDjDisplayName is correct today — WXYC/Backend-Service#1286 removed auth_user.name from the chain — but a resolution helper being correct is not the same as the stored data conforming to it (the distinction WXYC/Backend-Service#2281 draws).
Sampling one 2015 week through GET /flowsheet/range: 10 of 46 distinct marker-row dj_name values and 12 of 50 show-heading values match a "Firstname Lastname" shape. That is a regex, so it counts two-word handles as hits and misses single-word real first names — it bounds the exposure as substantial, not exactly.
Consequence: any public page rendering historical DJ identity has this problem. Restoring these pages before the scrub would just move the exposure back.
Desired end state
Both pages are back on main and live at wxyc.org, rendering DJ identity that conforms to the current resolution policy, with the wxyc.info successor story whole again.
Where
Recoverable whole from the parent commits — this is a re-add, not a rewrite:
pages/airplay-search.jsx and __tests__/airplaySearch.test.jsx — at 43c433d^
pages/playlists/archive.jsx and __tests__/archivePlaylists.test.jsx — at 90643dc^
Both reverts deliberately left the data layer in place, so restoring is re-adding the page components alone:
Also touched by both reverts and needing the inverse edit:
content/page/programming.mdx — the links to both pages were removed so the reverts left no dead links; re-add them.
__tests__/wxycInfoLinks.test.js — carries the deliberate-gap note; remove it when the gap closes. The turndown guard itself (no linking back to wxyc.info) stays.
README.md — both reverts removed the pages' sections.
Backend surfaces, both already live, public, and CORS-cleared for wxyc.org — no Backend work is in front of this beyond the scrub:
Re-verify the scrub against the data before re-adding the pages. #2281 landing is necessary, not sufficient — re-run the 2015-week sampling above (and at least one other decade) through /flowsheet/range and confirm the marker-row and show-heading counts have gone to zero. shows.legacy_dj_name and flowsheet.dj_name are two different exposure routes and the scrub must be confirmed on both.
The archive page takes the week as a query param (/playlists/archive?week=YYYY-MM-DD) — no SSR and no getStaticPaths over 2.6M rows.
Calendar days are not 24h and calendar weeks are not 168h. Derive end with calendar arithmetic, never start + 86400000. Verified live: 2026-03-08 is 23h, 2025-11-02 is 25h, and the fall-back week is 169h — which is why MAX_RANGE_MS is 8 days, not 7.
The repo cannot vitest a JSX-bearing .js file (esbuild reports "The JSX syntax extension is not currently enabled" despite vitest.config.js's esbuild.include). Any test-covered page or component must be named .jsx. Both restored pages already are.
Flowsheet history reaches back to at least 2004-11-01, not 2009 as some wxyc.org copy advertises.
Re-sampling historical weeks through /flowsheet/range finds no real-name-shaped values on marker rows or show headings.
pages/airplay-search.jsx and pages/playlists/archive.jsx are back on main, with their tests restored and passing.
content/page/programming.mdx links to both pages again; README.md sections restored.
The deliberate-gap note is removed from __tests__/wxycInfoLinks.test.js; the wxyc.info turndown guard still passes.
Live verification: https://wxyc.org/airplay-search and https://wxyc.org/playlists/archive both return 200 and render real data.
WXYC/wiki#93's "Accepted listener-surface gap" section is updated to record the gap as closed.
Notes for implementer
The legacy wxyc.info/radioWeek grid had defects found during the #215 build — it silently dropped shows on slot collisions (show 172677 was absent from the grid while radioShow?radioShowID=172677 returned a full playlist), could not render zero-height blocks, and clipped overnight shows at midnight, filing the after-midnight half under the next day. These are not parity targets. The successor page was built without them; don't reintroduce them chasing a faithful copy.
A week of range data costs 2,300–2,800 entries / 470–640 KB gzipped / 1.2–1.8s, which is why the page renders each show as a collapsed <details> rather than laying out ~2,500 rows.
website/CLAUDE.md is untracked in this repo — README.md is the committed documentation surface.
Problem
Two of the three Phase 0 D3 successor pages were reverted off
websitemainon 2026-08-26 because they render historical DJ identity that can hold DJs' real names. WXYC/website#230 decided on 2026-08-27 to accept that gap through the tubafrenzy turndown rather than slip Milestone 1 or ship the pages with identity stripped.That decision settled the cutover. It did not schedule the rollforward, and #230 closed with the restoration recorded only as "reopen this issue then, or file a fresh one." This is that fresh one. Nothing else on org Project #36 currently represents the intent to bring these surfaces back.
wxyc.infosurface it succeeded43c433dpages/airplay-search.jsx/playlists/searchPlaylists90643dcpages/playlists/archive.jsx/playlists/radioWeek+ its/playlists/radioShowdrill-inwxyc.org/playlist(#211, succeeding/playlists/recentat ~224/day) is unaffected and stays up — it serves only recent entries, all written after the write-path fix.Traffic figures are from the Phase 0 access-log measurement, bot traffic netted out. So the largest listener surface on
wxyc.infoafterrecentEntriesis currently retiring with no replacement, and the smallest keeps one.Timing — deliberately deferred, not stalled
The fixed rollforward is deferred until after tubafrenzy is switched off (Milestone 1, 2026-09-07, WXYC/wiki#93). Decided 2026-08-29. This ticket is
After 9/7 — chain-gatedon Project #36 and should not be pulled into the cutover.Two reasons, both structural rather than scheduling preference:
STORED GENERATEDtsvector column, on which migration 0053 already wedged on locks once (Migration 0053 wedge: split DDL from backfill, harden migration runner Backend-Service#511). Running that against prod during the week a cutover lands is the wrong risk to stack.Root cause (the exposure is in the data, not in these pages)
flowsheet.dj_nameis a denormalized snapshot taken at play time, andprojectFlowsheetEntrypasses it through raw, sodj_join/dj_leavemarker rows still carry whatever was resolved at write time. Show headings are exposed by a different route:resolveShowDjName's third step isshows.legacy_dj_name, backfilled from tubafrenzy and already holding real names for historical shows.resolveDjDisplayNameis correct today — WXYC/Backend-Service#1286 removedauth_user.namefrom the chain — but a resolution helper being correct is not the same as the stored data conforming to it (the distinction WXYC/Backend-Service#2281 draws).Sampling one 2015 week through
GET /flowsheet/range: 10 of 46 distinct marker-rowdj_namevalues and 12 of 50 show-heading values match a "Firstname Lastname" shape. That is a regex, so it counts two-word handles as hits and misses single-word real first names — it bounds the exposure as substantial, not exactly.Consequence: any public page rendering historical DJ identity has this problem. Restoring these pages before the scrub would just move the exposure back.
Desired end state
Both pages are back on
mainand live atwxyc.org, rendering DJ identity that conforms to the current resolution policy, with thewxyc.infosuccessor story whole again.Where
Recoverable whole from the parent commits — this is a re-add, not a rewrite:
pages/airplay-search.jsxand__tests__/airplaySearch.test.jsx— at43c433d^pages/playlists/archive.jsxand__tests__/archivePlaylists.test.jsx— at90643dc^Both reverts deliberately left the data layer in place, so restoring is re-adding the page components alone:
lib/flowsheetSearch.js+__tests__/flowsheetSearch.test.jslib/flowsheetRange.js+__tests__/flowsheetRange.test.jscomponents/OrganizingArchive.jsAlso touched by both reverts and needing the inverse edit:
content/page/programming.mdx— the links to both pages were removed so the reverts left no dead links; re-add them.__tests__/wxycInfoLinks.test.js— carries the deliberate-gap note; remove it when the gap closes. The turndown guard itself (no linking back towxyc.info) stays.README.md— both reverts removed the pages' sections.Backend surfaces, both already live, public, and CORS-cleared for
wxyc.org— no Backend work is in front of this beyond the scrub:GET /flowsheet/range(feat(flowsheet): add GET /flowsheet/range?start=&end= returning {shows, entries} Backend-Service#2062)GET /flowsheet/search(search.controller.ts, routed inflowsheet.route.ts)Constraints
/flowsheet/rangeand confirm the marker-row and show-heading counts have gone to zero.shows.legacy_dj_nameandflowsheet.dj_nameare two different exposure routes and the scrub must be confirmed on both.websiteis a static export (output: 'export'), so these pages fetchapi.wxyc.orgfrom the browser. Public read CORS is already in prod viaPUBLIC_READ_ORIGINS=https://wxyc.org,https://www.wxyc.org(feat(cors): allow wxyc.org origins on the public flowsheet read routes Backend-Service#2061/#2075) — credential-less, scoped to the public GET routes. Don't route this throughFRONTEND_SOURCE; that grants credentialed access org-wide (the cors: '*' fallback combined with credentials:true when FRONTEND_SOURCE unset Backend-Service#1107 hazard)./playlists/archive?week=YYYY-MM-DD) — no SSR and nogetStaticPathsover 2.6M rows.endwith calendar arithmetic, neverstart + 86400000. Verified live: 2026-03-08 is 23h, 2025-11-02 is 25h, and the fall-back week is 169h — which is whyMAX_RANGE_MSis 8 days, not 7..jsfile (esbuildreports "The JSX syntax extension is not currently enabled" despitevitest.config.js'sesbuild.include). Any test-covered page or component must be named.jsx. Both restored pages already are.wxyc.orgcopy advertises.Acceptance criteria
/flowsheet/rangefinds no real-name-shaped values on marker rows or show headings.pages/airplay-search.jsxandpages/playlists/archive.jsxare back onmain, with their tests restored and passing.content/page/programming.mdxlinks to both pages again;README.mdsections restored.__tests__/wxycInfoLinks.test.js; thewxyc.infoturndown guard still passes.https://wxyc.org/airplay-searchandhttps://wxyc.org/playlists/archiveboth return 200 and render real data.Notes for implementer
The legacy
wxyc.info/radioWeekgrid had defects found during the #215 build — it silently dropped shows on slot collisions (show 172677 was absent from the grid whileradioShow?radioShowID=172677returned a full playlist), could not render zero-height blocks, and clipped overnight shows at midnight, filing the after-midnight half under the next day. These are not parity targets. The successor page was built without them; don't reintroduce them chasing a faithful copy.A week of range data costs 2,300–2,800 entries / 470–640 KB gzipped / 1.2–1.8s, which is why the page renders each show as a collapsed
<details>rather than laying out ~2,500 rows.website/CLAUDE.mdis untracked in this repo —README.mdis the committed documentation surface.Related
wxyc.infolink repointdj_namescrub (blocker)