fix(stats): finalize snapshots on the verse-day window

A verse date is a player's local calendar day, spanning D-1 10:00Z
(UTC+14) through D+1 12:00Z (UTC-12). The old UTC end-of-day cutoff both
dropped late-night finishes in negative timezones and could persist a
partial day.

- Scope snapshot loads by the verse date column; use completedBefore only
  to pin an in-progress point-in-time build.
- Gate persistence on isDayClosed (D+1 12:00Z); open days are computed
  live and never written.
- Build the cron snapshot for latestClosedDate instead of EST yesterday,
  and read the same date for the community page.
- Add all-time best single day to snapshot totals.
- Bump PAYLOAD_SCHEMA_VERSION to 4 and extend snapshot/window tests.
This commit is contained in:
George Powell committed 2026-09-28 20:21:47 -04:00
1 parent 45ffa39f2a
commit a562ee3cc9
10 files changed
+198 -52

No files matched your search

+6 -7
View File
@@ -1,15 +1,13 @@
import type { PageServerLoad } from './$types';
import { ensureSnapshot } from '$lib/server/stats/snapshot';
import { addDays } from '$lib/server/stats/dates';
import { latestClosedDate } from '$lib/server/stats/dates';
/**
* Slim public community widget. Reads the latest daily snapshot and exposes
* only safe aggregates (no retention, churn, signup or revenue data).
* Slim public community widget. Reads the latest completed daily snapshot and
* exposes only safe aggregates (no retention, churn, signup or revenue data).
*/
export const load: PageServerLoad = async () => {
const todayEst = new Date().toLocaleDateString('en-CA', { timeZone: 'America/New_York' });
const yesterday = addDays(todayEst, -1);
const snapshot = await ensureSnapshot(yesterday);
const snapshot = await ensureSnapshot(latestClosedDate());
const payload = snapshot.payload;
return {
@@ -20,6 +18,7 @@ export const load: PageServerLoad = async () => {
monthlyPlayers: payload.activity.mau,
yesterdayCompletions: payload.totals.yesterdayCompletions,
yesterdayAvgGuesses: payload.totals.yesterdayAvgGuesses,
calendar: payload.habit.calendar.slice(-30)
bestDayAllTime: payload.totals.bestDay,
calendar: payload.habit.calendar.slice(-90)
};
};