From 1b101c28894bb426092e2dec1d94c3744f0fa768 Mon Sep 17 00:00:00 2001 From: Void Date: Sat, 14 Nov 2026 02:27:25 +0000 Subject: [PATCH 1/2] Scene sky-change sweep: bake each state's sky once so the test stays inside its timeout under load Co-Authored-By: Claude Sonnet 5.5 --- src/app/features/scene/index.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/app/features/scene/index.test.ts b/src/app/features/scene/index.test.ts index 5574d60c..71769f5c 100644 --- a/src/app/features/scene/index.test.ts +++ b/src/app/features/scene/index.test.ts @@ -11,6 +11,21 @@ const { createRidgeScene, createScene } = vi.hoisted(() => ({ })); vi.mock('./ridge/Ridge', () => ({ createRidgeScene })); vi.mock('./Scene', () => ({ createScene })); +// The physical sky is baked per call (tens of ms: atmosphere LUTs) and the clock here is fixed, so a state always +// draws the same sky. Baking once per state keeps the 21-offset sweep under the default timeout when the machine is +// loaded; the sky each build receives is still the one `ridgeSkyFor` makes for that state. +vi.mock('./skyForState', async (importOriginal) => { + const actual = await importOriginal(); + const baked = new Map>(); + return { + ...actual, + ridgeSkyFor: (...args: Parameters) => { + if (args.length !== 1) return actual.ridgeSkyFor(...args); + if (!baked.has(args[0])) baked.set(args[0], actual.ridgeSkyFor(args[0])); + return baked.get(args[0]) as ReturnType; + }, + }; +}); const canvas = {} as HTMLCanvasElement; const config = { tier: 'low' as const }; -- 2.47.3 From a61252e664d7ce249de4d77cf6be0c97062bf0d2 Mon Sep 17 00:00:00 2001 From: Void Date: Sat, 14 Nov 2026 02:32:31 +0000 Subject: [PATCH 2/2] Scene test: pin the stand-down of a replaced rebuild deterministically Co-Authored-By: Claude Sonnet 5.5 --- src/app/features/scene/index.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/app/features/scene/index.test.ts b/src/app/features/scene/index.test.ts index 71769f5c..c89fa3b0 100644 --- a/src/app/features/scene/index.test.ts +++ b/src/app/features/scene/index.test.ts @@ -200,6 +200,19 @@ describe('startBackgroundScene follows the sky state', () => { expect(live.dispose).toHaveBeenCalledOnce(); }); + it('a rebuild for a sky that was replaced while its key was awaited builds nothing: only the newest sky is built', async () => { + createRidgeScene.mockReturnValueOnce(fakeScene()).mockReturnValueOnce(fakeScene()); + createRidgeScene.mockReturnValue(fakeScene()); + const scene = await startBackgroundScene(config, caps, { canvas }); + setSkyState('day'); + setSkyState('dusk'); + await flush(); + // The initial build and one rebuild, for dusk; day's rebuild stood down before building anything. + expect(createRidgeScene).toHaveBeenCalledTimes(2); + expect(hex(createRidgeScene.mock.calls[1][0].sky.top)).toBe(hex(ridgeSkyFor('dusk').top)); + scene?.dispose(); + }); + // A second sky change `ticks` microtasks after the first. True when it landed mid-build: the build for the first // change finished, was found stale and dropped (never shown), and only the newest sky was announced. const changeAfter = async (ticks: number) => { -- 2.47.3