2026-08-27 · Updated 2026-08-27 · 8 min read

Use multiple coding agents in one workflow

Run several coding agents as one validated workflow: put each agent behind a harness-agnostic step contract, hand off typed responses between steps, and land the result through a single admission gate.

By Juno AI INC · multi-agent · workflow-runner · yylo

Using several coding agents on one change fails in a specific way: the agents do not share a plan, so their work overlaps or drifts, and nobody can say afterwards which agent produced which decision. The fix is not to give the agents a group chat. It is to put every agent behind the same step contract inside one workflow, pass typed outputs between the steps, and let exactly one admission gate decide what lands. That is the whole discipline: harness-agnostic contracts, response handoff, single admission.

This guide shows the working shape in YYLO's Workflow Runner — the step contract every documented agent can sign, the three handoff channels that connect agents without shared memory, a two-agent chain you can run today, and the merge discipline that keeps any number of contributing agents from becoming any number of merge decisions.

What agents talking to each other has to mean

The usual mental picture of multiple AI agents is agents talking to each other — a continuous conversation where work moves organically from one agent to the next. For work that has to be validated, that picture is the problem, not the goal. A free-form agent-to-agent exchange has no decision boundary: you cannot reconstruct which agent concluded what, which prompt produced which change, or where a wrong turn entered the chain.

The auditable version of agents talking to each other is handoff with a receipt. One agent finishes and emits a typed output — a final response, a session id, a file on disk. The next agent starts with exactly that output in its prompt. The workflow records both sides. Each agent keeps a clean boundary of responsibility, and the conversation between them is data you can read, diff, and replay instead of scrollback you can only trust.

Make every agent sign the same step contract

In YYLO, the unit of multi-agent composition is a workflow step: an id, a command, and typed outputs. A step that invokes yy pi dispatches a prompt to a configured coding-agent service; when the agent finishes, the step owns its status, its stdout and stderr, its session id, and its final response. The contract has teeth: a detected agent command that exits successfully with an empty response is marked failed, because a step that produced no answer did not do its job.

The contract is deliberately harness-agnostic. The -s/--subagent flag documents the services claude, codex, gemini, pi, and cursor, and -m selects the model behind a service, resolved in a fixed order: the CLI flag first, then the configured per-service default, then the built-in default. The handoff surface between steps never includes the vendor: steps exchange ids, responses, exit codes, and sessions, never vendor-specific APIs — which is exactly why swapping the harness under a step stays cheap instead of becoming a rewrite. The harness boundary guide owns why this layering works, and the portability audit owns what each layer costs to swap.

Failure semantics are part of the contract, in two tiers. Generic steps record their failure in the manifest without failing the whole run, unless you set fail_workflow: true to stop the sequence at that boundary. Typed managed_agent steps fail closed by design: they require a declared policy and a worktree stage boundary with admitted paths, hash content before and after dispatch, and fail the step before its successor when an agent mutates anything outside its declared paths. Start with generic steps; graduate the steps that touch product code.

Hand off responses, sessions, and files

Three channels connect agents without giving them shared memory. Each one is typed, template-addressable, and recorded in the run directory:

  • Response handoff. {{ steps.<id>.response }} renders an earlier agent's final answer into a later step's prompt. This is the primary channel: the second agent's prompt literally contains the first agent's conclusion, and the rendered command is persisted, so the handoff itself is replayable evidence.
  • Session handoff. {{ steps.<id>.session_id }} addresses the recorded conversation behind a step. A later step can continue that thread directly with yy pi --resume "<session id>", which is one agent continuing its own context rather than a new agent reading a transcript. After the run, top-level continue_from_step hands a chosen agent step to yy cc so you can keep working with the agent that produced the result.
  • Artifact handoff. Steps read and write files in the run directory — per-step stdout, stderr, and response files, plus your own aggregation artifacts. Files are how more than two agents coordinate without forcing every intermediate conclusion through a prompt. The full artifact map, and how to recover an interrupted run without inventing success, has its own guide.

Use responses for conclusions, sessions for continuation, and artifacts for volume. When every handoff is recorded data instead of hope, a failed chain tells you exactly which agent stopped delivering — and a successful chain can prove where each conclusion came from.

A two-agent chain you can run today

Workflow Runner ships a built-in example for exactly this shape. Generate it, lint it, and dry-run it before anything executes:

sh
./.juno_task/scripts/workflow_runner.sh --init-example agent-chain .juno_task/workflows/agent_chain.yaml
./.juno_task/scripts/workflow_runner.sh lint --workflow .juno_task/workflows/agent_chain.yaml
./.juno_task/scripts/workflow_runner.sh --workflow .juno_task/workflows/agent_chain.yaml --dry-run --print-output none

