anomalyco / anomalyco/opencode

[FEATURE]: Orchestrator agent for parallel subagent fan-out/fan-in (parallel dispatch without polling)

Open
#47,639 1 comment 0 reactions 1 assignee View on GitHub

@rekram1-node is already working on this.

Since Sep 6, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Feature hasn't been suggested before.
  • I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request

Multi-phase tasks are slow in wall-clock time because independent subtasks run sequentially. The task tool already supports background=true and the BackgroundJob registry forks each job into its own scope, so parallel execution is possible — but the model has to orchestrate it manually by issuing multiple task calls in one message and then collecting each background notification. That's error-prone: the model often forgets to wait, polls, or duplicates work.

A focused orchestrator agent that does fan-out / fan-in with discipline would close the gap without a new scheduler:

  • decomposes a complex objective into 2–7 independent subtasks,
  • dispatches each via task with background: true in parallel,
  • respects concurrency: 10 (Effect.all({ concurrency: 10 })) and depth: 1 (subagents cannot spawn further subagents),
  • continues on partial failure and reports it,
  • caps cost per dispatch.

This is the upstream ask in #47105 (task-parallel fan-out) and the open PR #47107 (task-parallel tool, everest-an, non-draft, 4 files). That PR is a solid invoker: pass tasks[], run Effect.forEach, return a <parallel-tasks> summary. This request is the harness around the same idea — scoping it as an agent, not a tool, so the discipline lives in the system prompt rather than in ad-hoc tool arguments.

The two are complementary, not competing: #47107 can land as the builtin tool for explicit calls; this agent is the opt-in harness for when the model should decide to fan out on its own (keyword trigger, no behavior change when the flag is off). If maintainers prefer one path, happy to fold into #47107 as review suggestions (e.g. concurrency: 10 instead of unbounded, opt-in flag) rather than a separate effort.

Why this complements existing issues (honest scope — alivia vs resolve, verificado contra os bodies reais):

  • #37548 (headless exit mid-fan-out) — alivia, não resolve. Fan-in disciplinado via BackgroundJob.wait fecha o race que causa o silent success na maioria dos runs; mas o root cause está no opencode run command (exit 0 antes do next turn) — precisa fix próprio.
  • #43594 (V2 transport hang, prompts grandes) — alivia, não resolve. Cap concurrency: 10 + prompt curto por worker + limite maxPromptBytesPerWorker: 8000 (proposto) reduzem pressão no transport; mas é regression de transport da V2 (funciona em 1.18.15) — precisa fix no transport layer.
  • #45417 (cost exclui subagents no display) — atribui, não agrega. Harness checa totalSessionCost() antes de cada dispatch (cap observável); mas TUI/stats/export precisam somar parent_id recursivo — fix separado no display layer.
  • #44748 (preemption primitives) — referência, não resolve. Harness seria o primeiro consumer de max-turns-per-dispatch/cancel/streaming, mas funciona sem eles.
  • #30308 (dynamic workflows) — referência, não resolve. Fan-out-and-synthesize é o pattern mínimo; dynamic harness generation é state-of-the-art separado (Anthropic 06/2026).

Expected gain (DEFERRED until H1-H5 measured — honest, per quality-gate):

  • Wall-clock ~2.5× (3×20s) to ~4× (5×30s): projection on recall lab corpus (docs/POC-orchestrator.md 3 scenarios), not measurement. Without an orchestrator-specific dataset, no numeric gain is claimed as verified; H1 will be measured in Sprint 2 with time opencode run before/after.
  • Discipline ~30% error-prone → 0% by construction: #47105 reports the model "often forgets to wait, or polls, or duplicates work"; #44748 reports ~34% no-op loops from missing bounds. Claim: harness enforces background: true always, concurrency: 10 always, task: deny on workers always — so the counted failure class should drop to 0%. Verification: H5 in lab (flag OFF 0% spurious, flag ON 100% intended).
  • Cost capped: $0.50 per dispatch via totalSessionCost() (0-safe when the provider returns cost=0, D5+D6 as backstop) vs linear unbounded billing (#45417 as baseline). H4 deferred — provider must return cost; local models cost=0.

Proposed shape (behind an opt-in experimental flag, no default behavior change — same posture as OPENCODE_EXPERIMENTAL_TRANSCRIPT_RECALL in the recall work):

  • Agent orchestrator (mode: all, steps: 7, read-only by default — no edit/write/patch/bash, allow read/grep/glob/webfetch/websearch) with a prompt that encodes the discipline above (9 bullets + 5-step protocol + cost & output format). all is needed so the harness itself is not blocked by the subagent_depth guard when delegating (see #47107's opposite choice: concurrency: "unbounded" without a harness discipline — this agent is the disciplined alternative).
  • Agent worker (read-only, task: deny, todowrite: deny) — workers that cannot spawn further subagents (enforced by deriveSubagentSessionPermission at depth: 1; Truncate.GLOB allow preserved).
  • An opt-in flag (same enabledByExperimental pattern as OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS, e.g. OPENCODE_EXPERIMENTAL_ORCHESTRATOR, experimental || enabled semantics) + a keyword trigger (orchestrate|orchestrator|parallel|fans?out|delegate at step===1 after title and after the overflow guard, immediately before agents.get(lastUser.agent), \b boundaries, contíguo fans?out — not fan out with space nor fan-out with hyphen) — no behavior change when the flag is off.

Why a harness matters vs a tool: a tool is an invoker (the model decides when/how to call it); the harness is where discipline is enforcedbackground: true always, concurrency: 10 always, task: deny on workers always, steps: 7 (2–7 workers + synthesize), cost cap observability via totalSessionCost(). The recall landed the same way: substrate first, gated, lab-verified, no breaking change.

Out of scope: dynamic workflow generation (Claude writes its own harness), LangGraph-style state machines, Temporal-style multi-cluster durability, and TUI cards — all already exist or are separately tracked. The open task-parallel tool PR #47107 is complementary (not a substitute): this harness delegates to it internally if/when both land, rather than re-implementing the dispatch logic.

Related prior art: #37421 (MCP hot reload), #34492 (unified watch service), #8751 (agent/skill hot reload) — different domain, same pattern of unifying scattered issues under one design discussion.

Happy to work on this behind a design review if maintainers agree with the scope; also glad to fold it into #47105 / #47107 as review suggestions if you prefer one umbrella effort (e.g. add a cap on concurrency for the tool, an opt-in flag for the harness, and a dedicated read-only worker) rather than a separate agent.


Refs: #47105 (primary ask), #47107 (complementary tool PR, non-draft), #37548, #43594, #45417, #44748, #30308.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.