Orchestrata Ultimate › Plans › briefs › qwen › Q8-continuation
Q8 continuation — the leaking live_run_tree fixture, then finish
Source: plans/briefs/qwen/Q8-continuation.md at 8a2cb42ad · last changed 2026-10-12 · first committed 2026-10-12 · 73 lines.
Prose pending.
Plan text
Q8 continuation — the leaking live_run_tree fixture, then finish
Same worktree, same branch, same rules as plans/briefs/qwen/Q8-hook-process-matching.md. Do not commit. Every
scripts/run_tests.py run goes through bash .run_exclusive.sh .... Every measured step goes through
bash .hostlock.sh ..., if your worktree has it. A guard denial is reported, never worked around.
1. The defect (orchestrator, measured)
tests/claude/test_test_run_context.py, fixture live_run_tree (lines 74-111 on master):
- It starts a parent:
python -c ... scripts/run_tests.py. - The parent spawns a child:
python -c 'import time; time.sleep(60)' -m pytest. - Teardown kills only the parent:
parent.kill(); parent.wait(...). - The child is re-parented to pid 1 and lives up to 60s after the test. Every test that uses the fixture leaks one such process.
Evidence:
- 2026-10-11 21:20: 14 orphans were alive after a run of
tests/claude. Each had ppid 1 in/proc/<pid>/statand cwdwt-COVERAGE-GATE. - 21:31: four more, e.g. pid 1158826:
/home/mohamed/workspace/wt-COVERAGE-GATE/.venv/bin/python -c import time; time.sleep(60) -m pytest, ppid 1.
Anything that scans /proc sees these as pytest processes:
- the status hook you are fixing (Part 2);
- your host-safety proof (Verification 2);
- any concurrent test that enumerates pytest processes.
2. Fix, red first
- Factor, do not replace.
- Move the fixture's body into a module-level context manager,
_live_run_tree()(contextlib.contextmanager). It yields the same object the fixture yields today, plus a way to read the child's pid; add that only if it is not already exposed. - The fixture keeps its name, scope and yield value, and becomes
with _live_run_tree() as tree: yield tree. - No existing test changes, and no assertion changes.
- Move the fixture's body into a module-level context manager,
- Red test first. Add a test that uses the context manager directly:
def test_live_run_tree_leaves_no_child_behind() -> None: with _live_run_tree() as tree: child = <the child pid> assert Path(f"/proc/{child}").exists(), "precondition: the child is running inside the block" deadline = time.monotonic() + 5 while Path(f"/proc/{child}").exists() and time.monotonic() < deadline: time.sleep(0.1) assert not Path(f"/proc/{child}").exists(), f"child {child} outlived the fixture"- Run the module through the wrapper before the fix. Quote the red output: the child outlives the block.
- A zombie (
State: Zin/proc/<child>/status) still has a/procentry. If the red result depends on that, say so and quote/proc/<child>/status.
- The fix, inside
_live_run_tree():- start the parent with
start_new_session=True; - at teardown,
os.killpg(parent.pid, signal.SIGKILL)(tolerateProcessLookupError), thenparent.wait(timeout=...); - then poll
/proc/<child_pid>for up to 5s and raiseAssertionErrorif it remains. A leak becomes a red result instead of a silent one.
- start the parent with
- Same treatment for any other fixture in the files you touch that spawns grandchildren. Check
foreign_run. Report each fixture you checked, and what you found. - Run the module again. It must be green; quote the totals.
3. Verification additions
- Do Verification 2 (host-safety proof) after this fix.
- Also report that no
-m pytestorphan with ppid 1 and your worktree as cwd remains after the q8 run. Run this and quote its output (empty is the pass):for p in $(pgrep -f -- '-m pytest'); do [ "$(awk '{print $4}' /proc/$p/stat 2>/dev/null)" = 1 ] && readlink /proc/$p/cwd; done | grep -c wt-HOOK-MATCH
4. Finish
Complete your brief's done-when and write plans/briefs/qwen/Q8-report.md. Include this item with its red/green
evidence and the orphan check output. Do not commit.