2026-08-28 · Updated 2026-08-28 · 10 min read

One multi-harness workflow for Claude Code, Codex, and Gemini

Run Claude Code, Codex, and Gemini as one canonical workflow: per-service dispatch surfaces and shorthand families, harness-agnostic steps with typed handoffs, and provider failure boundaries for quota, credentials, and evidence.

By Juno AI INC · multi-harness · claude · codex · gemini · yylo

Plenty of teams hold credentials for three coding-agent vendors at once — an Anthropic plan, an OpenAI plan, a Google key — and each vendor's CLI works fine on its own. What none of them provides is the workflow between them: which step runs on which provider, what crosses the seam when one step hands to the next, and what the record looks like afterwards when three different agents touched one change. Bolt three vendor playbooks together and you get three documents that drift apart, three evidence formats, and no single place that can say who concluded what.

This guide is the one canonical workflow for that combination in YYLO. The vendors keep their models, their shorthand families, and their quota clocks; YYLO keeps the steps, the handoffs, the bounds, and the record. One sentence carries the design: each provider answers for its own invocation; the workflow answers for everything between invocations.

Why one route owns all three providers

The tempting alternative is a "Claude Code with YYLO" recipe, a "Codex with YYLO" recipe, and a "Gemini with YYLO" recipe — three pages that each re-explain dispatch, iteration bounds, and evidence capture, then quietly disagree as the product moves. Provider and model variants merge into this single owner instead, because the contracts underneath do not differ per vendor. What actually differs is small and enumerable: the service name you dispatch, the default model behind it, the shorthand family that service accepts, and where its quota and credentials live. That is what this page covers.

The neighboring questions already have canonical owners, and this guide links to them rather than repeating them:

  • The generic multi-agent contracts — step identity, response/session/artifact handoff channels, and landing through exactly one admission gate — belong to use multiple coding agents in one workflow. Read it first if the step contract itself is new to you.
  • Switching the harness under in-flight work — including its dated support matrix and the session commands that keep both sides addressable — belongs to how harness switching works.
  • Deep compositions with other harnessesCursor, Kiro, OpenCode, and Pi — each own their own pages, gated where product truth still needs external verification.

What remains here is the named three-provider combination: the dispatch surfaces claude, codex, and gemini actually expose, and the failure boundaries that only matter once three vendors share one workflow.

The three services, one dispatch surface

YYLO's global -s/--subagent flag names five services — claude, codex, gemini, pi, and cursor — with the README's summary: "Switch between Claude, Codex, Gemini, Pi, or Cursor with one flag" (YYLO documentation). Each service name addresses that vendor's coding-agent CLI; the control plane around it — task truth, worktrees, bounds, evidence — is the same for all of them. Three surfaces matter in practice:

  • Quick shortcuts. yylo claude 'your task', yylo codex 'your task', and yylo gemini 'your task' are documented one-liners in the README's quick-subagent-shortcuts block, taking the prompt as a positional argument with the service preset. The README's own example of the form is yylo claude -i 3 -p "Summarize git status: !'git status --short'" — flags compose onto the shortcut exactly as they compose onto the long form.
  • Per-service defaults and shorthand families. The documented service table gives each service its own default model and its own shorthands: claude defaults to claude-sonnet-4-6 with :haiku, :sonnet, and :opus; codex defaults to gpt-5.3-codex with :codex, :codex-mini, :gpt-5, and :mini; gemini defaults to gemini-2.5-pro with :pro, :flash, :pro-3, and :flash-3. The families are service-scoped: :opus names a Claude model, :pro a Gemini model, and the README notes explicitly that these aliases are subagent-specific — Pi's :mini and the Codex service's :mini are different models. When a step omits -m, it runs the service's default; when it sets one, the shorthand resolves inside that service's family.
  • Pinned defaults per service. yylo claude set-default-model :opus and yylo codex set-default-model :gpt-5 write per-subagent defaults into .juno_task/config.json, so a team can pin its model policy once instead of repeating -m in every step. Resolution is fixed and documented: a CLI --model flag takes precedence, a configured per-service default comes next, and the built-in default answers last.
sh
# three dispatch surfaces, one control plane
yylo claude 'triage the failing payment test and name the smallest fix' -i 3
yylo codex 'same triage, independent second opinion' -i 3
yylo gemini 'write a two-sentence brief of both reports' -i 3

# pin the policy once, per service
yylo claude set-default-model :opus
yylo codex set-default-model :gpt-5

The first bounded run in this style is exactly the one the bounded-loop guide teaches for a single agent: one outcome whose success you can check by hand, validation scoped to the code a step touched, and -i/--max-iterations capping every invocation. Multi-provider does not mean unbounded.

One workflow, three providers

Now the combination itself. In Workflow Runner, a step that runs a yylo-prefixed command is a detected agent step: the runner records whether it succeeded, everything it printed, and its final answer — and a step that finishes with exit code zero but no final response is treated as failed, whatever the service. That contract is what lets three vendors share one file. Each step below names its provider through the shortcut form, two of them lean on their service defaults, and the handoff between steps is typed data:

yaml
schema_version: 1
workflow_id: three_provider_triage
vars:
  target: "the failing payment-path integration test"
