consensus node

N-of-K agent voting for higher-stakes AI gates.

Runs the same prompt through N parallel Claude agents and routes to approved only if at least quorum of them agree. consensus is ai_judge with redundancy: the right tool for gates where one model's wrong answer is too expensive to accept.

Purpose#

Use consensus for high-stakes routing decisions: release approvals, go/no-go gates, content moderation, irreversible actions. The cost is linear in agentCount, but the reduction in tail-risk wrong answers is substantial, and the per-agent reasoning cards in the UI make split decisions inspectable.

Decision modes#

The validator.kind selects what the agents decide. The rest of this page documents gate mode (the original behaviour); the other two modes share the same fan-out and per-agent diversity but route differently:

validator.kind What it does Transitions
boolean / confidence Gate: each agent approves/rejects; route on a quorum. approved / rejected
answer (+ optional choices) Pick the best answer: each agent answers; the plurality wins (winningValue). decided / undecided
most_consistent Pick the most consistent response: each agent writes a free-form response, then an aggregation step keeps the one most consistent with the rest (Universal Self-Consistency, USC). decided / undecided

In MCP mode on a subagent-capable host (Claude Code / Cowork), all three run their fan-out on the user's subscription via handed-back subagents; most_consistent does this in two rounds (generate, then aggregate).

YAML schema#

states:
  review:
    type: consensus # discriminator
    label: Approve release? # required
    model: claude-haiku-4-5 # required, must be in roscoe.yaml allow-list
    prompt: | # required, auto-augmented with JSON envelope spec
      Should we ship this release? Be skeptical.
    agentCount: 3 # optional, 2-7, default 3
    quorum: 2 # optional, >=1, must be <= agentCount, default 2
    validator: # required — boolean OR confidence (no enum)
      kind: boolean
    maxRetries: 1 # optional, 0-3
    on: # exactly two keys: approved | rejected
      approved: ship
      rejected: revise

Configuration#

Field Type Required Default Meaning
type 'consensus' yes Discriminator.
label string yes Human-readable name for the step.
model string yes Model id; checked against roscoe.yaml at pre-flight.
prompt string yes Question for each agent. Supports {{node.key}} interpolation.
agentCount integer (2-7) no 3 How many parallel agents vote. Hard cap of 7.
quorum integer (>= 1) no 2 Approvals needed for approved. Must be <= agentCount.
validator boolean | confidence yes Per-agent approval rule. enum is intentionally not supported.
maxRetries integer (0-3) no 1 Per-agent retries on parse failure. Capped at 3.
timeoutSeconds integer (1-2147483) no min(120s, defaults.timeoutMs) Per-agent deadline. When set it is authoritative (overrides both the 120s built-in default and the global defaults.timeoutMs). See below.
maxIterations integer (1-50) no Per-run iteration cap when this node sits in a feedback loop. See the closed-loop feedback recipe.
detectStall boolean no false Fail the run early if the consensus outcome is identical three times in a row.
on transition map yes Must contain approved and rejected.

Per-agent approval rule#

Validator An agent approves when…
boolean its result is exactly true.
confidence its score >= validator.threshold.

enum is rejected at schema validation time: the per-agent approval rule isn't well-defined for arbitrary route names.

Per-agent deadline#

Each agent has a hard 120-second deadline by default (capped further by your roscoe.yaml defaults.timeoutMs if smaller). Set timeoutSeconds on the node to override this with an authoritative per-agent deadline: it takes precedence over both the 120s default and the global defaults.timeoutMs, so it's the way to give agents more than 120s (the global cap alone can't, since the deadline is the smaller of the two). Agents that time out or error count as no-vote. They neither approve nor reject. With agentCount: 5, quorum: 3 and 2 timeouts, the run still completes if 3 of the remaining 3 approve.

Outputs#

consensus writes a ConsensusOutcome object into variables.<nodeId>:

Key Type Notes
outcome 'approved' | 'rejected' | 'cancelled' Mirrors the chosen transition. 'cancelled' if the run was cancelled before the node settled.
approvals number Count of votes with verdict === 'approve'.
agentCount number Echo of the configured count.
quorum number Echo of the configured quorum.
votes array of vote objects One per agent; see below.

