S00-014: Make successful runner process-tree cleanup idempotent

Changes: Record completed termination on the Popen instance after all cleanup steps succeed; avoid repeated signals to the reaped group. Add deterministic repeated-EPERM and failed-descendant-kill retry regressions without suppressing permission errors.
Validation: Four focused cleanup tests passed; make check passed 72 tests and plan/snapshot validation. git diff --check passed. Natural prior EPERM did not recur; injected EPERM reproduced the pre-fix failure. No application or parent-task acceptance is claimed.

Task: S00-014
Agent: auto-ci-cd-14343249da62
Role: ci-cd
Action: claim
Status: review

Task title: Fix repeated runner cleanup after a submitted parent is reaped
This commit is contained in:
wt committed 2026-10-15 01:47:20 +07:00
1 parent 49c054aaf9
commit 7a737eeb49
9 files changed
+106 -9

No files matched your search

@@ -0,0 +1,25 @@
# S00-014 executor evidence
Agent: auto-ci-cd-14343249da62; role: ci-cd; date: 2026-10-15.
Result: repeated process-tree cleanup correction, pending independent QA review.
Input: assigned card/context and exact NFR-007, startup/workflow documents, S00-012 preserved implementation/evidence/handoff, S00-012/S00-014 blocker reports, accepted ADR 0000 and direct dependency evidence/reviews/handoffs. Output: idempotent successful process-tree termination for the same Popen instance. Acceptance: no repeated signal after successful cleanup, real descendant termination on timeout/submit, and termination errors still propagate.
## Actual change
stop_process_tree records completion on the Popen object only after its existing termination steps all succeed. The transition/deadline call and finally therefore share a completed cleanup result. The first call still sends SIGTERM, waits/reaps the parent and sends SIGKILL to descendants even if the parent exited. An inaccessible live group raises PermissionError; no completion marker is set on failure, and a later retry executes termination again. No permission error is suppressed. Existing monitoring, stdin transport, prompt budgets and workflow transitions are unchanged.
Added two deterministic signal regressions: a completed cleanup is followed by injected EPERM if another signal is attempted; a failed final SIGKILL must propagate and allow a complete retry. Existing real subprocess regressions launch a SIGTERM-ignoring descendant that would write source after 1.5 seconds, and check that both submit and timeout cleanup prevent the write.
## Actual commands and results
- python3 scripts/tasks.py resume --agent auto-ci-cd-14343249da62 and show S00-014: exit 0; already assigned in_progress, no repeated claim.
- Initial python3 -m unittest discover -s tests/tooling -p test_automation.py -k submit_transition_stops_source_writing_descendant -v: exit 0, 26 tests in 11.180s. The custom load_tests ignores -k and ran the entire automation suite. The previous sandbox EPERM did not recur naturally in this session; no claim of a fresh real-syscall reproduction is made.
- Before runner change: PYTHONPATH=tests/tooling python3 -m unittest test_automation.RunnerTests.test_completed_cleanup_does_not_signal_reaped_group_again test_automation.RunnerTests.test_failed_descendant_kill_is_not_marked_complete -v: exit 1; two tests, completion test errored with injected PermissionError at runner.py os.killpg(SIGTERM), failure/retry test passed. This deterministically reproduces the repeated-signal cause without launching a model.
- After runner change: PYTHONPATH=tests/tooling python3 -m unittest test_automation.RunnerTests.test_completed_cleanup_does_not_signal_reaped_group_again test_automation.RunnerTests.test_failed_descendant_kill_is_not_marked_complete test_automation.RunnerTests.test_submit_transition_stops_source_writing_descendant test_automation.RunnerTests.test_timeout_stops_source_writing_descendant -v: exit 0; 4 tests in 3.981s. Real descendants cannot perform the delayed source write, submit remains review, timeout creates its valid remediation, and scope violations are empty.
- make check > /private/tmp/luma-S00-014-check.log 2>&1: exit 0; 72 tests in 17.247s; validated 279 tasks, 22 stages, 221 requirements, 211 allocated Web requirements, revision 20. Includes complete delayed-reader 140024-byte UTF-8/EOF, fresh subprocess chain, transition/deadline, scope, prompt budgets, snapshot and CLI checks.
- git diff --check: exit 0; read-only source diff inspected. Source changes limited to scripts/runner.py and tests/tooling/test_automation.py. Initial state/tasks.json change was coordinator claim metadata.
## Limits
No model, product server, browser or application readiness check was performed. The earlier EPERM policy cause remains unproven; its failure condition is covered by deterministic signal injection and the preserved real subprocess checks. Windows-specific cleanup was not exercised on this macOS/Python 3.9.6 harness. Successful SIGKILL retains the existing operating-system termination guarantee; no new process enumeration or permission bypass is introduced. No dependency, immutable spec, contract, Git configuration or production transport changed. Coordinator owns the implementation commit and fresh independent reviewer. Parent S00-012 and root S00-002 are not accepted by this task.