Skip to content

Commit cd93534

Browse files
schubydooclaude
andauthored
test(reaper): wait for the stub to run its script before reaping (#1781)
_spawn_stub now waits for <debug-file>.argv.json before the test reaps. Popen returns after the shebang's /usr/bin/env exec. While env execs python3, /proc/<pid>/cmdline reads empty and the argv-based reaper skips the stub, which flaked the musl CI leg on 2026-10-31. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 094a98a commit cd93534

1 file changed

Lines changed: 24 additions & 3 deletions

File tree

‎tests/test_stub_reaper.py‎

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,46 @@
99

1010
import subprocess
1111
import sys
12+
import time
1213

1314
from conftest import FAKE_CLAUDE, reap_fixture_stub_strays
1415

1516
#: Generous: this only bounds a SIGKILL'd process's disappearance, never a happy path.
1617
DEATH_TIMEOUT = 30
18+
#: Generous too: bounds how long the stub takes to reach its own first line of Python.
19+
STARTUP_TIMEOUT = 30
1720

1821

1922
def _spawn_stub(tmp_path) -> subprocess.Popen:
20-
"""Start a `fake_claude` bridge stub that idles until signalled, as the runner does."""
21-
return subprocess.Popen( # noqa: S603 — fixture stub, absolute path, list argv
23+
"""Start a `fake_claude` bridge stub that idles until signalled, as the runner does.
24+
25+
Returns only once the stub is RUNNING its script, not merely spawned. `Popen` returns
26+
after the first exec, which is `/usr/bin/env` from the shebang; env then execs
27+
`python3`, and while that second exec is in flight `/proc/<pid>/cmdline` reads empty.
28+
The reaper selects on argv, so a reap in that window misses the stub (seen on the
29+
musl CI leg, and about one read in five locally right after `Popen`). The stub writes
30+
`<debug-file>.argv.json` as its first act, which is proof the exec chain is done.
31+
"""
32+
debug_file = tmp_path / "bridge.log"
33+
proc = subprocess.Popen( # noqa: S603 — fixture stub, absolute path, list argv
2234
[
2335
str(FAKE_CLAUDE),
2436
"remote-control",
2537
"--name",
2638
"reaper-probe",
2739
"--debug-file",
28-
str(tmp_path / "bridge.log"),
40+
str(debug_file),
2941
]
3042
)
43+
ready = tmp_path / "bridge.log.argv.json"
44+
deadline = time.monotonic() + STARTUP_TIMEOUT
45+
while not ready.exists():
46+
if proc.poll() is not None or time.monotonic() > deadline: # pragma: no cover
47+
proc.kill()
48+
proc.wait(timeout=DEATH_TIMEOUT)
49+
raise AssertionError(f"fake_claude stub never started (rc={proc.returncode})")
50+
time.sleep(0.01)
51+
return proc
3152

3253

3354
def test_reaper_kills_an_abandoned_stub(tmp_path):

0 commit comments

Comments
 (0)