Repository navigation
Conversation
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The daemon now ends the executor (and with it the box) when it cannot record the launch pid after starting it, or when it loses track of the executor during the start-up watch or in the poller. ProcessChild::kill calls std::process::Child::kill (TerminateProcess on Windows) and is Ok for an executor that has already exited. A kill error is logged as a warning and never replaces the original error or keeps the launch from being marked exited. The README's 500 list names the watch failure. Behaviour change: a box whose launch fails after the executor started is now ended; before, the daemon dropped the handle and left it running. Terminating wxc-exec ends the box's processes (lab-observed). Tests: 6 on Linux, 7 on Windows (the seventh is a Windows-only unit test that stops a real child process). All are baseline tests; one baseline test's early-exit case now expects the documented 500 (ledgered). No writer tests. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The daemon now stops the executor (and with it the box) when it cannot record the launch pid after starting it, or when it can no longer watch the executor during the start-up watch or in the poller; before, it dropped the handle and left the process running. A small stop_executor helper does this at all three places. ProcessChild::kill calls std::process::Child::kill (TerminateProcess on Windows) and is Ok for an executor that has already exited. A kill error is logged as a warning and never replaces the original error or keeps the launch from being marked exited. The poller's log line and doc comment now say the executor is stopped. README: the 500 list names the watch failures, a new sentence says that a running sandboxed session the daemon can no longer watch is stopped, and "within three seconds" now clearly refers to the executor. Terminating wxc-exec ends the box's processes (lab-observed). Behaviour change: a box whose launch fails after the executor started, or which the poller can no longer watch, is now stopped. Tests: 6 on Linux, 7 on Windows (one Windows-only unit test that stops a real child process). They check that the executor is stopped on each of the three paths and not on spawn failure, early exit or normal exit, that the poller stops it before the launch directory is removed, that the launch directory is gone after a watch failure, and that a kill error does not replace the original error. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Documentation only; no behaviour or test change (6 tests on Linux, 7 on Windows). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟢 Approval recommended
The focused termination changes preserve error handling and cleanup behavior, with regression coverage and no identified blocking issues.
No open issues 🎉
What changed in this PR
Stops sandbox executors when the daemon cannot record or monitor them, preventing sessions from continuing untracked.
Changes:
- Adds explicit termination to all three tracking-failure paths while preserving original errors.
- Adds regression tests and a Windows process-termination test.
- Documents the behavior without changing daemon-restart survival.
| File | Description |
|---|---|
| README.md | Documents termination on tracking failures. |
| crates/agent/src/launch/writer_tests.rs | Updates the test child with kill support. |
| crates/agent/src/launch/tests.rs | Tests termination, error preservation, and cleanup. |
| crates/agent/src/launch/mod.rs | Stops executors before tracking-failure cleanup. |
| crates/agent/src/launch/host.rs | Implements process termination and tests it on Windows. |
Review effort: Balanced
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(agent): stop a sandboxed session the daemon has lost track of
Builds on PR 1 and lands after it. When the daemon starts the MXC executor (
wxc-exec.exe) but thencannot record the launch pid, or loses track of the executor during the start-up watch or later in
the poller, it now stops the executor instead of dropping the handle. Stopping the executor ends
the box: in the lab every process with the box's AppContainer identity was gone within seconds.
Before this change such a box kept running, unrecorded and with a live credential file until the
next sweep.
What changes
LaunchChild::kill(Ok for an executor that has already exited);ProcessChildcallsstd::process::Child::kill(TerminateProcess on Windows).stop_executorhelper at the three places that lose track of the executor. A kill error islogged as a warning and never replaces the original error or keeps the launch from being marked
exited.
no longer watch, is now stopped.
daemon can no longer watch is stopped.
explicit, not on drop).
Process evidence
(The spec, review and lab paths below live in the engineering workspace used to build this series,
not in this repository.)
specs/PR-2.md(v5 frozen after four Opus spec-review rounds; the scope was cut from theoriginal policy/engine PR to this one change, the rest is listed under "Later").
real child process; strengthened and renamed during review, every change ledgered
(
specs/PR-2-test-changes.md). 6 on Linux, 7 on Windows.lab/evidence/pr2a/gate-v2.log; v3 is a doc comment, fmt + clippy re-run): fmt, clippy-D warnings, unit tests, schema, frontend build + lint, Storybook, test guard: PASSED.reviews/PR-2a-*.md): conformance (Sonnet), adversarial (Opus, mutation probes),maintainer fit (Opus) on v1; delta review v2 (Opus) pass with minors; v3 wording-only.
Fixed on the way: the missing README sentence, a test that did not check the launch directory was
removed, a poller test that could not detect a kill after the directory removal, two test errors
that could not be told apart.
lab/evidence/pr2a/first-run-92e99b1.md, Windows 11 VM, build = v3 + fix(agent): store Windows secrets as encrypted files keyed from Credential Manager agentdesktop-dev/agentdesktop#171): K0–K2 pass.wxc-exec(the pid the daemon recorded) ended all nine descendants (ClaudeCode,
cmd,conhost, all with the box's AppContainer token) within 10 s; the launch showsexited,exitCode -1, its directory removed.POST /v1/messages 200, normal exitexitCode 0.unit tests cover them.
Known limits and follow-ups
can race the teardown and leave the credential file until the next startup sweep.
pin it before the directory removal).
cargo clippy --all-targets --target x86_64-pc-windows-gnufails on an existingclone_on_copyin
bin/agentdesktop-service.rs:83, a file this PR does not touch; separate fix.todo!()stubs).🤖 Generated with Claude Code