Skip to content

server: end the uds worker's processes at gateway shutdown (CSRG-U6) - #98

Merged
novelKR merged 1 commit into
mainfrom
claude/project-thread-af6ntg
Oct 13, 2026
Merged

novelKR merged 1 commit into
mainfrom
claude/project-thread-af6ntg

Conversation

@novelKR

@novelKR novelKR commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

A bounded correction found by the CS-RG harness on the qualification Mac (CSRG-U6 plan, step 8). Nothing here runs or claims a qualification.

No pin change. No change to the installed service.

Issue

Scope: CSRG-U6. The faults suite's uds shutdown case failed on the Mac's check of #97. Tracked in #76 and novelKR/DevGuard#14. This PR does not close an issue.

Item Value
Base main at 2117800 (#97's merge)
Head b74e5c2
Pins Codex gitlink 6b9826e3 with the recorded patch, DevGuard 6e7e065; both unchanged

Summary

Before: in uds mode, gateway shutdown (stdio EOF included) killed the worker outright, so the worker never ended its executions. A PTY execution ended on its terminal's hangup, but a pipe execution kept running, as nothing gives it a parent-death signal on macOS. On the Mac a governed /bin/sleep 30 outlived the gateway until it ended by itself, about 30 seconds later, and only then was its DevGuard attempt released. The documentation already said that gateway shutdown ends the worker's processes.

After: at shutdown the gateway closes the worker's connection and waits for the worker to exit. The worker ends its processes as it exits, as an in-process runner's processes end when the gateway exits. A worker still running 5 seconds later is killed, as before.

How. RuntimeProcess::wait_exit closes the connection, then has the wait task give the worker WORKER_EXIT_GRACE (5 seconds) before it kills it. The close is bounded by the same grace, so a worker that no longer reads cannot hold up shutdown. A connection lost while the gateway runs gets the same grace. main now stops the runner it started before it returns, instead of dropping it.

Also found on the Mac. A tty: true process launched through DevGuard reports a signal death as exited with 128 plus the signal, such as 143 for SIGTERM, not 1: it inherits descriptors, so the pinned Codex PTY spawns it on its descriptor-keeping path. #97 corrected the integration guide; this corrects the process_status tool description and a runner comment to match.

Contract changes

  • The process_status description adds "or 128 plus the signal number when it was launched through DevGuard". That is what such a process already reports.
  • In uds mode, gateway shutdown now waits for the worker to exit: usually well under a second, at most 5 seconds before the worker is killed, or 10 when its connection is stuck too.
  • No tool schema, error code, status value or ID changes. In-process mode is unchanged.

Tests

  • New runtime::tests::a_stopped_worker_ends_its_processes starts a real worker with a pipe and a PTY process that each count into a file. wait_exit must return before the grace, so the worker exited by itself, and neither count may move afterwards. With the old immediate kill it fails: the pipe process kept counting, from 1 to 15 in 1.5 seconds, while the PTY process stopped.

Verified in the cloud (Linux).

  • On this head, with a debug worker built from it as CODESPACE_RUNTIME_BIN: cargo test -p codespace-server --lib passed all 32 tests. cargo fmt --check, scripts/check_docs.py (19 paired documents) and scripts/ci_plan.py pass.
  • On this commit applied to 7024f74 (scripts, docs: CS-RG qualification harness and protocol (CSRG-U6) #97 changes no Rust code except one doc comment): cargo clippy -p codespace-server --all-targets -- -D warnings is clean with and without --features devguard.

CI: passed on this head (run 38391100965): format and upstream pin, plan, policy scan, clippy (root and codex-adapters), codex-runtime unit tests, macOS contracts and integration. Linux isolation is not in the plan for the runner and server components this change touches. The documentation site build passed too (run 38391102081).

On the qualification Mac (macOS 27.0, Apple M1), on this head, in a minimal environment with Rust 1.95.0: release builds of codespace-mcp and codespace-codex-runtime from this head with --features devguard, the installed release 0.1.0-6e7e065-94d85365, and the same fixture authority, launch helper and devguard-qualify as #97's check, whose sources did not change. Before each suite nothing was charged, no codespace instance was active and memory pressure was normal. Afterwards nothing stayed charged, every codespace instance was retired, and the service kept its process.

Suite Result
faults Passed: all 27 cases in both runner modes (13 in each, and worker-killed in uds only). shutdown in uds mode passed in 3.3 s with ended_s [0.0, 0.0] for its two process groups, as in-process mode did. On #97's check it took 33.2 s with [null, 0.0], as the pipe execution's group had not ended. The case's successor gateway wrote its first log line 1.06 s after the first gateway's input closed (1.04 s in-process); that span holds the gateway's exit, the worker's stop included.
matrix --release 0.1.0-6e7e065-94d85365 Passed: all 45 checks in both runner modes, including the uds gateway stops and the runner socket.
authority Passed: all five phases in both modes. The restarted targets were admitted after 35.6 s (in-process) and 33.7 s (uds), the fixture's recovery after its hold; those of every other phase within 0.12 s.
verify All three sealed directories verified: recorded and recomputed verdicts agree.

The evidence is sealed on the Mac; MANIFEST.json SHA-256 by suite: faults 6057f26fb81a59f4f8ef6f8c12ff95235ae25c10b7503c7a6f2aaa5264ddae8b, matrix 577962c15c89e70b4debfe93c2f7513876fa592c6d15ff86bf7535a585121a76, authority dad385b738361ef883928329f3ed6879f9a874ebdc32f3bdd5aed464ba6dc679.

Security scenarios

  • The worker's descriptors, environment and credential hand-off are unchanged.
  • Shutdown cannot hang on the worker: the close and the wait are both bounded, then the worker is killed.
  • No execution outlives the gateway's shutdown in uds mode any more than in in-process mode.

Out of scope

  • The other qualification suites, the frozen candidate, and any qualification claim.
  • Reconnect recovery and a worker that outlives its gateway (P1-RECOVERY).
  • Background processes that a pipe execution leaves behind. As in in-process mode, the worker ends the processes it started.

At shutdown the gateway killed its worker outright, so the worker never
ended its executions, and nothing gives a pipe execution a parent-death
signal on macOS. The CS-RG harness's uds shutdown case found a governed
`sleep 30` outliving the gateway on the qualification Mac.

The gateway now closes the worker's connection and waits for the worker,
which ends its processes as it exits, as an in-process runner's processes
end when the gateway exits. A worker still running 5 seconds later is
killed; the close is bounded by the same grace. main stops the runner it
started before it returns instead of dropping it.

The process_status description and a runner comment now say that a tty
process launched through DevGuard reports a signal death as 128 plus the
signal, as the harness found on the Mac.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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