From 3fb128ea3ed653b6cd1d94bae6d2b8790ba174f9 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Tue, 1 Sep 2026 22:43:28 -0700 Subject: [PATCH] test(mcp): child PID snapshot must see a subprocess spawned from another thread --- tests/tools/test_mcp_stability.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/tools/test_mcp_stability.py b/tests/tools/test_mcp_stability.py index f3a5591ebb..e1261bdda7 100644 --- a/tests/tools/test_mcp_stability.py +++ b/tests/tools/test_mcp_stability.py @@ -66,6 +66,36 @@ class TestStdioPidTracking: for pid in result: assert isinstance(pid, int) + def test_snapshot_sees_child_spawned_from_another_thread(self): + """/proc//task//children is per-thread; the MCP subprocess + is spawned from the background loop thread, so a main-thread-only + read misses it and every dead-child fast-fail / respawn / killpg + path silently no-ops.""" + import subprocess + import sys as _sys + import threading + + from tools.mcp_tool import _snapshot_child_pids + + procs = [] + started = threading.Event() + release = threading.Event() + + def _spawn(): + procs.append(subprocess.Popen([_sys.executable, "-c", "import time; time.sleep(30)"])) + started.set() + release.wait(10) # keep the spawning thread alive while we snapshot + + t = threading.Thread(target=_spawn, daemon=True) + t.start() + assert started.wait(10) + try: + assert procs[0].pid in _snapshot_child_pids() + finally: + release.set() + procs[0].kill() + procs[0].wait(5) + def test_kill_orphaned_handles_dead_pids(self): """_kill_orphaned_mcp_children gracefully handles already-dead PIDs."""