Skip to content

test: pay Windows PowerShell's cold start outside the hook budgets - #3

Merged
packetloss404 merged 1 commit into
mainfrom
fix/windows-hook-cold-start
Sep 5, 2026
Merged

test: pay Windows PowerShell's cold start outside the hook budgets#3
packetloss404 merged 1 commit into
mainfrom
fix/windows-hook-cold-start

Conversation

@packetloss404

Copy link
Copy Markdown
Owner

Fixes the flaky TestRunUserPromptSubmit_CollectsStdout on test (windows-latest).

Diagnosis (measured, not assumed)

Instrumented on four GitHub windows-latest runners:

sample bare cmd.exe CreateProcess first PowerShell in the job steady-state PowerShell
1 38 ms 4.625 s ~170 ms
2 15 ms 4.867 s ~166 ms
3 21 ms 4.328 s ~178 ms
4 15 ms 4.660 s ~175 ms

The first powershell -Command "exit 0" on a machine costs ~4.6 s; every later
one costs ~0.18 s. A bare CreateProcess at the same instant costs 15-38 ms, so
the machine is not contended.

Ruled out by the same measurement:

  • stdin plumbing — 170 ms with no stdin, 175 ms with stdin attached, 180 ms
    running the full hook script.
  • internal/hooks itself — 184 ms with the ConfigureTreeCancel wiring,
    194 ms end to end through Runner.
  • pwsh as a cheaper interpreter — slower warm, 265-285 ms vs 165-185 ms.

So the whole ~4.6 s landed on whichever test spawned first, against a 5 s
budget. The margin was 0.1-0.7 s, which is why it failed ~2 runs in 3 on CI and
never on a developer machine.

Fix

  • internal/hooks spawns the interpreter once in TestMain, before any test's
    budget is running.
  • The budgets scale through internal/testwait (PACKETCODE_TEST_TIMEOUT_SCALE),
    as every other deadline in the suite already does.
  • The tests still run the real hook path through the real interpreter. Nothing
    is skipped on Windows.

Other tests in the file

  • TestRunPreToolUse_MatcherCanBlock (TimeoutSec: 2) measured 1.0-1.7 s
    against two seconds — Write-Error builds a full ErrorRecord, so it costs
    several times a bare spawn. It survived only because the test above it
    absorbed the cold start. Scaled.
  • TestRunPostToolUse_TruncatesStdoutAndStderr has already failed once on CI
    for the same reason (job 99385812448). Scaled.
  • TestRunPreToolUse_TimeoutMessage deliberately not scaled: there the
    timeout is the subject, and scaling the 3 s ceiling past the 5 s sleep would
    stop it distinguishing a cancelled hook from a completed one. Measured at
    1.02-1.05 s; it does not need the slack.

internal/statusline and internal/jobs spawn the same interpreter with the
same budgets, and were surviving only because internal/hooks happens to run
earlier and absorb the cost — a dependency on package scheduling that nothing
stated. Their budgets scale too.

🤖 Generated with Claude Code

TestRunUserPromptSubmit_CollectsStdout failed on test (windows-latest)
about two runs in three and never on a developer machine. The number was
measured before it was changed.

On four GitHub windows-latest runners the first `powershell -Command
"exit 0"` in a job took 4.33s, 4.66s, 4.63s and 4.87s; every later one
took 0.16-0.19s. A bare cmd.exe CreateProcess at the same instant took
15-38ms, so the machine was not busy. Attaching stdin cost nothing
(170ms with none, 175ms with it, 180ms running the full hook script),
and internal/hooks cost nothing (184ms with the tree-cancel wiring,
194ms end to end through Runner). The cost is Windows PowerShell's own
start-up, paid once per machine, and it landed entirely on whichever
test spawned first -- whose 5s budget sat 0.1-0.7s above a 4.6s
constant. That is a coin flip, not a test.

So: spawn the interpreter once in TestMain, before any budget is
running, and let the budgets scale through internal/testwait like every
other deadline in the suite. The tests still run the real hook path
through the real interpreter; pwsh was measured as an alternative and is
slower warm (265-285ms vs 165-185ms), so it would have been a
regression.

TestRunPreToolUse_MatcherCanBlock had less headroom than its 2 suggests
-- 1.0-1.7s against two seconds, because Write-Error builds a full
ErrorRecord -- and survived only because the test above it absorbed the
cold start. TestRunPostToolUse_TruncatesStdoutAndStderr has already
failed once on CI for the same reason. Both scale now.

TestRunPreToolUse_TimeoutMessage deliberately does not: there the
timeout is the subject rather than the scaffolding, and scaling the
three-second ceiling past the five-second sleep would stop it telling a
cancelled hook from a completed one. Measured at 1.02-1.05s, it does not
need the slack.

internal/statusline and internal/jobs spawn the same interpreter with
the same budgets and were surviving only because internal/hooks happens
to run earlier and absorb the cost -- a dependency on package scheduling
that nothing stated. Their budgets scale too.

testwait.Seconds is new: Timeout in whole seconds for the timeout_sec
config fields, rounding up so a scaled budget is never quietly shortened
by truncation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@packetloss404
packetloss404 force-pushed the fix/windows-hook-cold-start branch from ce1887a to 61d8540 Compare September 5, 2026 16:26
@packetloss404
packetloss404 marked this pull request as ready for review September 5, 2026 17:06
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 5, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-05T17:09:07.948121Z 61d8540 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@packetloss404
packetloss404 merged commit 7f765ab into main Sep 5, 2026
22 of 32 checks passed
@packetloss404
packetloss404 deleted the fix/windows-hook-cold-start branch September 5, 2026 17:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant