Repository navigation
fix: give the Windows smoke suite's SessionStart call its own stdin - #103
Merged
Merged
Conversation
`hooks/smoke-test-windows.ps1` ran `run-hook.cmd session-start` with no stdin of its own, so the hook inherited the runner's — and `session-start` reads its event with `cat`. An open, silent stdin (a terminal, a held pipe) never reaches EOF, so the suite printed its first banner and then hung indefinitely. The cause is the inherited stdin, not a throw: probing with `throw` at eight positions, every one before that call printed and exited 1 in under 14s, while every one after it hung. The boundary is exactly `Invoke-SessionStart 'pr'`. CI could never have caught this — a runner's stdin is empty, so `cat` sees EOF at once and every windows-latest job has been green regardless. `hooks/smoke-test` now runs the same suite again with a held-open stdin and fails if it does not finish, which is the condition CI cannot supply. The same inherited stdin reached one more call, in `hooks/smoke-test`'s own Windows launcher section; that one is given `< /dev/null` too.
# Conflicts: # CHANGELOG.md
CI caught what the local check could not. `'' |` is not an empty stdin in PowerShell: the string is encoded and written as a line, and which line depends on `$OutputEncoding` — 0x0d 0x0a here, a BOM on the runner, where `session-start` rejected it as invalid JSON and the suite failed (run 40712165629). `& cmd /c "... < nul"` is zero bytes on every host, and is the shape `Invoke-Hook` in this same file already uses.
…ns it The check I added last commit was in hooks/smoke-test, which no CI job executes on Windows: the workflow gates it on `runner.os != 'Windows'`, and ubuntu and macos have no powershell.exe or cygpath so the guard skips it. Green CI therefore said nothing about it — and Windows is the only platform that can reproduce the defect. Moved into hooks/smoke-test-windows.ps1, which is the only TaskFlow check a Windows runner runs. It spawns itself with RedirectStandardInput set and never written; TASKFLOW_STDIN_SELFCHECK bounds the recursion to one child. Verified by mutation: reverted to inheriting, the child is killed at the 90s deadline and the suite fails naming the read; with `< nul` it passes in 51s.
Windows PowerShell 5.1 reads a .ps1 with no BOM using the system ANSI code page, not UTF-8. An em dash I put in a throw message became three UTF-8 bytes whose last one swallowed the closing quote, and the whole file stopped parsing -- after Linux and macOS had passed on the same bytes. The file was already ASCII-only for this reason and builds its Chinese fixtures from [char] codes; the rule just was not enforced. The suite now reads itself first and refuses to run if any non-ASCII character is present, naming the line and the code point.
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.
Summary
The Windows smoke suite stopped dead whenever its stdin was open and silent.
hooks/smoke-test-windows.ps1ranrun-hook.cmd session-startwith no stdin of its own, so the hook inherited the runner's — andsession-startreads its event withcat. From a terminal that read never reaches EOF, so the suite printedWINDOWS LAUNCHER ARGS PASSEDand then hung indefinitely. A red assertion reached a person as an unexplained freeze rather than a message.The report that produced this task said "a throw after the launcher section hangs the suite". That measures the wrong object. Probing with
throwat eight positions (88/95/97/110/121/142/160/197/208), every one before the SessionStart call printed its message and exited 1 in under 14 seconds, and every one after it hung. The boundary is exactly$first = Invoke-SessionStart 'pr'; a throw is never the cause, and the outertry/finallyalready reports and exits non-zero without any change.CI could never have caught this — a runner's stdin is empty, so
catsees EOF at once and every windows-latest job has been green regardless. That is also why the assertion has to manufacture the condition rather than wait for CI to supply it.hooks/smoke-testalready carried this lesson for its ownsession-startcall (< /dev/null, comment at:564-566); the PowerShell suite was its unapplied twin. The same inherited stdin also reached one more call —hooks/smoke-test's own== Windows launcher finds Git Bash ==section, which runscmd.exe //d //c ... session-start— and that is fixed the same way.TaskFlow traceability
TaskFlowDocs/2026-10-28-windows-smoke-stdin/hooks/smoke-test-windows.ps1(one call, given an empty stdin),hooks/smoke-test(the same fix for its Windows launcher section, plus one end-to-end assertion),CHANGELOG.md. No hook's runtime behaviour changes;hooks/task, and the four hooks'catcontract, are untouched.mainVerification
Hooksworkflow reports pass for this branch — run to be linked once CI reportsgit diff --checkbash -n hooks/smoke-testMeasured locally (WSL + Windows PowerShell 5.1), with an equivalent script rather than the whole suite — see the limitation below:
/dev/nullPASSEDbannersPASSEDbannersdiff -qbyte-identicalThe mutation found a hole in the assertion, not in the code. The first version used
sleep 60as the pipe's writer. With the fix reverted it passed at 79s — the writer exited at 60s, the blockedcatreceived its EOF, and the suite then completed against a build carrying the defect. The writer must outlive the deadline. Atsleep 200against 120s the reverted build is still silent when the deadline fires. That reasoning is in the comment above the check, because shortening the number gives no warning.Review boundaries
TaskFlowDocs/todo.mddiff is one entry, recorded by this task's own intake and promoteKnown limitations
bash hooks/smoke-testcould not be run on the author's machine. It stops at== Windows launcher finds Git Bash ==becausecmd.exerefuses to use a WSL UNC temp directory as its cwd (UNC 路径不受支持), so the section'sgrepsees only the cmd banner. This predates the change: the same line taken frommainproduces identical output locally, and adding< /dev/nullmakes it exit 0 — the section cannot pass under WSL either way. The new assertion was therefore validated with an equivalent script that builds the same stdin, not by a green full suite. The authoritative run is thewindows-latestjob below.powershell.exeandcygpathare both present, reusing the guard the existing Windows section already has. Ubuntu and macOS skip it, as they skip that whole section.smoke (windows-latest)job. That is the price of a check that can distinguish the fix from the bug; anything cheaper would be asserting the shape of the source line rather than its behaviour.hooks/smoke-test:743invokes the sub-suite with-ExecutionPolicy Bypass, an existing choice this change does not evaluate.