test(cli): one owner for Process.Wait in the interrupt test - #1423
Merged
Conversation
TestGoCodeScriptStopsHarnessdOnInterrupt called cmd.Process.Wait() twice — once in the deferred cleanup, once in the goroutine feeding the timeout select. os.Process.Wait is not safe to call twice on the same process. That is the one defect here that is proven; it was introduced in #1417. The stub daemon now takes its sleep child with it on SIGTERM instead of orphaning it, and the exit budget is widened to 30s as headroom for a loaded runner. Two earlier diagnoses were measured and disproven: the wrapper exits 0.21s after SIGINT rather than taking the 5s force-kill path, and the stub does genuinely survive SIGINT (POSIX inherits an ignored disposition across exec, so the sleep ignores it too). The Linux CI failure is not reproduced on macOS and its root cause stays unconfirmed. #1422 stays open until CI is green without a re-run. Refs #1422 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJGxhoFhA8JjkwZFcLGdS5
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
dennisonbertram
added a commit
that referenced
this pull request
Sep 8, 2026
TestGoCodeScriptStopsHarnessdOnInterrupt fails only in CI, and only as an opaque timeout. It has now resisted five reproduction attempts: macOS idle and loaded, Linux, Linux under -race with constrained CPU, and Linux with SIGINT ignored by the parent process. The 30s failure on a docs-only PR also showed it is a hang rather than a thin timing margin — widening the budget in #1423 did not buy headroom, it just made the failure take three times longer to report. Rather than guess a sixth time, dump the process tree with parent, state and wchan when the wrapper does not exit, so the next CI failure carries evidence about what is actually stuck. Refs #1422 Claude-Session: https://claude.ai/code/session_01WJGxhoFhA8JjkwZFcLGdS5 Co-authored-by: Claude Opus 5 (1M context) <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.
Refs #1422 — deliberately not "Closes". The Linux failure is not reproduced, so this should not be auto-closed by a merge.
What is proven
TestGoCodeScriptStopsHarnessdOnInterruptcalledcmd.Process.Wait()twice — once in the deferred cleanup, once in the goroutine feeding the timeoutselect:os.Process.Waitis not safe to call twice on the same process. That is a real defect, I introduced it in #1417, and it is the kind of thing that behaves differently across platforms and under the race detector. Fixed: the deferred cleanup now only kills, andWaithas one owner.Also: the stub daemon orphaned its
sleepchild when bash was terminated. It now traps TERM and takes the child with it.What was disproven, and why that matters
I filed #1422 with a confident diagnosis. It was wrong, and so was my second one. Both took under a minute to disprove by measuring instead of reasoning.
"The stub forces a 5-second force-kill path every run, leaving no headroom." Timed the wrapper's exit after a process-group SIGINT, driving it exactly as the test does:
A non-interactive bash does not defer SIGTERM behind a foreground
sleep; it terminates. The graceful-shutdown wait is never reached."The stub doesn't really survive SIGINT, so the test passes for the wrong reason." Checked directly:
POSIX inherits an ignored disposition across exec, so
trap '' INTmakes thesleepignore INT too. The premise is sound and the trap stays.The issue body has been corrected and the title updated, so nobody implements against the version I got wrong.
What is still unknown
The Linux CI root cause is unconfirmed. Not reproduced on macOS: 8/8 under
-racewith four CPU hogs running, 3/3 idle.So the widened budget (10s to 30s) is honest headroom for a loaded runner, not a fix justified by a timing story — the old 10s was itself chosen against the story I have now disproven. This PR removes a genuine concurrency defect and reduces fragility. It may or may not be what was failing.
#1422 stays open until CI shows a clean run without a re-run. A green that needed a re-run proves nothing, which is the same trap that let this reach
mainin the first place.Verification
go test ./cmd/harnesscli -run TestGoCodeScript -race -count=2— all seven tests, green. Exactly onecmd.Process.Wait()call remains in the file.🤖 Generated with Claude Code
https://claude.ai/code/session_01WJGxhoFhA8JjkwZFcLGdS5