🤖 feat: return task_await on first completion via min_completed#3414
Conversation
task_await now returns once min_completed awaited tasks have completed (default 1 = first completion) instead of always blocking on every task. The parent can act on each result as it lands while the rest keep running, then re-await the remainder. Losing waits are detached via per-task AbortControllers without terminating the children, which stay re-awaitable. Updated task/task_await tool descriptions and the best-of-n/task-variants prelude to steer aggregation flows to pass the batch size.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f8ab1d87f4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
task_await's early-return aborts still-pending bash getOutput reads once min_completed is satisfied. getOutput's interrupted path advanced outputBytesRead without flushing accumulatedRaw, so an aborted read could drop an unread line fragment and the next read would resume past it. Save the consumed bytes back into incompleteLineBuffer before returning. Adds a deterministic regression test.
|
@codex review Addressed the P1 on interrupted bash reads. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7a5f038601
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
If an awaitOne path rejected outside its internal catches (e.g. a bash getProcess/getOutput read throwing), the recording callback never inserted a result, so the min_completed gate could never reach the threshold or all-settled and would stall. Wrap each per-task promise so a stray rejection becomes a recorded error result; the task counts as settled and the call returns promptly. Adds a regression test.
|
@codex review Addressed the P2 on rejected waiters. Each per-task promise is now wrapped so a stray rejection escaping |
|
Codex Review: Didn't find any major issues. Chef's kiss. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Summary
task_awaitnow accepts an optionalmin_completedinteger (default 1). It returns as soon as that many awaited tasks have completed — by default the first completion — instead of always blocking until every awaited task finishes. The parent can act on each result as it lands (e.g. integrate variant lane #45 while #32/#69 keep running) and re-await the remainder, rather than idling until the whole batch is done.Background
When the parent spawns a grouped batch (
taskwithnfor best-of-N orvariants), there was often useful dependent work available after just one child completed — most obviously forvariants, where each lane is independent. But both the foregroundtaskpath andtask_awaitusedPromise.alland only returned once the slowest task settled, and the tool/prelude guidance framed the flow as "launch → await as a batch → synthesize all." Nothing let the parent begin work after the first child.Implementation
toolDefinitions.ts): newmin_completed: z.number().int().min(1).nullish()onTaskAwaitToolArgsSchema; absent/null⇒1.task_await.ts): the per-task await body is nowawaitOne(taskId, taskSignal). Each task gets its ownAbortControllerchained to the tool-call signal. A small coordinator resolves oncemin_completedtasks have completed or every task has otherwise settled (so an unreachable threshold still returns promptly). Still-pending "losers" are then aborted to detach their waiters — which only removes the in-memory waiter / interrupts a bash poll; the child keeps running and its report stays cached and re-awaitable on a later call. Losers are reported with a liverunning/queuedstatus snapshot rather than an error (a real tool-call interrupt is still distinguished and surfaced aserror: "Interrupted").timeout_secs: 0stays a non-blocking snapshot of every task regardless ofmin_completed.task/task_awaitdescriptions and the<best-of-n>/<task-variants>prelude blocks now steer independent lanes toward the first-completion loop, and tell best-of-N synthesis (which must compare every candidate) to passmin_completedequal to the batch size or use a foreground grouped spawn. Auto-generated docs (system-prompt.mdx,hooks/tools.mdx) and the built-in skill snapshot were regenerated viamake fmt.The foreground
task(run_in_background: false) path is intentionally unchanged — grouped spawns still return all reports, which remains the natural "give me every candidate" path.Validation
task_awaittests: default returns after first (restrunning);min_completed = totalwaits for all;min_completed = kreturns after the k-th; clamp; re-awaitable loser resolves on a follow-up call; unreachable threshold returns promptly;timeout_secs:0non-blocking withmin_completed. The first-completion test also asserts the loser's per-task signal is aborted while the winner's is not.make static-checkpasses (typecheck, eslint, formatting, docs-sync).Risks
min_completeddefaults to1, so this is a behavior change fortask_await: an existing background-spawn-then-await flow that expected all reports now returns after the first completion. Mitigations: the all-settled fallback + clamp keep results well-formed, losers remain re-awaitable (no work lost, children not terminated), and guidance steers aggregation flows to pass the batch size. Affected area is limited to sub-agent orchestration.Generated with
mux• Model:anthropic:claude-opus-4-8• Thinking:xhigh• Cost:$5.76