Flaky nav-tree test: "#79 checkin × Backspace → noop" intermittently fails before retry #160
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/cellblock#160
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?
Observed
Surveyor's double-run flake-detection (during #146/PR #158 review) caught an intermittent failure:
nav.spec.ts:126TABLE row —#79: checkin × Backspace → noop (DOM input owns text)Why it's notable
The row already uses
expect.poll(the deterministic-settle pattern the#81harness adopted specifically to avoid fixed-delay flakes), yet it still flaked. So the poll has an occasional edge on this row.Leading hypothesis (unverified — needs a repro)
toCheckinarranges the start node without awaiting the transition completing:The row body then immediately
page.keyboard.press(row.key)and polls. If theEnter(title→checkin) transition — and the#66DOM#name-inputfocus that mounts on checkin — hasn't settled whenBackspacefires, the key can land on a transitioning/!focused state. The screen-level assertion (screen === 'checkin') usually still holds, but there's a race window. Backspace is the suspicious key here because checkin's behavior hinges on whether the DOM input has captured focus (it owns the text; screen stays checkin only if focus routing is settled).Possibly affects other rows whose
arrangeends on an un-awaited transition, but this is the only one observed flaking.Proposed fix + verification path
toCheckinpollsscreen === 'checkin'after the Enter, so the row key never races the arrange. (Same shape as the existingtoLobby/toMatchedhelpers in versus.spec, which DO poll their resulting phase.)npx playwright test nav.spec.ts -g "checkin × Backspace" --repeat-each=100(±--workers=1) to surface the flake first, confirm the fix drives it to 0/100. If it won't reproduce locally even under repeat-each, note that and lean on the reasoned arrange-await fix + a CI-side repeat watch.Severity
LOW — test-determinism only; ships green via retry. No product impact.
Anchor
2026-06-24, Surveyor flake-flag during #146/PR #158 review (his 6cb8); Bosun routed to Shipwright (his 5ef3). Filed rather than blind-fixed because intermittent-without-local-repro (filed-rootcause-is-hypothesis discipline).
ⓘ Investigation update — Shipwright, 2026-06-24 (substrate-grounded; corrects line + cause)
The repro overturned both the filed line and the filed cause. Recording the actual findings for substrate-of-record honesty:
The filed row does NOT reproduce.
checkin × Backspace(the row Surveyor's auto-retry annotated) was rock-solid under stress: 0 failures in 100× isolated + 20× full-file + 60× checkin-rows. The leading hypothesis (un-awaitedtoCheckin/ input-focus race) could not be demonstrated.The real, reproducible flake is a different test:
#80: touch PLAY AGAIN inert during submitting— ~3% intrinsic (reproduces alone, not order-dependent). Captured failure state: the PLAY-AGAIN tap restarted into a fresh solo game (phase:playing, initialsPhase:idle).Root cause (confirmed, not hypothesised), via a route-fire counter that read
hits=0on failure: the test'sholdSubmit(page.route('**/leaderboard/submit', …)— hangs the submit so the commit lands in a stablesubmitting) was registered afterfresh()'sgoto, leaving a ~3% window where the interception wasn't yet live. The POST then slipped through to the vite dev server, resolved (404 / SPA-fallback), andsubmitLeaderboardrevertedsubmitting→entering(main.ts:636/646). WithinitialsPhaseno longersubmitting, the product's inert-guard (main.ts:1259touch /:1024keyboard —initialsPhase !== 'submitting') correctly let the tap restart. The product is sound — this was a test-substrate race. The TABLE rows reusingtoSubmittingnever flaked because they assert quickly (catchingsubmittingbefore the revert); only the standalone#80test adds ~160ms (canvas wait + boundingBox) before clicking, widening the window.Fix (mutation-proven): register
holdSubmitbeforefresh()(Playwright guidance: routes set up pre-navigation are reliably active for later requests). → 0/100. Mutation (revert ordering, keep everything else) → flake returns. Load-bearing confirmed.Also shipped as defense-in-depth (NOT mutation-verified — the filed row never reproduced):
toCheckinnow awaitsscreen === 'checkin'+ the#name-inputfocus before returning, directly addressing the filed hypothesis's concern; verified it introduces no new flake (60× checkin rows clean).PR: fix in
nav.spec.tsonly (test-determinism; no product change).