Repository navigation
fix(gate): signal a scope from the gate, not a helper the enclosing scope kills - #629
Merged
Merged
Conversation
Quest-Claim: dba50197c046101e
…cope kills A gate inside a Quest scope forwarded its owner's signal with systemctl kill, started in the enclosing cgroup while that scope's owner was still signalling it, so the helper could die of the signal it forwarded. Signal the cgroup tree from the gate itself, repeating until no new process appears. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…gnalled Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
marked this pull request as ready for review
November 11, 2026 10:54
# Conflicts: # quest/m0/macos-gate.md
Pre-merge checks:
|
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.
Finishes Gate tests pass deterministically, deleting it and its links.
Summary
Both intermittent failures in
test/gate.test.tsare reproduced and fixed at their cause:concurrent processes never admit past the headroom, and admit every caller: the exit-code mismatch in run 42676200917 was a caller crashing withESRCHreading/proc/<pid>/statof an exiting process (its stderr in that run's log). moq-dev/quest#589 already fixed it, landing two hours after that run. 100 runs at 20-way concurrency pass here.lets a phase finish its cleanup once the enclosing scope signals it: reproduced at about 1 in 300 to 500 runs under 16 to 48 concurrent test processes, loggingquest: cannot signal every process in quest-gate-...scope, only its command. Instrumenting showed thesystemctl --user killhelper itself was killed bySIGTERM. A gate inside a Quest scope runs its command in a sibling scope and forwards its owner's signal there, but it spawned that helper inside the enclosing scope's cgroup while the enclosing owner's ownsystemctl kill --kill-whom=allwas still sweeping it (systemd repeats its sweep to catch new forks). The helper died of the very signal it forwarded, so the gate fell back to signalling only its command and cut the leaf's cleanup short (a missingdone, or a hang).Fix:
containsignals its cgroup tree from the gate itself (which handles the signal), readingcgroup.procsrecursively and repeating until a pass finds no new process, as systemd does. This drops thesystemctl killhelper,Containment.signal, andpopulatedOr. After the fix: 0 failures in 480 runs at 48-way concurrency (previously 1 in 480 there).Caused by
systemctl killhelper from moq-dev/quest#429 started inside a cgroup that was being signalled.Public API and wire
None. Internal to
src/cli/supervise.ts.Choices made without asking
systemctl killbehind atrap '' TERMshell wrapper: the wrapper still leaves a window between fork andtrap.cgroup.procsand then callingkillhas the same PID-reuse window systemd had before pidfds; Bun exposes no pidfd, and reuse within that microsecond window would need the host to cycle through every PID.ESRCH), such asEPERMfrom another user's process, keeps the old fallback: report it, signal only the command, and stop draining.Follow-ups
is stopped when the enclosing scope's owner kills itfailed once onuntil's fixed 5 s deadline (200 x 25 ms) intest/gate.test.ts. That is a wall-clock timeout, not a race, so it belongs to Test CPU timeout, being worked in parallel; recommend that quest's PR also coversuntil's deadline rather than a new quest.Checks
quest gate just check: passquest gate just test: pass (1222 passed, 5 skipped)Models
Size: S
Implementer: Claude Code, claude-opus-5-5, low effort
🤖 Generated with Claude Code
(Written by Claude Opus 5.5 with low)
Review
Rewritten by
quest reviewafter each round.01a1ca3a-8ede-7a93-97d4-aa36e75456f1, round 3The incremental diff contains only merged base changes, with no new branch changes to review and no outstanding findings.