Streaming callers see every terminal event their result reports - #446
Merged
Conversation
`execute_with_options_streaming` calls `on_output` once per top-level statement, and `kaish -c` prints only what `on_output` delivers. Three paths put text in the returned result that never reached the callback, or ran a program the caller had already cancelled. An `exit` statement added its drained stderr to the aggregate but streamed the statement without it, so `kaish -c 'exit $(ls /nope; echo 3)'` printed no `ls` error while the same substitution under `echo` did. The watchdog wrote `timeout: timed out after ...` only into an empty `err`, so a timed-out program that printed any stderr, including `sleep: interrupted`, exited 124 with no timeout line. It now appends the line the way the `timeout` builtin does, and streams it, since no statement carries a deadline. Whole-program jobs receive it through `on_output`, so their own timeout plumbing is gone. `execute_streaming_inner` reset the cancel token its caller had just installed. When an embedder or job token fired before the first statement, the reset swapped in a fresh token and the program ran to completion: `echo a; echo b` under a cancelled token exited 0. The loop now keeps the installed token and stops at its first checkpoint. Co-Authored-By: DeepSeek V4 Flash <noreply@deepseek.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`execute_with_options` documents 124 for a timeout and 130 for a cancellation. A timeout already reported 124 regardless of the child's signal, but a cancel that killed a lone external command reported the child's own status: 143 after SIGTERM, 137 after the grace escalated to SIGKILL. The statement loop's 130 checkpoint runs only between statements, so a single statement never reached it. After the run, a cancelled token on a failed, non-timed-out result now sets 130. The token decides, not the code, so `exit 143` is unchanged. `kernel_cancel_kills_running_external` drops its 130-or-143 hedge. `grace_escalation_sigkills_term_trapping_child` asserted 137 as its evidence of escalation; it now expects 130 and keeps the evidence that already proved it: the child ignores SIGTERM, dies anyway, and not before the grace. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tobert
added a commit
that referenced
this pull request
Sep 13, 2026
tobert
added a commit
that referenced
this pull request
Sep 13, 2026
Brings in #445, #447, #450, #446, and #448. Two conflicts, both from additions at the same spot: CHANGELOG.md Fixed bullets (all kept, in merge order) and scheduler/pipeline.rs, where #448's `fault_result` and this branch's `redirects_stdout` were each added after `finalize_scatter_gather_error`. Both helpers are kept; the dispatch site merged cleanly with `fault_result(e)` and the stream-flag restore. Co-Authored-By: Claude Opus 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.
execute_with_options_streamingcallson_outputonce per top-level statement, andkaish -cprints only whaton_outputdelivers. Text that reaches the returned result but not the callback is text that caller never shows. Three paths did that, or ran a program the caller had already cancelled.An
exitstatement added its drained stderr to the aggregate but streamed the statement without it:The watchdog wrote
timeout: timed out after ...only into an emptyerr. A timed-out program that printed any stderr, includingsleep: interrupted, exited 124 with no timeout line. The line is now appended after existing stderr, as thetimeoutbuiltin does, and streamed, since no statement carries a deadline. Whole-program background jobs receive it throughon_output, so their separate timeout handling is removed.execute_streaming_innerreset the cancel token its caller had just installed. When an embedder or job token fired before the first statement, the reset replaced it and the program ran to completion;echo a; echo bunder a cancelled token exited 0. The loop now keeps the installed token and stops at its first checkpoint.A cancelled call now exits 130 whatever signal ended its child, matching how a timeout exits 124. A cancel that killed a lone external command reported the child's own status, 143 after SIGTERM or 137 after the grace escalated to SIGKILL, because the statement loop's 130 checkpoint runs only between statements. The token decides, not the code, so
exit 143is unchanged.grace_escalation_sigkills_term_trapping_childused 137 as its evidence of escalation; it now expects 130 and keeps the evidence that already proved it, since the child ignores SIGTERM, dies anyway, and not before the grace. If 137 should stay visible to callers, that assertion is the one to revisit.The new streaming tests use only builtins, so a default
cargo testruns them.This stacks on #445.
Co-Authored-By: DeepSeek V4 Flash noreply@deepseek.com
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
🤖 Generated with Claude Code