fix(rust): prevent orphaned CLI processes - #2292
Conversation
Bind each spawned CLI transport to an SDK-owned process tree before it can create descendants. Use a kill-on-close Job Object on Windows and a process group on Unix, and carry the RAII owner through startup, stop, force-stop, and drop paths. Cover grandchild teardown and startup-failure cleanup without changing the public client API. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1090dd5b-e149-4d9d-9cc3-67f26e11ad06
There was a problem hiding this comment.
Pull request overview
Adds cross-platform process-tree ownership and teardown for Rust SDK-spawned CLI processes.
Changes:
- Uses Unix process groups and Windows Job Objects.
- Integrates tree termination into startup, stop, force-stop, and drop paths.
- Adds lifecycle and process-tree tests.
Show a summary per file
| File | Description |
|---|---|
rust/src/process_tree.rs |
Implements process-tree lifecycle management. |
rust/src/lib.rs |
Integrates managed children into the client lifecycle. |
rust/src/errors.rs |
Updates teardown error documentation. |
rust/Cargo.toml |
Adds platform-specific dependencies. |
rust/Cargo.lock |
Locks the added dependencies. |
Review details
- Files reviewed: 4/5 changed files
- Comments generated: 3
- Review effort level: Balanced
Match the repository's nightly rustfmt configuration on Linux. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1090dd5b-e149-4d9d-9cc3-67f26e11ad06
|
Spent time on this against github/app#2303. I think the fix is already in this diff — it's just not the headline change.
Could we land this PR as just those two changes plus their tests? That fixes #2303 on its own. On the process-tree work — I don't think it's wrong, but it's solving a different failure mode and I'd like to separate it. The issue reports "61 orphaned hosts with zero descendants," so descendant containment isn't what's broken here. The genuinely unique thing it buys is Windows
A few things worth carrying into that PR whenever it happens: the reaper thread in Last thing, and I think it's the highest-leverage one: should the runtime just exit on its own? The logs show Generated by Copilot |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Generated by Copilot Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Add a private kill-process-tree helper to each SDK, called from the existing owned-process termination points in stop() and forceStop(). Spawn-time isolation (POSIX): Node.js detached, Python start_new_session, Go SysProcAttr.Setpgid. Teardown: Windows uses taskkill /T /F; POSIX signals the process group; Java collects ProcessHandle.descendants() before terminating the root; .NET already passes entireProcessTree. stop() stays graceful and escalates, and every helper falls back to the single-process termination it replaced. Rust is excluded - github#2292 already fixed it with job objects and process groups. No public API changes. External-server and in-process (FFI) paths are not affected. Closes github#1804
Summary
ClientInnerand its CLI child alive after the lastClientwas droppedkill_on_dropfor SDK-spawned CLI commands so startup failures cannot strand a child before ownership reachesClientInnerFixes github/app#2303.
Validation
dropping_last_client_kills_spawned_clifails against the original lifecycle cycle withchild survived after its owner was dropped, then passes with the fixcargo test --no-default-features --lib dropping_last_client_kills_spawned_clicargo test --no-default-features --lib spawned_child_is_killed_when_droppedGenerated by Copilot