Heartbeat honesty: SYNC badge reads refreshed_at, not grade-lock updated_at
The "SIGNAL LIVE vs STALE 8h" contradiction was a field mismatch, not a dead pipeline. updated_at is the grade-LOCK time (advances only on a full snapshot, 5×/day — grades never change in-game, so it is intentionally stable). The SYNC badge measured the 20-min intraday cadence (expected_interval_s=1200) against that 5×/day field → structurally guaranteed STALE between slots even when intraday refreshes lines perfectly. - snapshotService: full snapshot now seeds refreshed_at at lock time - intradayRefresh already bumps refreshed_at every ~20 min (unchanged) - /api/snapshot/summary + GET /:sport now expose refreshed_at (was written to Redis but never serialized → no public liveness signal existed) - LiveLayer SYNC badge measures freshness from refreshed_at (fallback updated_at) Exposing refreshed_at also gives a public heartbeat probe: it advances every intraday slot, so pipeline liveness is verifiable without container logs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,28 @@ describe('GET /api/snapshot/summary', () => {
|
||||
expect(res.body.sports.nba).toBe(0); // off-season → honest zero
|
||||
});
|
||||
|
||||
test('refreshed_at is the freshness heartbeat, distinct from the grade-lock updated_at', async () => {
|
||||
// updated_at = grade LOCK (5×/day, stable). refreshed_at = last intraday
|
||||
// line refresh (~20 min). The SYNC badge measures against refreshed_at —
|
||||
// measuring the 20-min cadence against the lock field caused "STALE 8h".
|
||||
store['snapshot:mlb:latest'] = {
|
||||
updated_at: '2026-07-10T18:00:00.000Z', // graded at 2pm ET
|
||||
refreshed_at: '2026-07-10T21:40:00.000Z', // lines refreshed at 5:40pm ET
|
||||
grades: [{ player: 'A' }],
|
||||
};
|
||||
const res = await request(mountApp()).get('/api/snapshot/summary');
|
||||
expect(res.body.updated_at).toBe('2026-07-10T18:00:00.000Z');
|
||||
expect(res.body.refreshed_at).toBe('2026-07-10T21:40:00.000Z');
|
||||
});
|
||||
|
||||
test('refreshed_at falls back to updated_at when a snapshot predates the field', async () => {
|
||||
// Old snapshots written before this fix have no refreshed_at — the freshness
|
||||
// signal must still resolve (never null while a grade lock exists).
|
||||
store['snapshot:mlb:latest'] = { updated_at: '2026-07-10T18:00:00.000Z', grades: [{ player: 'A' }] };
|
||||
const res = await request(mountApp()).get('/api/snapshot/summary');
|
||||
expect(res.body.refreshed_at).toBe('2026-07-10T18:00:00.000Z');
|
||||
});
|
||||
|
||||
test('falls back to the grades:{sport} envelope when no snapshot exists', async () => {
|
||||
store['grades:mlb'] = { updated_at: '2026-07-10T16:00:00.000Z', grades: [{ player: 'A' }, { player: 'B' }] };
|
||||
const res = await request(mountApp()).get('/api/snapshot/summary');
|
||||
|
||||
Reference in New Issue
Block a user