Skip to content

fix(windows): stop paying 30s of start-up for a probe that finds nothing - #1414

Merged
FerroxLabs merged 1 commit into
mainfrom
fix/probe-timeout-startup-cost
Sep 19, 2026
Merged

FerroxLabs merged 1 commit into
mainfrom
fix/probe-timeout-startup-cost

Conversation

@FerroxLabs

Copy link
Copy Markdown
Owner

win32-arm64 has passed 1 of 9 release attempts for v0.13.2, so rerunning to green stopped being a viable path. This is the app-side cause, and the evidence is about the probe's VALUE, not just its cost.

The probe has never once found anything

Across nine win32-arm64 legs on 2026-09-19, every run that dumped its app log showed the same pair of facts:

[AcpDetector] batched PowerShell Get-Command failed: Command timed out after 15000ms
[AgentRegistry] Completed in 32233ms, found 2 agents: Fuigo, Gemini CLI

Detection durations across those runs: 32233, 31697, 32099, 31612, 32154, 32166, 32100, 32007 ms. Agents found: the same two, every time.

Two serialized 15s rounds. About 32 seconds of start-up. Zero detections bought for it.

This is not a CI artifact. It is the code path every Windows user runs at launch.

The change

POWERSHELL_PROBE_TIMEOUT_MS drops from 15000 to 5000. A powershell -Command Get-Command that has not answered in 5s is not going to answer usefully during start-up. Detection should fall from about 32s to about 12s.

What it costs: a CLI on a machine where PowerShell needs between 5s and 15s to resolve will stop being detected. On such a machine the old 15s ceiling was already being hit as often as not, so this converts an unreliable detection into a fast miss rather than a reliable detection into a miss.

Why this fixes both failure modes

The two win-arm64 failures this release are "Electron renderer did not become ready" and "packaged app did not shut down cleanly". Both trace to the same 30 seconds:

  • readiness: detection spawns its probes during window creation and competes with renderer start-up
  • shutdown: quit waits on killAllAgentChildren, and probes still mid-timeout are what it waits for

Cutting the dead 20 seconds attacks both without weakening any assertion in the smoke harness.

What this does NOT claim

It does not claim the probe is useless everywhere. On a healthy box PowerShell start-up is 0.5-2s and the probe resolves well inside 5s, so nothing changes for the users it currently helps.

It also is not the whole story. Start-up detection still spawns roughly twenty-three processes from the Electron main thread, and uv_spawn calls CreateProcessW synchronously on the calling thread. Deferring detection off the boot path is the complete fix and is tracked in #1410.

Verification

  • full unit suite: 1768 passed, 0 failed
  • typecheck: 0
  • bun run test:bun: 2 pass, 0 fail
  • scoped oxfmt --check clean on both touched files
  • the new test is mutation-proven: restoring 15000 fails it with "expected 15000 to be less than or equal to 5000"

🤖 Generated with Claude Code

Measured across nine win32-arm64 release legs on 2026-09-19, the batched
PowerShell probe hit its 15s ceiling and resolved NOTHING on every single
one, while agent detection reported the same result each time:

  [AcpDetector] batched PowerShell Get-Command failed: timed out after 15000ms
  [AgentRegistry] Completed in 32233ms, found 2 agents: Fuigo, Gemini CLI
  ... 31697 / 32099 / 31612 / 32154 / 32166 / 32100 / 32007 ms, same 2 agents

Two serialized 15s rounds, ~32s of start-up, zero detections bought. This
is not a CI artifact - it is the path every Windows user runs at launch.

A `powershell -Command Get-Command` that has not answered in 5s is not
going to answer usefully during start-up, so the ceiling drops to 5000.
Detection should fall from ~32s to ~12s. The only machines that lose a
CLI are those where PowerShell needs 5-15s, and on those the old ceiling
was already being hit as often as not.

This also attacks both win-arm64 release failure modes at once: less
competition with renderer start-up (the "renderer did not become ready"
half) and fewer probes still in flight at quit (the "did not shut down
cleanly" half). win-arm64 has passed 1 of 9 attempts this release, so
rerunning to green was no longer a viable path.

Gates: full unit suite 1768 passed 0 failed, typecheck 0, test:bun 2/0,
scoped oxfmt clean. The new test is mutation-proven - restoring 15000
fails it with "expected 15000 to be less than or equal to 5000".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the area:core Wayland Core engine / backends label Sep 19, 2026
@FerroxLabs
FerroxLabs merged commit aaf1d1a into main Sep 19, 2026
24 checks passed
@FerroxLabs
FerroxLabs deleted the fix/probe-timeout-startup-cost branch September 19, 2026 15:53
FerroxLabs added a commit that referenced this pull request Sep 19, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core Wayland Core engine / backends

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant