fix: spawn-test stderr persistence and drain-cap deadlock (PR #11 review)
CI / Lint (ruff) (pull_request) Successful in 7s
CI / Tests (py3.10) (pull_request) Successful in 8s
CI / Tests (py3.12) (pull_request) Successful in 10s

Two bugs caught in supervisor review:

1. Stderr was silently dropped when the Details panel was closed during a
   crash. _on_spawn_done appended to diag_text only when the panel was
   already open, and refresh_dependency() clobbered that text on the next
   field change anyway.
   Fix: stash the result in self._last_spawn; _full_diag_text() appends
   the stderr section whenever diag text is generated; _on_spawn_done
   auto-opens the panel on non-ok outcomes (same pattern as the existing
   auto_open for missing commands).

2. _drain stopped reading once _STDERR_CAP (4 KB) was reached. A process
   that writes more than 4 KB then blocked on a full pipe buffer, never
   exited, and was misclassified as "ok" instead of "crashed".
   Fix: drain to EOF unconditionally; keep only the first _STDERR_CAP bytes.
   Regression test: 64 KB stderr + exit(3) → outcome "crashed".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
AJ
2026-07-02 01:05:34 -04:00
parent e98cb7abd7
commit 231403c1d5
3 changed files with 39 additions and 10 deletions
+15
View File
@@ -303,3 +303,18 @@ def test_spawn_test_stdin_not_closed():
timeout=0.3,
)
assert r["outcome"] == "ok", f"expected ok (blocking on stdin), got {r['outcome']}"
def test_spawn_test_large_stderr_does_not_deadlock():
# A process that writes >4 KB (pipe buffer territory) to stderr then exits non-zero.
# Without EOF-draining, the subprocess blocks on a full pipe and is wrongly
# classified as "ok" (still running) instead of "crashed".
script = "import sys; sys.stderr.write('x' * 65536); sys.stderr.flush(); sys.exit(3)"
r = c.spawn_test(
{"command": sys.executable, "args": ["-c", script]},
timeout=2.0,
)
assert r["outcome"] == "crashed", f"expected crashed, got {r['outcome']}"
assert r["returncode"] == 3
# stderr is capped, not the full 64 KB
assert len(r["stderr"]) <= c._STDERR_CAP