# Pick the best of several options

Use this when a step produced a handful of candidates (draft replies, plan
options, name ideas, competing approaches) and you want the workflow to pick
the strongest one _reliably_, rather than trust a single model's one-shot
ranking. A `round_robin` judges every pair head-to-head against your
criterion, tallies a crosstable and standings, and routes `decided` (a clear
winner) or `tie` (the top options are genuinely level). Pairwise comparison
is easier for a model than ranking a whole list, and the full scorecard
shows exactly _why_ one won.

## Best-of-N: draft, judge, act

The signature use is best-of-N: have the node `generate` several candidates in one
call, judge them, then act on the winner.

```yaml
name: best-reply
version: 1
description: |
  Draft four replies to a tricky customer email, judge them head-to-head, and
  present the strongest one ready to send.
initial: draft
states:
  draft:
    type: round_robin
    label: Draft and rank four replies
    model: claude-haiku-4-5
    criterion: |
      A long-time customer is upset and asking for a refund just outside our
      30-day window. Which reply best keeps them as a customer — genuinely
      empathetic, honest about the policy, and offering a fair next step?
    generate:
      prompt: |
        A loyal customer forgot to cancel and was charged for another year three
        days after renewal — just past our 30-day refund window. Draft four
        genuinely different complete replies a teammate could send (vary the tone
        and the concrete offer). Each ready to send as-is.
      count: 4
    swapMode: contested
    on:
      decided: finalize
      tie: tie-end

  finalize:
    type: ai_agent
    label: Present the winning reply
    model: claude-haiku-4-5
    prompt: |
      A round-robin ranked four candidate replies.

      Replies: {{ draft.ideas }}
      Standings: {{ draft.standings }}
      Winning index: {{ draft.winnerIdx }}

      Output the winning reply exactly as the team should send it, then add one
      line — "Why this one:" — on what made it beat the runner-up.
    on:
      done: done-end

  done-end:
    type: end
    label: Reply ready
    outcome: success
    message: The strongest reply was selected and prepared to send.

  tie-end:
    type: end
    label: Too close to call
    outcome: failure
    message: Two replies were judged equally strong — a teammate should pick between them.
```

## Running it

```bash
roscoe run best-reply
```

The web UI shows the **scorecard**: a winner banner, a crosstable (who beat whom,
by how much), standings, and "highlights" (undefeated, closest call, biggest
upset). When the top two finish level on points, the winner is settled on a
tie-break, and the "Won by tie-break" callout names _which_ criterion broke it
(winning margin, head-to-head, or strength of opposition) with the two leaders'
values, so the decision is auditable. A genuine `tie` means the options were
inseparable; route it to a human (see Variations).

`round_robin` runs key-free on your Claude subscription when driven from Claude
Code / Cowork (the pairwise judging hands back to the host). Standalone or from
Claude desktop chat it runs server-side and needs `ANTHROPIC_API_KEY` or the
`claude` CLI.

## Where the candidates come from

Exactly one source per node:

| Source           | Use it when…                                                                                                                                                                                 |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `generate`       | the node should brainstorm the options itself (best-of-N, as above).                                                                                                                         |
| `candidatesFrom` | an upstream node produced them (an `ai_agent` that brainstormed, a `json` input, a subworkflow). Any text output works: the node auto-extracts a clean list, no special formatting required. |
| `candidates`     | the options are fixed at authoring time.                                                                                                                                                     |

## Tuning swapMode (position bias)

Models can be swayed by which option is shown first. `swapMode` trades cost
for resistance to that bias:

| Mode        | Behaviour                                                                         |
| ----------- | --------------------------------------------------------------------------------- |
| `off`       | judge each pair once (`C(N,2)` calls). Cheapest.                                  |
| `contested` | judge once, re-judge only the close/cyclic pairs in reverse. Recommended default. |
| `all`       | judge every pair in both orders (`N·(N-1)` calls). Least bias, ~2× the cost.      |

When a pair is judged both ways and the two orders disagree, it's scored a
draw: neither position can be trusted.

## When to reach for it

This covers best-of-N drafts (generate several replies, summaries, or specs
and ship the strongest), picking an approach (brainstorm options for a
goal, judge them, then expand the winner into a plan), and choosing copy
(rank candidate headlines, subject lines, or names).

For a yes/no gate or a single fact check, use `ai_judge`. For "do N agents agree on
one question?", use `consensus`. `round_robin` is for "which of these several is
best?".

## Variations

To draw candidates from an upstream brainstorm, drop `generate` and add an
`ai_agent` that brainstorms options first, then
`candidatesFrom: '{{ ideate.response }}'`. A single input then drives both
the options and the judging, so it never goes stale. To route `tie` to a
human instead of a failure end, send `tie` to a `human` node so a person
breaks the genuinely close calls while AI handles the clear ones. To act on
the winner, have the downstream `ai_agent` do real work with
`{{ draft.winnerIdx }}` and `{{ draft.ideas }}`: write it up, ship it, draft
a plan.

## See also

- `/docs/nodes/round-robin` — full reference for the node type
- `/docs/nodes/ai-judge` — single decision, cheaper
- `/docs/nodes/consensus` — N agents vote on one question