The same skeleton generalizes to any investigate-then-review job. Two agents, one contract, one handoff:

yaml
schema_version: 1
workflow_id: two_agent_review
vars:
  target: "the flaky integration test"
steps:
  - id: investigate
    command:
      - yy
      - pi
      - |
        Investigate {{ target }} in this repository without changing files.
        Finish with: FINDINGS: <three sentences of root-cause evidence>
  - id: review
    command:
      - yy
      - pi
      - |
        A first agent investigated {{ target }} and reported:
        {{ steps.investigate.response }}
        Challenge the report: name one cause it missed. Finish with: VERDICT: <one sentence>
summary:
  command:
    - yy
    - pi
    - |
      Summarize a two-agent investigation of {{ target }}.
      Findings: {{ steps.investigate.response }}
      Verdict: {{ steps.review.response }}

Run it without --dry-run to execute for real. Any step can add -s <service> and -m <model> to dispatch a different documented agent, and the chain keeps its shape — that is the point of the contract. Two rules keep early chains honest: keep the first chains read-only (investigate, review, summarize) until the handoffs are trusted, and lint before every unattended run so template mistakes fail before expensive agents launch.

Fan out, then aggregate

Chains are for steps that need each other. When the items are independent, fan out instead — and the question of which runner owns a job, one ordered chain or a bounded pool of independent items, is a coupling question that the runner-choice guide decides in one pass.

The two runners compose. Parallel Runner's raw command mode fans out complete commands — including several workflow_runner.sh --workflow ... invocations — across a bounded worker pool, while each Workflow Runner keeps its ordered steps and per-run artifacts:

sh
./.juno_task/scripts/parallel_runner.sh --init-commands-example .juno_task/commands/workflows.yaml
./.juno_task/scripts/parallel_runner.sh --lint-commands-file .juno_task/commands/workflows.yaml
./.juno_task/scripts/parallel_runner.sh --commands-file .juno_task/commands/workflows.yaml --parallel 3

Bounded fan-out has its own safety rules — pool caps below every real limit, readiness as the only admission rule, one boundary per worker — and safe parallel execution owns them. The multi-agent rule that matters on top of them: fan-out produces candidates and evidence, never admissions. That is the last discipline.

However many agents, one admission

Multi-agent workflows break at the end if every contributing agent can also land its own change. YYLO's merge discipline is the opposite: any number of agents may investigate, draft, and cross-check inside a task, but the product target accepts exactly one clean committed tip, through one serialized gate. The mechanics live in the task lifecycle, not in the agents:

  • One intent, one task. A task owns the change's scope and its task record; agents contribute inside that boundary instead of each opening its own front door to the repository.
  • One worktree per change. Every product change implements in a dedicated yy task worktree, so concurrent agents never share a checkout, and task start freezes the target base the change will be admitted against.
  • One admission. Read-only yy task preflight reports closure defects while repair is still cheap; yy task finish queues a clean committed tip; the merge queue alone owns moved-target composition, conflict handling, and affected validation, serializing mutation per target.
  • Review is not the workers' job. Implementation and repair agents never launch lifecycle-semantic reviewers; review ownership stays with the merge queue, so the agents that produced a candidate never grade it.

This is what makes fan-out safe to scale. Two agents or twenty, the worst case stays bounded: a rejected candidate strands only itself, the queue stops after its repair budget instead of looping forever, and the repository's history records one decision per intent with its evidence attached.

Start with two agents and one contract

The smallest multi-agent workflow is also the cheapest one to validate: install YYLO from npm, initialize a project, and run the shipped two-step chain before writing your own.

sh
npm install -g @yylo/cli
yylo init --task "Describe one verifiable outcome" --subagent claude
./.juno_task/scripts/workflow_runner.sh --init-example agent-chain .juno_task/workflows/agent_chain.yaml
./.juno_task/scripts/workflow_runner.sh lint --workflow .juno_task/workflows/agent_chain.yaml
./.juno_task/scripts/workflow_runner.sh --workflow .juno_task/workflows/agent_chain.yaml --dry-run --print-output none

When the dry-run looks right, drop the flag and watch the handoff happen. The operational reference — including the full Workflow Runner and Parallel Runner script pages — lives in the YYLO documentation. The three products are separately installable and separately removable: YYLO and YYLO Benchmark from npm, YYLO Ledger from PyPI, each with its own repository.

Keep the first chains bounded the way the bounded-loop guide teaches single agents: one verifiable outcome, focused checks on the touched surface, evidence checked every cycle. Then add a second agent where a second opinion actually pays — review after investigation, verdict after draft — and let one gate decide what lands.