fix: Codex watchdog freezes the command before reaping its children

The bash-native watchdog (stock macOS, no timeout binary) ran pkill -P on
the command's children and then killed the command. Between the two, the
command continued past its killed child and printed output after its
deadline. Stop it first. The test's minimal PATH also lacked pkill, which
the watchdog uses to reap children (stock macOS ships it), so the orphaned
child held stdout open and the capture waited out its sleep.
This commit is contained in:
garrytan committed 2026-10-06 18:14:41 +00:00
1 parent 01eec526ca
commit 2e1d3e735d
2 files changed
+7 -1

No files matched your search

+3
View File
@@ -418,6 +418,9 @@ _gstack_codex_timeout_wrapper() {
sleep 1
_GSTACK_CODEX_WAITED=$((_GSTACK_CODEX_WAITED + 1))
done
# Freeze the command first: killing its children while it runs lets it
# continue past the killed child and print output after its deadline.
kill -STOP "$_cmd_pid" 2>/dev/null
pkill -KILL -P "$_cmd_pid" 2>/dev/null
kill -KILL "$_cmd_pid" 2>/dev/null
exit 124
+4 -1
View File
@@ -357,7 +357,10 @@ describe('gstack-codex-probe: timeout wrapper + namespace hygiene', () => {
// tool gate killed the whole call and the partial output was lost.
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-watchdog-stubborn-'));
try {
for (const tool of native ? ['bash', 'sleep', 'cat'] : []) {
// pkill is part of the watchdog's contract (it reaps the provider's
// children; stock macOS ships /usr/bin/pkill). Without it the orphaned
// `sleep 30` keeps stdout open and the capture waits out the sleep.
for (const tool of native ? ['bash', 'sleep', 'cat', 'pkill'] : []) {
const resolved = spawnSync('bash', ['-c', `command -v ${tool}`], { timeout: 5000 });
fs.symlinkSync(resolved.stdout.toString().trim(), path.join(dir, tool));
}