You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Two tests read a frame before the deferred scroll pass lands, and fail at random under load #53
Reached by: run bun test src/ as the full suite under load (or in CI); the two named tests fail intermittently depending on whether the setTimeout(0) deferred pass lands before the test reads its frame.
What happens
Two tests fail intermittently on a full suite run and pass when their own file is run alone. Four sightings across one working session, two of them named:
the editor opens in view when the layout moves the row it edits (src/ui/settings-screen.test.tsx)
a lane-series timeout (src/store/history.test.ts)
Each failure was followed by a passing full run with no change to the tree.
The mechanism
Both read a rendered frame that is only correct after a deferred second pass has landed.
src/ui/settings-screen.tsx scrolls twice on purpose. The first pass decides where the selected row goes; the second, scheduled with setTimeout(..., 0), re-measures once the new layout has been drawn, because opening an editor collapses two columns into one and the row has not moved yet on the first pass. That comment is in the source and the behaviour is deliberate.
A setTimeout(0) is a macrotask. Under a full suite run the event loop has other work, so the second pass can land after the test reads its frame. Alone in its file, it lands first. Nothing about the code under test is wrong: the test reads a frame the program has not finished producing.
Why this matters more than a rerun
Three defects in this session were caught only because a control existed for a neighbouring path (see vanillagreencom/vsys#43 and vanillagreencom/vsys#50). A suite that goes red at random teaches a reader to rerun rather than to look, and the cost of that habit is paid the one time the red is real. It also blocks the merge queue for as long as it takes someone to notice the failure is not theirs.
Deliverable
The test harness waits for the deferred pass instead of racing it. src/test/harness.tsx already owns mounting and frame reading for every screen, so the wait belongs there rather than in each test: a frame read that yields to the macrotask queue before returning, or an explicit settle step the two affected tests call.
Whichever shape is chosen, the check is the same: run the full suite repeatedly under load and see both named tests stay green, then confirm each still fails when its own behaviour is planted.
Impact
Every developer running the suite, and every pull request whose continuous integration run happens to land on it. Observed four times in one session on one machine.
Three more tests joined this class today, in src/ui/agent.tsx's scroll close on #50.
They are deterministic as written, because the wait is explicit — the test asks for the settled state rather than reading a frame and hoping the deferred pass has landed, and the pane state settles on arrival instead of pressing keys inside the same tick. That is the shape this issue proposes for the two flaky ones.
Worth recording anyway: it is more tests depending on a setTimeout(0) landing, not fewer. The count of tests in this family is now high enough that the harness-level fix proposed here is cheaper than continuing to get the wait right one test at a time.
The reason the deferred pass cannot simply be removed, for whoever picks this up: a row that has just grown does not know its size until the layout after the render that grew it. Both screens read geometry after that layout on purpose. The fix is a harness that waits for it, never code that stops needing it.
Activity
linear-code commentedon Sep 10, 2026
VSY-29
test(collect): prove the capture with a stand-in tmux server
bmethod commentedon Sep 10, 2026
Three more tests joined this class today, in
src/ui/agent.tsx's scroll close on #50.They are deterministic as written, because the wait is explicit — the test asks for the settled state rather than reading a frame and hoping the deferred pass has landed, and the pane state settles on arrival instead of pressing keys inside the same tick. That is the shape this issue proposes for the two flaky ones.
Worth recording anyway: it is more tests depending on a
setTimeout(0)landing, not fewer. The count of tests in this family is now high enough that the harness-level fix proposed here is cheaper than continuing to get the wait right one test at a time.The reason the deferred pass cannot simply be removed, for whoever picks this up: a row that has just grown does not know its size until the layout after the render that grew it. Both screens read geometry after that layout on purpose. The fix is a harness that waits for it, never code that stops needing it.