Skip to content

ADR 0017 Fork Join and Parallel Execution

Pavel Ravvich edited this page Oct 11, 2026 · 3 revisions

ADR-0017 — Fork-Join and Parallel Execution

  • Status: Accepted (the DSL as shipped is below; the first proposal's fork({...}) step was replaced by nextParallel)
  • Issue: #155

Context

AGBoss workflows were linear. Real-world tasks need agents running in parallel and their results synchronized before the run goes on — without implicit mutable state shared by the branches.

Decision

  1. DSL: fan-out and fan-in are flow steps — from(A).nextParallel(B, C) starts the branches, from(B, C).join(D) waits for all of them; batchParallel(Target, Strategy, { concurrencyLimit, batchSize }) maps a target over items known only at run time; joinQuorum(Strategy, { min, max?, timeoutSeconds? }) lets the run go on once enough branches voted. A router may start branches too (a route to parallel(...), optional(...)).
[
  from(PlannerAgent).nextParallel(ResearchAgent, RiskAgent), // both branches run
  from(ResearchAgent, RiskAgent).join(SynthesisAgent), // waits for both
],
[
  from(PlannerAgent).batchParallel(ResearchAgent, TopicsOfPlan, { concurrencyLimit: 2, batchSize: 1 }),
  from(ResearchAgent).next(SynthesisAgent),
],
  1. Parallel execution with LangGraph's Send API: a fork compiles into conditional edges that return one Send per branch; the branches run concurrently and the join is a barrier node.
  2. Results by name, not shared state: each branch's output is kept under its node's name (ForkOutput: { name, data }, internal types of src/graph/fork-join.ts); the joined target reads the branches' contributions, and a join node may implement onJoin(outputs) to turn them into contributions, payload or a state update. FinOps data (tokens, money) aggregate through the state's reducers and are not part of a branch's output.
  3. State: a payload channel for custom key–value data next to the contributions.

Consequences

  • Easier: parallel routines are one flow step; the assembly rules check fan-outs and joins like every other step (Workflow, Advanced Routing).
  • Harder: the graph compiler manages Send objects, barriers, batches and quorums.
  • Watch: contributions are multimodal (Contribution.content may hold parts), so joined branches can pass images as well as text.

Clone this wiki locally