[P3] WebKit compatibility smoke is flaky on a loaded host because of fixed timing assumptions in the harness (no product defect) #123
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Priority: P3
Type: bug
Source: full codebase review requested 2026-10-31 (independently reproduced against
2cac7cf)Location:
tests/browser/check-compatibility-browser.mjs:128,tests/browser/check-compatibility-browser.mjs:129,tests/browser/check-compatibility-browser.mjs:169,tests/browser/check-compatibility-browser.mjs:174,src/game/ReefRunner.tsx:64,src/game/reefGame.ts:24,src/planner/PlanLibrary.tsx:59Problem and evidence
Under host load, tests/browser/check-compatibility-browser.mjs fails intermittently with BROWSER_ENGINE=webkit even though the app behaves correctly. Two failure points are shown here.
(a) Line 128 waits for the planner dialog to become visible. Line 129 then reads the 'Planned cylinder 1 label' value once. The shared plan is restored afterwards, in PlanLibrary's mount effect (PlanLibrary.tsx:59-67), so the single read can still return the default ''.
(c) Line 174 waits a fixed 20 s for the 'coins banked' heading. ReefRunner clamps each frame to dt <= 0.05 s (ReefRunner.tsx:64). The return phase needs about 8.3 s of game time: the 3-game-minute stop at REEF_TIME_SCALE 24, plus the ascent. Below about 8 fps, the real time needed is more than 20 s.
(b) Line 169 reads the master-gain value once. The earlier reviewer saw it return undefined once. I did not reproduce this, and the mechanism is unclear: React flushes passive effects from a discrete click synchronously, so setSilent(true) should already have run.
I served /tmp/divesim-review-dist-SqbiNx with python http.server and ran the unmodified harness 3 times with BROWSER_ENGINE=webkit, using the Playwright 1.64.1 copy at /tmp/divesim-browser/node_modules/playwright/index.mjs (it matches installed webkit-2370; the toolbox copy expects webkit-2368, which is not installed). Load average was 11-17 on 6 cores.
A WebKit probe measured requestAnimationFrame at 2.6 fps while the Reef Runner game was swimming.
A scratch script ran the real stepReef from returning to banked at fixed fps, with dt clamped as in ReefRunner. Real seconds to bank:
Game time was about 8.3 s in every case and the phase always reached 'banked', so the app is correct and only the real-time budget is short.
A separate probe opened the share link in 6 fresh WebKit contexts. All six showed 'Reef café 🐠' both immediately and 2 s later. So (a) is a race that does not show up every time, and the label is always applied eventually.
Suggested correction
Change only the harness:
await shared.waitForFunction(() => document.querySelector('[aria-label="Planned cylinder 1 label"]')?.value === 'Reef café 🐠'), orexpect.poll/toHaveValuewith a timeout.page.waitForFunction(() => window.__qaAudio.at(-1)?.qaGains[0]?.qaGainCalls.at(-1)?.value === 0)instead of reading once.Leave the game's dt clamp unchanged.
Acceptance criteria
BROWSER_ENGINE=webkit node tests/browser/check-compatibility-browser.mjsagainst the production build on a host with load average 10 or more. It should pass in at least 3 consecutive runs, including runs where the game renders at 5 fps or less.Scope: maintenance of the educational simulator. Preserve historical R1 acceptance and operational-validation limits.