Each vote contains agentIndex, status (completed / failed / timed_out / cancelled), verdict (approve / reject / no_vote), result, reasoning, attempts, and durationMs. cancelled appears on an agent whose call was aborted because the run was cancelled mid-vote.

Transitions#

Transition When
approved approvals >= quorum.
rejected otherwise (including all-error scenarios).

Both keys must be present in on. There are no other emitted transitions.

Execution mode notes#

In a server or web UI run, agents fan out in parallel against the configured LLM backend.

In an MCP run from Claude Code or Cowork, the fan-out is handed back to the host: it runs the agentCount reviewers as independent subagents on your Claude subscription and returns their verdicts via advance_consensus. Roscoe tallies the quorum itself, with no API key required. See Running on your Claude subscription.

For gate mode, that handback is a single round trip: Roscoe pauses at the node and hands the prompt spec to Claude Code, which spawns the reviewers, collects their verdicts, and reports them back in one advance_consensus call that Roscoe tallies against the quorum.

sequenceDiagram
    participant CC as Claude Code
    participant GM as Roscoe MCP server
    participant SA as N sub-agents
    CC->>GM: start_workflow
    GM-->>CC: pause at consensus node, pendingStep (prompt, agentCount, quorum)
    CC->>SA: spawn agentCount independent reviewers
    SA-->>CC: each agent returns a verdict
    CC->>GM: advance_consensus(results)
    GM->>GM: tally approvals against quorum
    GM-->>CC: approved or rejected, run resumes

In an MCP run from Claude desktop chat, and in a standalone run, consensus runs server-side against the configured LLM backend instead (a chat session can't spawn subagents). That needs either ANTHROPIC_API_KEY set or the claude CLI installed; if neither is available, the node fails before any agent runs.

Worked example#

name: consensus
version: 1
description: Multi-agent gate where 3 reviewers must reach a 2-of-3 quorum.
initial: ask
states:
  ask:
    type: consensus
    label: Is this idea worth shipping?
    model: claude-haiku-4-5
    prompt: |
      Evaluate this product proposal:

        "Add a feature where users can attach short voice memos (up to 30s) to
         any item in their task list, transcribed automatically and searchable
         alongside text notes."

      Approve only if all four hold:
        1. it solves a real, common user problem (not a niche edge case)
        2. it can ship in under 4 weeks for a 3-person team
        3. it doesn't add meaningful privacy/compliance burden
        4. it's not better delivered as an integration with an existing tool

      Be skeptical — reject if any single criterion is shaky.
    agentCount: 3
    quorum: 2
    validator:
      kind: boolean
    maxRetries: 1
    on:
      approved: yes-end
      rejected: no-end

  yes-end:
    type: end
    label: Approved
    outcome: success
    message: Reviewer quorum agreed.

  no-end:
    type: end
    label: Rejected
    outcome: failure
    message: Reviewer quorum did not agree.

Common pitfalls#

  • quorum > agentCount. Rejected at schema validation time: the gate could never approve. Pick a quorum strictly between 1 and agentCount.
  • Using enum validator. Unsupported for consensus. Use boolean or confidence with a threshold.
  • Setting quorum: 1. Equivalent to "any one agent approves". Almost always weaker than a single ai_judge call, since you're paying N× cost for a looser bar.
  • Long prompts × agentCount: 7. You're sending the same prompt seven times per run. For very large prompts, this gets expensive quickly. Start with the default of 3.
  • Missing API key for server-side consensus. When consensus runs server-side (standalone, or driven from Claude desktop chat), it needs ANTHROPIC_API_KEY set or the claude CLI on PATH, or the node fails before voting. Driven from Claude Code / Cowork the fan-out runs on your subscription and needs neither.
  • Treating no-vote as rejection. Errored or timed-out agents do not count as "reject": they don't approve. With agentCount: 5, quorum: 3, three timeouts means the gate fails because only two agents can approve, but the failure is a quorum miss, not a rejection.

Where to next#

View this page as Markdown

Predictable workflows from unpredictable AI