Repository navigation
fix(runtime): bound the interrupt write so cancelling never waits on a stalled provider - #42
Merged
Merged
Conversation
…a stalled provider A provider busy inside a tool call can stop reading its stdin and leave the pipe full. The adapter-encoded interrupt was written without a bound in both the one-shot cancel path and the retained interrupt path, so cancellation (and the process-tree cleanup after it) waited until the tool ended by itself. Give the write 1 second and treat a timeout like an interrupt the provider did not accept: stop using the writer and terminate the provider. Also document that a one-shot Codex turn takes its background commands with the app-server and point at process retention. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
…lation Also remove a duplicated phrase the previous changelog edit introduced in the neighbouring entry. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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.
What
Bound the adapter-encoded interrupt write so cancelling a turn never waits on a provider that has stopped reading its stdin.
A provider busy inside a tool call can leave its stdin pipe full.
cancel_running_process(one-shot turns) andsettle_retained_interrupt(retained turns) wrote the interrupt frame with no bound, so cancellation, and the process-tree cleanup after it, waited until the tool ended on its own. The write now gives up afterINTERRUPT_WRITE_TIMEOUT(1 s) through one helper,write_interrupt_frame. A timeout is treated exactly like an interrupt the provider did not accept: the writer is dropped and the provider is terminated, as before.No public API changes. Behaviour on a healthy provider is unchanged.
Where this came from
It is the leftover of the uncommitted work in the
fix/windows-runtime-compatibilityworktree (whose PR #16 had already merged). Those edits were written against a base before #36, #37, #40 and #41, and are mostly superseded or in conflict with deliberate design. Each change was checked against currentmain, with live runs against real CLIs (Claude Code 2.1.296, codex-cli 0.162.1) where the behaviour is not visible in a unit test.main: both cancellation tests below time out.INTERRUPT_GRACE5 s to 250 mssleep 90, cancel to return took 49 ms onmain; the grace is only a ceiling for a provider that ignores the interrupt, and the shorter value risks truncating session persistence.result_seen-driven close on drain)ALL_DONE) ran to completion onmainwith both markers written. It also removes the one-shot close-on-drain thatbackground_work_draineddocuments on purpose, and with no follow-up grace in one-shot mode would hold a turn to its timeout whenever Claude does not answer the notification. Retained turns already handle follow-ups (#36).turn/completeduntil startedcommandExecutionitems finish (cap 256)background_terminal_retention_smokeonmain: listening after the turn, after 4 idle timeouts and after a follow-up turn). The change was not scoped to one-shot turns, so a retained turn that starts a dev server would be held open forever, which is what #36 removed for Claude. For one-shot turns the app-server exiting with its children is documented behaviour; this PR documents it more precisely.docs/background-commands.md,examples/background_command_probe.rsTests
Both written first and failing on
main(8 s outer timeout elapsed):cancellation_does_not_wait_for_a_provider_that_stopped_reading_stdin: one-shot cancel with a 1-byte stdin pipe the fixture never reads; assertsCancelledand that the provider was terminated.interrupting_a_retained_turn_does_not_wait_for_a_stalled_provider: the same through the retained interrupt path.New fixture
Script::StalledInterruptintests/codex_app_server.rs.Gates run locally:
cargo fmt --all -- --check,cargo clippy --locked --all-targets --all-features -- -D warnings,cargo test --locked --all-features(all pass).Docs and changelog
CHANGELOG.md: Fixed entry.docs/how-to/keep-tool-processes-running.md: new section "Background commands of a one-shot Codex turn" (observed with codex-cli 0.162: asleep 15started by a turn that had ended never wrote its finish marker) pointing at process retention andManagedProcessSupervisor.🤖 Generated with Claude Code