Skip to content

tasksmith: stop the author adapter's whole process tree on Windows too - #166

Open
KNambiarDJsc wants to merge 1 commit into
huggingface:mainfrom
KNambiarDJsc:fix/windows-process-group-cleanup
Open

KNambiarDJsc wants to merge 1 commit into
huggingface:mainfrom
KNambiarDJsc:fix/windows-process-group-cleanup

Conversation

@KNambiarDJsc

Copy link
Copy Markdown
Contributor

Part of #130 (process cleanup, the last item on the file modes / symlinks / process cleanup list).

Problem

run_external_agent cleans up the local author adapter with os.killpg(...) and signal.SIGKILL. Neither exists on Windows, so if a timeout or bridge failure fires while the adapter is still running, the finally block raises AttributeError. That masks the real error, and the adapter's child processes keep running.

Change

stop_process_tree(process, *, force) in tasksmith/author/external_agent.py:

  • POSIX: unchanged. SIGTERM, then SIGKILL after the existing 12 s wait, to the adapter's process group.
  • Windows: taskkill /PID <pid> /T /F. Both the polite and the forced request kill the whole tree. Windows has no graceful group signal, and terminate() alone orphans the children (taskkill /T cannot find a tree once its parent is gone). I hit exactly this in the first version of the test.

Scope: what I deliberately did not change

The other os.killpg sites listed in #130 do not run on the controller:

  • pipelines/recipes/terminal/grade.py, swe_smith/grade.py: standalone verifiers on absolute container paths (/logs, /tests, user=1001), run inside the Linux container.
  • execution/job.py: guarded by REPO2RLENV_REMOTE_WORKER, remote Linux worker only.

opencode.mjs already refuses to run on Windows ("requires macOS or Linux"), so this does not claim native-Windows authoring works; it makes the Python-side cleanup correct and its failure mode honest, and does not change the recommendation to use WSL for full workflows.

Tests

tests/test_external_agent_process_tree.py starts a real adapter stand-in that spawns a grandchild writing a heartbeat file, then asserts the heartbeat stops after cleanup (no OS-specific liveness API needed):

  • force stop ends the adapter and its descendant
  • polite-then-force (the order run_external_agent uses) leaves nothing running
  • stopping an already-finished adapter does not crash

Verified locally on native Windows: 3 passed, plus the existing test_tasksmith_bridge.py (29 passed, 1 skipped for the missing Node runtime); ruff clean. The POSIX branch is not exercised locally, so it relies on the Linux CI jobs running the same three tests.

I did not add the test to windows.yml, to avoid conflicting with the workflow edit in #158; happy to add it once that lands.

🤖 Generated with Claude Code

run_external_agent cleaned up with os.killpg / signal.SIGKILL, neither of which
exists on Windows, so a timeout or bridge failure while the adapter was still
running raised AttributeError from the finally block (masking the real error) and
left the adapter's children running.

stop_process_tree keeps the POSIX behavior (SIGTERM/SIGKILL to the adapter's
process group) and uses 'taskkill /T /F' on Windows. Both requests kill the whole
tree there: terminate() alone orphans children, and taskkill /T cannot find them
once the parent is gone.

The other os.killpg sites (terminal/grade.py, swe_smith/grade.py, execution/job.py)
run only inside the Linux verifier container / remote worker and are unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@adithya-s-k adithya-s-k left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three process-tree tests pass on POSIX, and the Windows route is useful. Two things before merge: taskkill timeout raises subprocess.TimeoutExpired out of the cleanup block and masks the original adapter failure; a nonzero exit is also silently treated as success even when the process remains alive, after which the final await process.wait() has no timeout.

Please handle cleanup failures without losing the original error, bound the final wait, and cover timeout/nonzero taskkill results. Also add these tests to native Windows CI (with the test dependencies installed); otherwise CI only tests the POSIX branch.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants