Conversation
A job started with `Kernel::execute_background_with_options` published output only when each top-level statement finished. A `for` loop or a single `cargo build` showed nothing in `/v/jobs/N/stdout` until it ended, while the same command as `cmd &` streamed live after #449. A shell user expects to see output as it is produced. One flag governed both streams, so turning stdout streaming on for these jobs would also turn on the external stderr tee, and the per-statement writer would write that stderr a second time. The stderr tee now has its own per-job flag, `background_stream_stderr`: true for `cmd &`, false for a whole-program job. spawn.rs tees stderr only when both flags are on, so `cmd &` behaves as before. A whole-program job turns stdout streaming on, and its writer carries only each statement's stderr and the terminal diagnostic. Stderr stays per statement here. Live stderr for every job lands after the execution context is threaded through the interpreter (GH #369). The comment and `JobStreams::stderr` docs that said every pipeline stage tees stderr were wrong: First and Middle stages stream nothing. Both now say where stderr tees. Streaming stdout exposed a routing hole #449 left open. An embedder tool (`backend.call_tool`) returned its result without publishing it, and nothing else published stdout once #449 dropped the completion write, so `embedder_tool &` left `/v/jobs/N/stdout` empty. The whole-program writer had hidden this by publishing whole statement results. The backend-tool arm now publishes through `ExecContext::publish_job_stdout`, the helper gather's rows use. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
kaibo review, cast D1: rendered D2: in AST mode, a whole-program job's stream stayed empty while its result held the AST dump, and the AST test compared only results. Not a regression, since no statement runs in AST mode. Fixed: D3: in a D4: No defect found:
Noted for GH #369, the next PR in this stack:
🤖 |
Review of the previous commit found two stdout producers with no publish. `execute_command_depth` returns rendered `--help` before the tool runs, so `ls --help &` left `/v/jobs/N/stdout` empty (since #449) and a whole-program job would now lose it as well. `execute_streaming_inner` returns the AST dump without running a statement. Both publish through `ExecContext::publish_job_stdout`. `finalize_streams` documented that a whole-program job publishes each statement, which stopped being true; it now lists the producers. The `/v/jobs` help and EMBEDDING.md said stderr takes every stage's. An earlier stage's stderr and a builtin's reach the stream only at completion and only when nothing streamed live, so a last-stage external writing stderr hides them. The text now says so; live stderr for every stage is the later sinks work. The timeout guard test now says it pins "written once", not the backend publish itself. Co-Authored-By: DeepSeek V4 Flash <noreply@deepseek.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A job started with
Kernel::execute_background_with_optionspublished output only when each top-level statement finished, so a loop or a long external command showed nothing in/v/jobs/N/stdoutuntil it ended. The same command started ascmd &streams live. Whole-program jobs now stream stdout the same way: an external command per chunk, a builtin when it returns.One flag governed both streams. Turning stdout streaming on for these jobs would also have turned on the external stderr tee, and the job's per-statement writer would write that stderr again. The stderr tee now has its own per-job flag,
ExecContext::background_stream_stderr: true forcmd &, false for a whole-program job. spawn.rs tees stderr only when both flags are on, socmd &is unchanged. A whole-program job's writer now carries only statement stderr and its terminal diagnostic.Stderr stays per statement in this change. Live stderr for every job is planned on top of GH #369, which threads the execution context through the interpreter so stdout and stderr can be sinks that redirects and
$(...)swap.JobStreams::stderrand the spawn.rs comment said every pipeline stage tees stderr. First and Middle stages stream nothing; both now say where stderr tees.Streaming stdout exposed a routing hole left by #449: an embedder tool's result was never published, so
embedder_tool &left/v/jobs/N/stdoutempty and the whole-program writer had been hiding it. The backend-tool dispatch now publishes throughExecContext::publish_job_stdout, the same helper gather's rows use.background_job_publishes_custom_tool_stdoutfails against main;background_job_publishes_redispatched_custom_tool_stdout_oncepins thattimeout 5 embedder_tool &writes the tool's output once.background_program_publishes_tool_help,background_job_publishes_tool_help, and the stream assertion inbackground_program_in_ast_mode_matches_foregroundfail against the tree before the fix.Two more producers had no publish. A tool's
--helptext returns before the tool runs, sols --help &left the stream empty since #449, and with this change a whole-program job would have lost it too. AST mode returns its dump without running a statement. Both now publish through the same helper.JobManager::finalize_streamssaid a whole-program job publishes each statement; it now lists the producers. The/v/jobshelp and EMBEDDING.md said stderr takes every stage's; they now say an external command at the end of its pipeline streams live and other stderr arrives at completion only when nothing streamed.Tests:
whole_program_external_stdout_is_liveandwhole_program_builtin_output_inside_a_loop_is_livefail against main.whole_program_stderr_stream_matches_the_result_in_orderandwhole_program_substitution_stderr_is_job_stderrpass against main and guard against the duplicate stderr this split prevents.🤖 Generated with Claude Code