Skip to content

Settle the notepad prompt's answer inside act so the kill's removal timer wins - #585

Draft
dormouse-bot wants to merge 2 commits into
mainfrom
fix/ci-34088776809
Draft

Settle the notepad prompt's answer inside act so the kill's removal timer wins#585
dormouse-bot wants to merge 2 commits into
mainfrom
fix/ci-34088776809

Conversation

@dormouse-bot

Copy link
Copy Markdown
Collaborator

Problem

Wall.test.tsx > Wall on the Lath engine > Close anyway discards the notes and removes the Surface without a batch failed on main in run 34088776809: expected <div data-lath-leaf="pane-a" …> to be null. The received element carried opacity: 0; width: 0px; height: 0px; z-index: 35; pointer-events: none — a leaf still mid-fade in the animator's dying band, not a leaf that failed to close. The same commit's test file is unchanged by #584, and CI on the next commit (cbb1e9d0) went green, so this is an intermittent ordering race, not a regression.

The race is between two setTimeout(0)s. clickButton clicked inside a synchronous act, so the async closure the answer starts (closeSurfacehelperRefusalkillPaneImmediately) only ran while the test was already awaiting flush(). flush() registers its own setTimeout(0) first; the two-phase kill's deferred removal — setTimeout(…, lath.exitMs), which is 0 here because the suite forces reduced motion — is registered second, during the microtask drain. Node clamps both to 1 ms and runs them in registration order, so whether the removal fires before flush() resolves depends on whether it has expired by the time the timers phase runs. Usually it has; under CI timing it didn't.

The helper's other three call sites share the shape — clickButton('Close anyway'); await flush(); at Wall.test.tsx:1979 asserts the same removal on pane-b — and one sibling test already worked around it with a doubled await flush().

Solution

Fix the helper rather than the one assertion: clickButton is now async, clicks inside await act(async …), and then flushes. The click's async chain now completes inside the act, so the removal timer is registered before flush() registers its own and is guaranteed to land first — the ordering the test relied on by luck is now enforced. Verified with a probe: the leaf is already gone when the act returns.

All five call sites become await clickButton(...), and the now-redundant trailing await flush() calls — including the doubled pair that was papering over this — are removed.

Test-only change; no spec covers it.

Testing

  • pnpm test in lib/ (typecheck + full vitest run): 175 files, 2788 tests passed.
  • vitest run src/components/Wall.test.tsx ×3 after the change: 56/56 each.
  • Before the change, the failing test passed locally 3/3 in isolation and 3/3 as a full file, and did not reproduce under 4-way CPU saturation — consistent with a timing-marginal race rather than a deterministic bug.

Automated fix for failed run

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 7, 2026

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: c1a5ec4
Status: ✅  Deploy successful!
Preview URL: https://84f7edd6.mouseterm.pages.dev
Branch Preview URL: https://fix-ci-34088776809.mouseterm.pages.dev

View logs

@dormouse-bot dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewing as a draft — flagging anything that looks worth a quick fix. Mark ready for a full review.

The helper change itself traces clean: the Close anyway chain to killPaneImmediately is microtask-only (helperRefusalhelperHasWork, then removeSurface/killPaneImmediately), so the async act drains it and the setTimeout(…, lath.exitMs) really is registered before flush() registers its own. All five call sites are awaited, and the shape now matches clickHeaderKill right above it.

One inaccuracy in the new doc comment — inline.

Comment thread lib/src/components/Wall.test.tsx Outdated
`Keep open` is `onKeepOpen={shiftArchiveFailure}` — a synchronous queue
shift with no `closeSurface` call and no removal timer — so only
`Close anyway` depends on the act/flush ordering the helper enforces.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant