ci(smoke): say which side is wedged when readiness times out - #1415
Merged
Merged
Conversation
win32-arm64 has failed readiness ten times across v0.13.2 while the app plainly booted - window created, renderer emitting telemetry - and then Runtime.evaluate simply never returned. The failure said only: Electron renderer did not become ready: CDP command timed out: Runtime.evaluate That message cannot distinguish a wedged RENDERER from a wedged MAIN process, and that single bit is what four separate fixes were guessed against. Two of them (#1411, #1414) were mechanically correct - detection fell from ~32s to 12019ms exactly as predicted - and neither changed the outcome, because neither was aimed at the real side. So the timeout now asks the BROWSER endpoint, which the main process serves rather than the renderer: Browser.getVersion answers -> main process live, renderer wedged Browser.getVersion hangs -> main process is the blocked one and prints the target list alongside it, which says whether the page still exists at all. Every probe is bounded and swallowed. Diagnostics must describe the failure, never change it, so a probe that fails is itself reported rather than thrown. The failure is bimodal, which is why this matters: the one passing leg reached readiness in 113s, every failing leg never reached it in 600s. That is a hang, not slowness, so no further timeout widening can fix it. Gates: platformPackageSmoke 71 pass 0 fail, scoped oxfmt clean, node --check clean. The new test drives the real exported waitForRendererReady rather than the injected harness double - the first draft asserted against runSmoke, which substitutes its own readiness function and so proved nothing. Mutation-proven: stubbing the diagnostics string fails it with "expected ... to contain 'browser endpoint did NOT answer'". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
FerroxLabs
added a commit
that referenced
this pull request
Sep 20, 2026
#1415 established WHICH side hangs on win32-arm64: Browser.getVersion on the browser endpoint went unanswered while the DevTools HTTP endpoint still served /json/list, so the main process's JS thread is blocked, not the renderer. It did not establish WHAT blocks it. Agent detection is already excluded - it completes in 12278ms and the block outlives it, and the app's last line is kickoff.telemetry about 12s after window creation, then silence for the remaining 600s. A blocked JS thread cannot fire its own timer, so the timer is the probe. A tick arriving late by more than its own period measures a stall the event loop actually suffered; the last tick before silence dates a block that never ends, which is enough to name whatever logged just before it. Gated on WAYLAND_PACKAGE_SMOKE_MARKER, which only the packaged smoke sets, so no shipped run pays for it. Extracted to its own module rather than buried in index.ts so it can be tested at all - the same reason describeDirtyShutdown was extracted. Gates: full unit suite 1769 passed 0 failed, typecheck 0, scoped oxfmt clean, oxlint 0 warnings. Mutation-proven on BOTH halves: removing the smoke gate fails with "expected [Function] to be null", and disabling stall detection fails because the stall logs as an ordinary tick. A fourth test pins that ordinary timer jitter is NOT reported as a stall, so the warning means something when it appears. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.
win32-arm64 has failed readiness ten times across v0.13.2 while the app plainly booted. The failure message is always the same, and it is not enough to act on:
It cannot distinguish a wedged RENDERER from a wedged MAIN process. That single missing bit is what four separate fixes have been guessed against.
Why guessing has to stop
Two of those fixes were mechanically correct and still did not change the outcome:
Same failure. Both were aimed at the main process, and nothing has ever established that the main process is the blocked side.
The failure is also bimodal, which rules out the remaining timeout theories: the one passing leg reached readiness in 113s, and every failing leg never reached it in 600s. That is a hang, not slowness. No further budget widening can fix a hang.
What this adds
On a readiness timeout the harness now asks the browser endpoint, which the main process serves rather than the renderer:
and prints the target list next to it, which says whether the page still exists at all.
Every probe is bounded and swallowed. Diagnostics describe a failure, they never change it, so a probe that itself fails is reported rather than thrown.
Verification
One note on the test, because the first draft was wrong in a way worth naming. It asserted against runSmoke, which substitutes its own waitForRendererReady through the dependencies object, so the real function never ran and the test proved nothing. It now drives the exported waitForRendererReady directly against a dead port.
What this does not do
It ships nothing and fixes nothing. It converts the next win-arm64 failure from a dice roll into a diagnosis.
🤖 Generated with Claude Code