feat(shell): add wait_background_job tool#3463
Merged
Merged
Conversation
Add a wait_background_job tool to the shell toolset that blocks until a
background job finishes and returns its exit code and captured output.
Previously agents had to poll view_background_job in a loop. The new
tool blocks on a done channel closed by monitorJob, avoiding polling
entirely. An optional timeout parameter (default 60 s) caps the wait;
the job keeps running if the timeout fires.
Changes:
- Add done chan struct{} to backgroundJob, closed by monitorJob via a
deferred close (LIFO: Unlock fires before close, ensuring callers see
fully written state)
- Extract renderBackgroundJob() helper; refactor ViewBackgroundJob to
use it (no behavior change). Fix pre-existing issue: stopped jobs no
longer incorrectly show Exit Code: 0
- Add WaitBackgroundJob handler with ReadOnlyHint: true; select on
job.done / timer.C / ctx.Done
- Register wait_background_job in Tools() and Instructions()
- Update docs: six tools, wait_background_job parameters table
- Add 7 tests: completed, failed-exit, already-completed, unknown-id,
timeout, context-cancelled, and stopped-then-wait
- Update teamloader readonly integration tests to include
wait_background_job in the read-only tool set
Closes docker#3462
docker-agent
reviewed
Jul 3, 2026
docker-agent
left a comment
Contributor
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
The wait_background_job implementation is correct and well-structured. Key design points verified:
- Defer ordering is correct:
defer close(job.done)is registered first,defer job.outputMu.Unlock()second — LIFO ensures the mutex is released before the channel is closed, soWaitBackgroundJobreaders always see fully-committed state. - Stopped-job path:
monitorJobacquires the lock, detectsstatusStopped, returns early; both defers still fire in correct order.statusStoppedis present instatusStringsso the map lookup inWaitBackgroundJobis always valid. - No double-close risk:
doneis only closed insidemonitorJob, which runs exactly once per job. - Timeout path:
renderBackgroundJobis safely callable while the job is running (usesoutputMu.RLock). - 7 tests cover: completed, failed-exit, already-completed, unknown-id, timeout, context-cancelled, and stopped-then-wait.
No bugs found in the changes introduced by this PR.
Sayt-0
approved these changes
Jul 4, 2026
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.
Adds a
wait_background_jobtool to the shell toolset that blocks until a background job finishes and returns its exit code and captured output.Previously, agents had to poll
view_background_jobin a loop to know when a background job completed. The new tool blocks on adone chan struct{}closed bymonitorJob, eliminating polling entirely. An optionaltimeoutparameter (default 60 s) caps the wait; the job keeps running if the timeout fires and the tool returns the current output with a notice.Key changes:
done chan struct{}onbackgroundJob, closed bymonitorJobvia a deferred close (LIFO:Unlockbeforeclose, so callers see fully written state)renderBackgroundJob()helper; refactorViewBackgroundJobto use it; fix pre-existing issue: stopped jobs no longer showExit Code: 0WaitBackgroundJobhandler withReadOnlyHint: true; selects onjob.done / timer.C / ctx.Donewait_background_jobparameters tableCloses #3462