Skip to content

First-class bounded batch fan-out for heterogeneous subagents #39129

Description

@Ducheved

What variant of Codex are you using?

Codex App primarily. The orchestration primitive would ideally be shared with CLI/tool-backed Codex surfaces.

What feature would you like to see?

Summary

Codex now has most of the pieces needed for heterogeneous multi-agent work: subagents, per-agent model configuration, async orchestration, and configurable concurrency.

The missing primitive is bounded batch fan-out.

A coordinator should be able to submit a large set of independent tasks in one operation, have Codex schedule them across cheaper/faster worker agents with explicit limits, then collect bounded results for synthesis.

The motivating shape is:

                strong coordinator
                       |
             decompose / schedule
                       |
         +-------------+-------------+
         |             |             |
      fast worker   fast worker   fast worker
         |             |             |
         +-------------+-------------+
                       |
                  coordinator
                     reduce

For example: a Sol coordinator analyzing a large multi-repository system could dispatch 80 independent repository audits to Spark workers, run at most 32 concurrently, then synthesize their findings.

This should not require the coordinator to make 80 individual spawn_agent calls and manually manage 80 wait_agent lifecycles.

Problem

spawn_agent is a good primitive for small, adaptive teams where the parent decides what to delegate one task at a time.

It is a poor primitive for workloads where the complete work set is already known:

  • inspect every package in a monorepo;
  • review 100 independent files/modules;
  • run the same migration audit across many services;
  • investigate one question independently against many repositories;
  • generate bounded implementation/review tasks from an already-computed plan.

In these cases, repeated parent-side spawn/wait decisions add orchestration turns and context churn without adding useful reasoning.

Simply increasing agents.max_threads does not solve that. Batch size and concurrency are different things.

I may want to submit 200 tasks while allowing only 16 or 32 workers to execute concurrently.

Proposed shape

The exact API is not important, but conceptually something like:

batch = spawn_batch(
    worker_profile = "fast_worker",
    tasks = [...],
    concurrency = 32,
    max_tokens_per_worker = 20000,
    total_budget = ...,
    max_result_size = ...,
    recursive_spawn = false,
)

results = wait_batch(batch)

tasks could contain independent prompts plus optional scoped context/path information.

spawn_batch should create one schedulable batch, not require the model to emit N individual spawn calls.

Important semantics

Batch size != concurrency

A batch may contain 200 tasks while the scheduler runs only 16 concurrently.

Excess work should queue rather than requiring the parent model to manage free slots.

Heterogeneous workers

The worker model/profile should be selectable independently from the coordinator.

A common configuration would be:

coordinator: expensive/high-reasoning model
workers:     fast/cheap model
reviewer:    optional stronger model

This is where models such as Spark are particularly useful: not necessarily as the coordinator, but as high-throughput bounded workers.

Explicit budgets

Large fan-out needs guardrails, not just a higher thread limit.

Useful controls would include:

  • maximum batch size;
  • maximum active workers;
  • model/profile allowlist;
  • per-worker token or credit budget;
  • aggregate batch budget;
  • timeout;
  • sandbox/tool permissions;
  • maximum result size;
  • maximum subagent depth;
  • queue vs fail behavior when capacity is exhausted.

A parent should be able to say “up to 32 Spark workers, 20k tokens each, read-only, depth 1” rather than implicitly creating an uncontrolled swarm.

Bounded result aggregation

The coordinator usually does not need the complete transcript of every worker.

By default, batch completion should return something closer to:

task id
status
final worker result
usage
optional artifact references

rather than injecting every worker's full interaction history into the parent context.

This matters once fan-out becomes large.

Partial failure

wait_batch should preserve successful results when some tasks fail, time out, or are cancelled.

It should be possible to retry selected task IDs without rerunning the whole batch.

Cancellation and observability

A batch should have an ID and expose:

queued
running
succeeded
failed
cancelled
token/credit usage

The user should be able to cancel the batch or individual tasks.

Why make this a first-class primitive?

A sufficiently capable coordinator can approximate this today with repeated spawn_agent calls.

But that pushes deterministic scheduler work back into model reasoning:

find free slot
spawn worker
remember worker id
wait
inspect completion
spawn next
repeat

None of those steps requires a frontier reasoning model.

A runtime-level batch primitive could handle queueing, limits, cancellation and result collection deterministically while leaving the coordinator responsible for the parts that actually require reasoning:

decomposition
task specification
dependency identification
final synthesis

This also makes very large fan-out safer. The runtime can enforce hard budgets even if the coordinator attempts to over-spawn.

Concrete example

Consider an architecture audit over 80 repositories.

The coordinator first determines that each repository can be inspected independently.

Desired execution:

Sol:
  build 80 bounded audit tasks
       |
       v
spawn_batch:
  worker = Spark
  tasks = 80
  concurrency = 24
  read-only
  depth = 1
       |
       v
scheduler:
  24 running
  56 queued
       |
       v
80 bounded reports
       |
       v
Sol:
  cross-repo synthesis
  contradictions
  architecture decision

The expensive coordinator is used where its reasoning matters. The fast workers provide breadth.

Acceptance criteria

  • A coordinator can submit N independent subagent tasks in one tool/runtime operation.
  • Batch size may exceed active concurrency.
  • Queued work starts automatically as worker capacity becomes available.
  • Worker model/profile may differ from the coordinator.
  • Per-worker and aggregate budgets are enforceable by the runtime.
  • Worker recursion/depth can be disabled or bounded.
  • Batch status, usage, cancellation and partial failures are observable.
  • Results can be collected without importing full worker transcripts into parent context by default.
  • Individual failed tasks can be retried without rerunning successful tasks.
  • Existing spawn_agent remains available for adaptive one-off delegation.

This is intended to complement, not replace, normal subagents and agent teams.

Additional information

Related requests

I searched the existing Codex issues before filing this.

Two current reports also show why explicit runtime scheduling and budgets matter:

I am not asking for an unlimited subagent count.

I am asking for a bounded map/fan-out primitive where the runtime, rather than the coordinator model, handles queueing, concurrency, budgets and result collection.

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appenhancementNew feature or requestsubagentIssues involving subagents or multi-agent features

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions