Summary
All five data pages are statically generated (○ in the build output), and their deadline-derived state — status (open/closing_soon/closed), daysLeft, and the marker/countdown labels — is computed from new Date() at build time. With no revalidate/ISR configured, "🔥 CLOSING SOON" flags and "N days left" countdowns freeze at the moment of the last build and drift out of sync with real time.
Location
web/lib/listings.ts:110-138 (build-time new Date() + deriveState/daysLeft)
web/lib/types-hq.ts (countdown() consumed by globe-map.tsx, deck.tsx)
- No
export const revalidate/dynamic in any web/app/*/page.tsx (build shows all pages ○ Static).
Evidence
export function loadHackathons(): Hackathon[] {
const raw: RawListing[] = JSON.parse(fs.readFileSync(LISTINGS_PATH, "utf8"));
const today = new Date(); // evaluated at build time on a static page
today.setHours(0, 0, 0, 0);
// ...
daysLeft = Math.ceil((d.getTime() - today.getTime()) / 86_400_000);
Build route table:
┌ ○ /
├ ○ /deck
├ ○ /globe
├ ○ /hackathons
└ ○ /my (○ = prerendered as static content)
Impact
After a deploy, a hackathon whose deadline passes (or moves within 7 days) will keep showing a stale status/countdown until the site is rebuilt. If the host is not wired to rebuild on every push, the countdowns are effectively frozen. If it is git-connected, the daily closing_soon/update_readmes bot commits to main will trigger rebuilds, bounding staleness to ~24h — but this is incidental, undocumented, and breaks the moment auto-deploy is paused. The site's core value proposition ("what's closing soon") depends on this being correct.
Recommended fix
Make the time-derived rendering explicit rather than relying on incidental rebuilds:
- Add
export const revalidate = 3600 (ISR) to the data pages so the server recomputes status/daysLeft on a schedule, or
- Compute the countdown/status on the client from the deadline (client
Date.now()), so it's always live regardless of build time, or
- Document and guarantee the rebuild-on-push pipeline as the intended freshness mechanism.
Acceptance criteria
Summary
All five data pages are statically generated (
○in the build output), and their deadline-derived state —status(open/closing_soon/closed),daysLeft, and the marker/countdown labels — is computed fromnew Date()at build time. With norevalidate/ISR configured, "🔥 CLOSING SOON" flags and "N days left" countdowns freeze at the moment of the last build and drift out of sync with real time.Location
web/lib/listings.ts:110-138(build-timenew Date()+deriveState/daysLeft)web/lib/types-hq.ts(countdown()consumed byglobe-map.tsx,deck.tsx)export const revalidate/dynamicin anyweb/app/*/page.tsx(build shows all pages○ Static).Evidence
Build route table:
Impact
After a deploy, a hackathon whose deadline passes (or moves within 7 days) will keep showing a stale status/countdown until the site is rebuilt. If the host is not wired to rebuild on every push, the countdowns are effectively frozen. If it is git-connected, the daily
closing_soon/update_readmesbot commits tomainwill trigger rebuilds, bounding staleness to ~24h — but this is incidental, undocumented, and breaks the moment auto-deploy is paused. The site's core value proposition ("what's closing soon") depends on this being correct.Recommended fix
Make the time-derived rendering explicit rather than relying on incidental rebuilds:
export const revalidate = 3600(ISR) to the data pages so the server recomputesstatus/daysLefton a schedule, orDate.now()), so it's always live regardless of build time, orAcceptance criteria