Skip to content

Commit 744b73f

Browse files
test(verify): the dashboard-window runs freeze Date at noon UTC, pinned from a wall clock 100 ms before midnight (#23583)
Fixes #23351 Clause-②: no ## What changed `packages/verify/src/dashboard-window.test.ts` only. The probe (`packages/verify/src/dashboard-window.ts`) is not edited: its day-turn `not-run` is a correct answer and stays as it is. No case is skipped. - **The freeze.** One helper, `onFrozenClock(run)`, fakes `Date` only (`vi.useFakeTimers({ toFake: ['Date'] })`; every timer stays real) at noon UTC of the day the run starts on, and gives the real clock back in `finally`. Every `runDashboardWindowProofs` call in the file goes through it. - **The pin.** `wallClockAtMidnight()` sets the wall clock to 23:59:59.900Z of today, still moving at its real pace (`shouldAdvanceTime: true`, `Date` only). The positive fixture run and both NEGATIVE CONTROL runs start from it, then go through `onFrozenClock`. With the freeze they pass. Without it they cross midnight in their first measure and fail with the day-turn reason on any day, not only on a day whose run crosses 00:00Z (ablation below). The pin is in the file, so it stays, and it costs no extra boot. - **One spelling.** THE DAY TURN block now uses the same helper instead of its own copy of the fake-`Date` setup. It still moves the clock one day on the run's first door call. It now starts from noon of the start day instead of the real instant. ## Inventory, on `origin/main` 252775d Every case that runs the family and asserts a verdict or reason other than the day turn: | block | cases | real clock before this PR | what a day turn changes | |---|---|---|---| | `runDashboardWindowProofs — the fixture app` | 4, one shared `beforeAll` run | yes | "proves every bound count widget…" asserts `proven`; "counts every dashboard…" asserts `proven: 10`, `notRun: 0` | | NEGATIVE CONTROL (drop) | 2 | yes | `contradicted`, the `logged_at` rewrite reason, the `audited_at` unconfirmed reason | | NEGATIVE CONTROL (zero) | 2 | yes | the same; these two went red in merge-queue run 38486827994 | | THE DAY TURN | 1 | no, already faked `Date` | asserts the day turn itself | | `deriveDashboardWindowCases` | 2 | no clock read | none | The freeze sits in each run's `beforeAll`, so it covers all 8 cases of the three runs. ## Mechanism, measured - The probe reads the clock through `new Date()` only (`dashboard-window.ts` lines 575, 795 and 884). The server resolves the same presets on `new Date()` in this process (`packages/services/service-analytics/src/dataset-executor.ts` line 127, `filterTokenContextFrom(context, new Date())`). So faking `Date` reaches both sides, and the frozen run's presets agree with the server's. - Timers stay real, so nothing the run waits on stalls. Wall time, the base file against this file, alternated twice, each pair in one lock hold on a shared box (ratios survive the sharing, the absolutes do not): | pair | this file: wall / vitest `tests` | base file: wall / vitest `tests` | ratio, `tests` | |---|---|---|---| | A | 38.5 s / 13.55 s | 41.1 s / 13.41 s | 1.01 | | B | 43.1 s / 14.74 s | 41.6 s / 13.42 s | 1.10 | ## Ablation, and a one-shot check of the other side of noon Both legs went through `scripts/ablation-replace.mjs` (anchor must hit, blob checked) inside a script with its own `trap` restore. Both were restored to the HEAD blob `49740f917f02` with `git diff HEAD` empty. No mutation file is left. - **Freeze removed, wall clock at 23:59:59.900Z.** The two `onFrozenClock` lines that fake and set `Date` are replaced. Anchor 1 → 0, blob `49740f917f02` → `4d765d5b68a4`; on disk the freeze anchor counts 0 and the injected marker 1. Result: `Tests 6 failed | 5 passed (11)`. The failures are the positive run's "proves every bound count widget…" and "counts every dashboard…", plus all four NEGATIVE CONTROL cases. Every measure reads `inconclusive: the day turned after the run started (2026-10-15 → 2026-10-16) …` or `the day turned during the probe …`. The ATTRIBUTION case fails with `expected 'the day turned after the run started …' to match /reads back logged_at = "2020-01-01T00…/`, which is run 38486827994's signature, now on demand. THE DAY TURN, the two derive cases and the two positive cases a day turn does not change stay green. - **Frozen at noon of the previous day.** This is about 13 h before the session's real sign-in: the other side of the noon-versus-now offset this suite meets over a day. At 01:2xZ today's noon is about 10.5 h ahead. Anchor 1 → 0, blob → `9336e35e133d`. Result: `Tests 11 passed (11)`. A frozen instant hours away from the real boot, in either direction, does not change a verdict. ## Tests and gates, all at `c9e7b172f` - `pnpm --filter @objectstack/verify exec vitest run --maxWorkers=2 src/dashboard-window.test.ts`: `Tests 11 passed (11)`, three runs: the verbose run and the fixed-file leg of each timing pair. Before the edit, at 252775d: `Tests 11 passed (11)`. - `pnpm --filter @objectstack/verify typecheck`: exit 0 (`tsc --noEmit`, then `check:test-typecheck`: 0 errors). `tsc --listFiles -p tsconfig.test.json` holds `dashboard-window.test.ts` (1 of the 41 test files in the program). - eslint over `packages/verify` with `--no-inline-config`: exit 0. 59 files, 0 errors, 0 warnings, and the target file was linted. Repo-wide `pnpm lint` is left to CI. - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`: 50 commands, each run with its exit code recorded before any pipe. `--ran` reports 50 derived, 49 run (all exit 0), 1 NOT MEASURED, 0 unrun. The NOT MEASURED one is `pnpm check:dual-build-cjs-loads`: exit 3, PREREQUISITE NOT MET. It reads every package's `dist/` and needs a whole-workspace `pnpm build`. This diff changes no built output, and CI runs it. - `skip-changeset`, by its criterion. `files[]` is `dist`, `README.md` and `CHANGELOG.md`. After `pnpm --filter @objectstack/verify build`, `onFrozenClock` and `wallClockAtMidnight` hit 0 files there, while the positive control `runDashboardWindowProofs` hits 4. ## Acceptance notes - `packages/cli/test/verify-dashboard-windows.test.ts` runs the same family through a spawned `os verify --dashboard-windows` child on the real clock. It asserts `proven` and `contradicted`, so a run that crosses 00:00Z would read `not-run` there too. `vi.useFakeTimers` cannot reach a child process, so the fix there is not this mechanical edit, and that file is outside this card's file surface. This is a read-only inference and has not been measured red. Carrier: none. - Inside a frozen run the structured logger stamps every line `12:00:00.000Z`, because it reads `Date` too. Log times there are not wall time. - `origin/main` gained 3 commits after the base (hono adapter, metadata-protocol, objectql tests). None touches `packages/verify`, and there is no conflict, so they are not merged in. --- _Generated by [Claude Code](https://claude.ai/code/session_01FLwgbTSmGMrqsXkBDsiwfG)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent cac0be8 commit 744b73f

1 file changed

Lines changed: 53 additions & 13 deletions

File tree

‎packages/verify/src/dashboard-window.test.ts‎

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
// • the DAY TURN: a run that crosses UTC midnight between its start (where
1616
// the presets resolve) and a measure's probe does not run that measure,
1717
// never contradicts it.
18+
//
19+
// Every run here is made on a frozen clock (`onFrozenClock`, below), so no
20+
// verdict depends on the hour the suite runs at; the runs that judge anything
21+
// but the day turn start from a wall clock 100 ms before UTC midnight
22+
// (`wallClockAtMidnight`), so a run that lost its frozen clock fails on any
23+
// day, not only on the one whose run crosses 00:00Z.
1824

1925
import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
2026

@@ -139,6 +145,43 @@ const widgetOf = (report: DashboardWindowReport, widget: string) => {
139145
return r;
140146
};
141147

148+
// ── The clock ───────────────────────────────────────────────────────────────
149+
150+
const utcDay = (now: Date) => now.toISOString().slice(0, 10);
151+
152+
/**
153+
* Run `run` on a clock frozen at noon UTC of the day it starts on, then give
154+
* the real clock back. The family reads the clock through `Date` alone: once
155+
* when the run starts (the presets resolve there), then per measure for its
156+
* fixture day; the server it asks resolves the same presets on `new Date()`
157+
* in this process. So only `Date` is faked (every timer stays real), and a
158+
* run on the real clock that crosses UTC midnight between those reads gets
159+
* the probe's day-turn `not-run`, a correct answer and the wrong one for a
160+
* case that asserts any other verdict.
161+
*/
162+
async function onFrozenClock<T>(run: () => Promise<T>): Promise<T> {
163+
const day = utcDay(new Date());
164+
vi.useFakeTimers({ toFake: ['Date'] });
165+
vi.setSystemTime(Date.parse(`${day}T12:00:00.000Z`));
166+
try {
167+
return await run();
168+
} finally {
169+
vi.useRealTimers();
170+
}
171+
}
172+
173+
/**
174+
* The pin: the wall clock 100 ms before UTC midnight on today's date, moving
175+
* at its real pace (only `Date` is faked; the clock ticks on a real interval).
176+
* A run started on it without `onFrozenClock` crosses midnight in its first
177+
* measure and is not run.
178+
*/
179+
function wallClockAtMidnight(): void {
180+
const day = utcDay(new Date());
181+
vi.useFakeTimers({ toFake: ['Date'], shouldAdvanceTime: true });
182+
vi.setSystemTime(Date.parse(`${day}T23:59:59.900Z`));
183+
}
184+
142185
describe('deriveDashboardWindowCases — what the family reads from the metadata', () => {
143186
it("reads the binding through the spec's parse and names every widget it cannot compare", () => {
144187
const cases = new Map(deriveDashboardWindowCases(source).map((c) => [`${c.dashboard}/${c.widget}`, c]));
@@ -178,7 +221,9 @@ describe('runDashboardWindowProofs — the fixture app', () => {
178221

179222
beforeAll(async () => {
180223
stack = await bootStack(app as never);
181-
report = await runDashboardWindowProofs(stack, await stack.signIn(), app);
224+
const token = await stack.signIn();
225+
wallClockAtMidnight();
226+
report = await onFrozenClock(() => runDashboardWindowProofs(stack, token, app));
182227
}, BOOT_TIMEOUT + RUN_TIMEOUT);
183228

184229
afterAll(async () => {
@@ -336,7 +381,9 @@ describe.each(['drop', 'zero'] as const)("NEGATIVE CONTROL — an app plugin tha
336381

337382
beforeAll(async () => {
338383
stack = await bootStack(app as never);
339-
report = await runDashboardWindowProofs(stack, await stack.signIn(), app);
384+
const token = await stack.signIn();
385+
wallClockAtMidnight();
386+
report = await onFrozenClock(() => runDashboardWindowProofs(stack, token, app));
340387
}, BOOT_TIMEOUT + RUN_TIMEOUT);
341388

342389
afterAll(async () => {
@@ -404,12 +451,9 @@ describe("THE DAY TURN — a run that crosses UTC midnight between its start and
404451
beforeAll(async () => {
405452
stack = await bootStack(app as never);
406453
const token = await stack.signIn();
407-
// The family reads the clock through `Date` alone: once when the run
408-
// starts (the presets resolve there), then per measure for its fixture day.
409-
// So only `Date` is faked (every timer stays real), frozen at the real
410-
// instant, and moved one day on the run's first door call: the measure's
411-
// control leg, which falls between those two reads.
412-
vi.useFakeTimers({ toFake: ['Date'] });
454+
// On the frozen clock (`onFrozenClock`), moved one day on the run's first
455+
// door call: the measure's control leg, which falls between the run's
456+
// start (where the presets resolve) and the measure's read of its day.
413457
let turned = false;
414458
const turning: VerifyStack = {
415459
...stack,
@@ -421,11 +465,7 @@ describe("THE DAY TURN — a run that crosses UTC midnight between its start and
421465
return stack.apiAs(...args);
422466
},
423467
};
424-
try {
425-
report = await runDashboardWindowProofs(turning, token, app);
426-
} finally {
427-
vi.useRealTimers();
428-
}
468+
report = await onFrozenClock(() => runDashboardWindowProofs(turning, token, app));
429469
}, BOOT_TIMEOUT + RUN_TIMEOUT);
430470

431471
afterAll(async () => {

0 commit comments

Comments
 (0)