From 8fedef93c5deae9ddc0a50045eb12563b46f3fbe Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Sat, 29 Aug 2026 19:24:03 +0000 Subject: [PATCH] Tell a scheduler when the next slice is actually takeable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The manifest advertised `nextWindowOpensAt` as `windowEnd(latestClosedWindow())` — the same value as `latestWindowEnd` on the line above it, and an instant that has already passed. It is the boundary that *closed* the window being served. A buyer's pipeline keying on it would wake immediately, re-pull the slice it already had, and spin until the real boundary arrived. The window starting at that moment is the one still filling, so there is nothing new to take until it closes, one window further on. Renamed to `nextWindowAvailableAt` while it is a day old and nobody is reading it yet: the old name described when a window opens, which is not a fact anyone needs, and reads as the answer to the question they were actually asking. The test asserts the property rather than a fixed string — the served window's own end is in the past, the next slice's is in the future — so it fails for any `now` and cannot be satisfied by the value it replaced. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Pij2tFcRgqceMpheotFSoX --- apps/web/src/app/api/dataset/route.js | 12 +++++++++++- apps/web/test/dataset-window.test.js | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/api/dataset/route.js b/apps/web/src/app/api/dataset/route.js index 183de1c..8005ab6 100644 --- a/apps/web/src/app/api/dataset/route.js +++ b/apps/web/src/app/api/dataset/route.js @@ -51,7 +51,17 @@ export function GET() { 'Each window is the half-open range [start, end) on the row timestamp named in `cutOn`. Windows are fixed against the Unix epoch, so a window is the same set of rows whoever asks and whenever they ask. Walk them in order to see every row exactly once.', latestClosedWindow: newest, latestWindowEnd: windowEnd(newest), - nextWindowOpensAt: windowEnd(newest), + // When the *next* slice becomes takeable, which is not when the next + // window opens — it is when that window closes. + // + // This field shipped as `nextWindowOpensAt` returning `windowEnd(newest)`, + // which is the same value as `latestWindowEnd` directly above it and is + // the wrong instant to hand a scheduler. The window starting at that + // moment is the one still filling, so a pipeline that woke then would + // re-pull the slice it already had and loop until the real boundary + // passed. One window further on is the first moment there is anything + // new to take. + nextWindowAvailableAt: windowEnd(windowEnd(newest)), }, datasets: { diff --git a/apps/web/test/dataset-window.test.js b/apps/web/test/dataset-window.test.js index b4dd0c5..9025fb2 100644 --- a/apps/web/test/dataset-window.test.js +++ b/apps/web/test/dataset-window.test.js @@ -111,6 +111,30 @@ test('a window that is not a timestamp is refused', () => { assert.equal(got.error, 'bad-window'); }); +test('the moment the next slice becomes takeable is always in the future', () => { + // The invariant the manifest got wrong on its first day. It advertised + // `windowEnd(latestClosedWindow())` as when to come back, and that instant has + // *already passed* — it is the boundary that closed the window being served. + // A scheduler obeying it would wake immediately, re-pull the slice it already + // had, and spin until the real boundary arrived. + // + // Asserting "strictly in the future" rather than a fixed string is the point: + // it fails for any now, and it is exactly the property a caller depends on. + for (const offsetMinutes of [1, 59, 60, 121, 239]) { + const now = Date.parse('2026-08-29T12:00:00.000Z') + offsetMinutes * 60_000; + const newest = latestClosedWindow(now); + + assert.ok( + Date.parse(windowEnd(newest)) <= now, + 'the served window has closed, so its own end is in the past', + ); + assert.ok( + Date.parse(windowEnd(windowEnd(newest))) > now, + `next slice must be takeable in the future, at +${offsetMinutes}m`, + ); + } +}); + test('the UTC day starts at midnight UTC wherever the server thinks it is', () => { // The full-dump allowance is a per-UTC-day count, and a server in a westward // timezone using local midnight would hand out a second full dump hours early