steps:
  - id: claude_triage
    command:
      - yylo
      - claude
      - -i
      - "3"
      - -p
      - 'Triage {{ target }} without changing files. Finish with: FINDINGS: <three sentences of root-cause evidence>'
  - id: codex_challenge
    command:
      - yylo
      - codex
      - -p
      - 'A Claude agent triaged {{ target }} and reported: {{ steps.claude_triage.response }} Challenge the report: name one cause it missed. Finish with: VERDICT: <one sentence>'
  - id: gemini_brief
    command:
      - yylo
      - gemini
      - -m
      - :flash
      - -p
      - 'Findings: {{ steps.claude_triage.response }} Verdict: {{ steps.codex_challenge.response }} Write a two-sentence brief suitable for the task record.'

The {{ steps.<id>.response }} renders the earlier provider's final answer into the later provider's prompt — Claude's conclusions become Codex's input, and both become Gemini's. That is the entire inter-vendor bridge: no shared memory, no vendor-to-vendor translation, just recorded text handed forward on purpose. The full contract behind it — sessions for continuation, artifacts for volume, and the discipline that however many agents contribute, exactly one clean committed tip lands through preflight, finish, and the serialized merge queue — is the multi-agent guide's to teach. Check the file before any provider launches, exactly as for a single-provider chain:

sh
./.juno_task/scripts/workflow_runner.sh lint --workflow .juno_task/workflows/three_provider_triage.yaml
./.juno_task/scripts/workflow_runner.sh --workflow .juno_task/workflows/three_provider_triage.yaml --dry-run --print-output none

Hold the early chains to read-only work — triage, challenge, brief — until the handoffs have earned trust. When the queue, not the chain, is the unit you want, run_until_completion.sh loops a board to completion and forwards its arguments straight to yylo, so the service is a flag there too: ./.juno_task/scripts/run_until_completion.sh -s codex -i 5 --stale-threshold 3 (runner reference).

Provider failure boundaries

One vendor failing is the scenario multi-provider workflows exist for, and the boundaries are worth knowing precisely.

Quota stops at the invocation, not at the workflow. --on-hourly-limit decides what one dispatch does when its vendor's hourly quota is hit: wait sleeps and retries automatically, raise exits immediately and is the default. Either way the boundary is per invocation and per vendor — a Claude quota wall never pauses a Codex step, because nothing is shared between them except the workflow's own record.

The next dispatch is where you spend the second vendor. When one provider is exhausted, unavailable, or simply wrong for the next sub-job, the remedy is not a migration: it is the service flag on the next dispatch. The README frames the single-shot version as "Stuck on a bug? Try different models" — the same one-flag move inside a workflow. A failed generic step records its failure in the manifest and the run continues, unless the step itself declares fail_workflow: true and halts everything after it — so "fall back to the other vendor on the next step" is a policy you write deliberately, not a silent retry.

Credentials are per vendor and stay in the environment. YYLO reads GEMINI_API_KEY for the Gemini service and honors CODEX_HIDE_STREAM_TYPES for Codex output; YYLO_SUBAGENT and YYLO_MODEL set the service and model as environment defaults, one notch below the CLI flags in the documented configuration hierarchy. Keys belong in the environment, never in prompts or task bodies — three vendors in one workflow means three credentials to keep out of three agents' context, which is one reason the prompt and shell-context safety rules come before any of this.

Evidence stays uniform across vendors. Every run — any service — appends an execution-history entry to session_history.json with its subagent and model recorded, and yylo session list plus yylo session info <id> address the sessions afterwards. A month later, the question "which vendor produced this conclusion?" is answered by the recorded subagent field, not by memory. Lifecycle hooks run with any backend, so an END_ITERATION test hook fires identically whether the iteration ran Claude, Codex, or Gemini.

The deeper reason these boundaries hold is architectural: the harness is picked per run, and the control plane above it — not any vendor's session store — is where task truth and evidence live. That layering is what the harness boundary guide lays out end to end, and the property the portability audit turns into exit drills.

Standing guidance every provider already shares

Each dispatch starts inside the same project, so the standing context is shared even though the agents are not. YYLO auto-provisions skills on every CLI run: the Claude service reads .claude/skills/ and the Codex service reads .agents/skills/, each receiving kanban-workflow, ralph-loop, plan-kanban-tasks, and understand-project; the README documents no skills directory for Gemini, so for that service the shared surfaces are the project files and the task record. One repository's guidance steers the services that document a skills surface, and the task record — intent, response, commit — stays the single episodic truth beside it for all three, as task-truth and auditable-workflow practice.

The separation that falls out is the one this guide promised. Claude, Codex, and Gemini each answer for their invocation: prompt in, response out, quota clock running. YYLO answers for everything between: which step runs on which service, what crosses the seam, what happens when one vendor's limit hits, and what the record proves afterwards. Three vendors, one workflow, one admission.

Run it on one real task

The cheapest honest test is one bounded task, triaged three ways. Install YYLO, put the outcome on the board as one task, and run the chain from inside its worktree:

sh
npm install -g @yylo/cli
yylo init --task "Describe one verifiable outcome" --subagent claude
yy task start TASK_ID
# save the three_provider_triage.yaml chain from above, lint it, dry-run it,
# then run it without --dry-run
yy task preflight TASK_ID
yy task finish TASK_ID

Aim the first run at a read-only question with a checkable answer, and keep -i small on every step. If the chain stalls, the run directory holds each step's stdout, stderr, and response — auditable agent workflows maps the recovery. Every product statement above was verified against the committed README and documentation on 2026-08-28; service defaults, shorthand families, and quota behavior move with the vendors, so re-verify at the next evidence date. Behind this site sit three independently packaged products: YYLO and YYLO Benchmark install from npm, YYLO Ledger from PyPI, and removing any one leaves the others working — the same property this workflow gives your agents: joined for the work, separable when the work ends.