Stop exitTimelineMode's test leaking a load that gates the poll - #213
Merged
Conversation
The suite calls exitTimelineMode(), which fires a live-HEAD reload it never
awaits. loadSource sets SCAN_PROGRESS before its first await and clears it only
in `finally`, and the stub EventSource never emits, so that load parks forever
and leaves SCAN_PROGRESS set for the rest of the file.
tick() yields to a foreground load on exactly that signal, so any later test
asserting the poll fires depends on having nulled SCAN_PROGRESS itself. The
config shuffles test order on purpose to surface couplings like this, and the
live-poll resume test has failed twice on main this way (runs 31991836275 and
33795322008).
Cancelling settles the load through the abort path streamManifest already
honours, so its `finally` runs. The added assertion is the guard: drop the
cancelLoad() and it fails with `expected { kind: 'local', … } to be null`.
Closes #212
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Qzxns3a3Q12RfayHZRrGZ
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Qzxns3a3Q12RfayHZRrGZ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #212
The failure
tests/hooks/useTimelineMode.test.ts > live poll suspends in Timeline mode > ... and resumes on exitfails intermittently onmain:Twice in three weeks, same assertion both times: run 31991836275 (2026-08-17) and run 33795322008 (2026-09-03). The second landed on
bb586d33, which touches noapp/orapi/file — the test and the code under test were byte-identical to what passed on that PR.Cause
exitTimelineMode's suite fires a live-HEAD reload it never awaits:loadSourcesetsSCAN_PROGRESSbefore its firstawait(useManifestSource.ts:161) and clears it only infinally(line 214). The stubEventSourceemits nothing unless a test callsemit(), so the load parks on the stream forever, thefinallynever runs, andSCAN_PROGRESSstays set for the rest of the file.Measured, not inferred — a probe after that suite:
tick()yields to a foreground load on exactly that signal (useManifestSource.ts:289):So every later test that asserts the poll fires depends on having nulled
SCAN_PROGRESSin its ownbeforeEach. Three describes in the file already do that defensively, which is the tell.vitest.config.jssetssequence: { shuffle: true }precisely to surface couplings like this — "a test that only passes after its neighbour ran says so."Fix
Cancel the load in
afterEach.streamManifestalready honours the abort signal (api/manifest.ts:175) andloadSourcereads it as a clean user cancel, so thefinallyruns andSCAN_PROGRESSclears. No production change: the leak is entirely in the test's lifecycle.The added
expect(SCAN_PROGRESS.value).toBeNull()is the guard, not decoration.Verification
prettier --checkcleancancelLoad()and it goes red:What this does not claim
I could not reproduce the CI failure locally: 60 shuffled runs of the file and two full-suite runs with coverage all stayed green. This removes a proven order-coupling that is the most likely cause, not a confirmed reproduction of that specific run. If it recurs, the next suspect is
inFlight(the exclude-refresh effect atuseManifestSource.ts:334sets it and clears it only in a.finally()), sinceSCAN_PROGRESSis now accounted for.🤖 Generated with Claude Code
https://claude.ai/code/session_011Qzxns3a3Q12RfayHZRrGZ