fix(shell): harden PowerShell host probe against cold-start timeouts - #1859
Merged
Conversation
Windows CI flake: TestShellEnvironment's static ctor resolves the shell environment by probing pwsh.exe with a hard 5s timeout. On a loaded Windows runner the cold pwsh.exe spawn (Defender scan, first-run init) exceeded 5s, the resolver threw (Failed did not fall back), and every dependent test class died with TypeInitializationException. The same risk exists in production: daemon startup resolves the shell env, so a slow pwsh could brick startup. Changes: - ProbeAsync retries once (250ms backoff) when the only failure is Timeout; other failures return immediately. Each attempt spawns a fresh process. - Resolver treats a pwsh probe failure as fallback-eligible: it probes powershell.exe 5.1 and records PowerShellFallbackReason .PreferredHostProbeFailed. Both-fail now throws a combined message listing both failures instead of the old early-throw that hid the preferred-failure detail. - CI: pre-warm the probe on Windows runners (pwsh + powershell.exe fallback) so cold-start cost doesn't eat the in-test timeout budget.
…mmand The previous warm-up passed $PSVersionTable.PSVersion.ToString() inside a double-quoted -Command string; the outer pwsh shell interpolated the variable into its type name and the child powershell.exe failed to parse the command. Use -Command "exit 0" (no variables) and guard the step with a final exit 0 so a missing powershell.exe can't fail CI.
Aaronontheweb
enabled auto-merge (squash)
August 10, 2026 21:21
This was referenced Aug 10, 2026
Merged
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.
Problem
Windows CI flake:
TestShellEnvironment's static ctor resolves the shell environment by probingpwsh.exewith a hard 5s timeout. On a loaded Windows runner the coldpwsh.exespawn (Defender scan, first-run init) exceeded 5s, the resolver threw (Faileddid not fall back), and every dependent test class died withTypeInitializationException— 123 tests in run 31417450377.The same risk exists in production: daemon startup resolves the shell env (Program.cs:133), so a slow
pwshcould brick startup.Changes
Probe retries once on timeout.
PowerShellHostProbe.ProbeAsyncretries (250ms backoff) when the only failure isTimeout; other failures return immediately. Each attempt spawns a fresh process. Worst-case probe latency doubles (~12.5s per executable) but only on the rare timeout path — healthy hosts see zero added latency.Probe failure is fallback-eligible. The resolver no longer hard-throws when the
pwshprobe fails. It probespowershell.exe5.1 and recordsPowerShellFallbackReason.PreferredHostProbeFailed(startup logs a warning). Both-fail still throws — fail-closed is preserved, and the combined message now lists both failures (the old early-throw hid the preferred-failure detail).CI warm-up.
pr_validation.ymlpre-warms the probe on Windows runners (bothpwshand thepowershell.exefallback) so cold-start cost doesn't eat the in-test timeout budget.Tests
Failednow selects Windows PowerShell 5.1 withPreferredHostProbeFailed; new both-fail test asserts the combined error.Failed(Timeout), non-timeout failure does not retry.Netclaw.Daemon.Tests: 1000/1000 pass on Linux.Note
Worst-case Windows daemon startup when both hosts are slow is now ~25s before failing with a clear combined error (vs ~6s before). Intentional tradeoff: slow-but-successful, or slow-but-clear failure, instead of bricked startup on a transient cold-start timeout.