Back to skills
SKILL.md
Choosing Swarm Patterns
ASecurityUse when coordinating multiple AI agents with Agent Relay's workflow engine and need to pick the right orchestration pattern - covers the 10 core patterns (fan-out, pipeline, hub-spoke, consensus, mesh, handoff, cascade, dag, debate, hierarchical) plus 14 specialized ones, with decision framework and accurate SDK/YAML examples.
- 862 stars
- 0 votes
- 0 copies
- 4 views
- Added September 3, 2026
Works with
Security analysis
100/100npx -y skills add AgentWorkforce/relay --skill choosing-swarm-patterns --agent claude-codeAre you the author of Choosing Swarm Patterns?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/agentworkforce-choosing-swarm-patterns-relay)---
name: choosing-swarm-patterns
description: Use when coordinating multiple AI agents with Agent Relay's workflow engine and need to pick the right orchestration pattern - covers the 10 core patterns (fan-out, pipeline, hub-spoke, consensus, mesh, handoff, cascade, dag, debate, hierarchical) plus 14 specialized ones, with decision framework and accurate SDK/YAML examples.
---
### Overview
The Agent Relay SDK (`@agent-relay/sdk`) supports 24 swarm patterns via a single `swarm.pattern` field. Patterns are configured declaratively in YAML — there are no standalone `fanOut(...)` / `hubAndSpoke(...)` helpers. Pick the simplest pattern that solves the problem; add complexity only when the system proves it's insufficient.
### Run a pattern
#### TypeScript SDK runner
```ts
import { runWorkflow } from '@agent-relay/sdk/workflows';
const run = await runWorkflow('workflows/feature-dev.yaml', {
vars: { task: 'Add OAuth login' },
});
```
### Quick Decision Framework
```text
Is the task independent per agent?
YES → fan-out (parallel workers, hub collects)
Does each step need the previous step's output?
YES → Is it strictly linear?
YES → pipeline
NO → dag (parallel where possible, `dependsOn` edges)
Does a coordinator need to stay alive and adapt?
YES → hub-spoke (single-level hub + workers)
hierarchical (structurally identical in current impl; use for naming/intent)
Is the task about making a decision?
YES → Do agents need to argue opposing sides?
YES → debate (adversarial, full mesh)
NO → consensus (cooperative, full mesh + coordination.consensusStrategy)
Does the right specialist emerge during processing?
YES → handoff (sequential chain, one active at a time)
Do all agents need to freely collaborate?
YES → mesh (full peer-to-peer edges)
Is cost the primary concern?
YES → cascade (chain of increasingly capable agents; each step's prompt
decides whether to pass through or redo the prior output)
```
### Pattern Reference (Core 10)
| # | Pattern | Topology (actual edges) | Best For |
| --- | ---------------- | ------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| 1 | **fan-out** | Hub broadcasts to N workers; workers reply to hub only | Independent subtasks (reviews, research, tests) |
| 2 | **pipeline** | Linear chain (`agent_i` → `agent_{i+1}`) | Ordered stages (design → implement → test) |
| 3 | **hub-spoke** | Hub ↔ spokes (bidirectional); no spoke-to-spoke | Dynamic coordination, lead reviews/adjusts |
| 4 | **consensus** | Full mesh; decision via `coordination.consensusStrategy` | Architecture decisions, approval gates |
| 5 | **mesh** | Full mesh (every agent ↔ every other) | Brainstorming, collaborative debugging |
| 6 | **handoff** | Chain; passes control forward | Triage, specialist routing |
| 7 | **cascade** | Chain of `dependsOn` steps; all run on success, downstream skipped on upstream failure (no built-in "fall through") | Cost optimization: cheap first, each step's prompt passes through or redoes |
| 8 | **dag** | Edges from step `dependsOn` | Mixed dependencies, parallel where possible |
| 9 | **debate** | Full mesh (same topology as mesh; roles drive behavior) | Rigorous adversarial examination |
| 10 | **hierarchical** | Hub + subordinates (single-level in current impl) | Large teams; semantic distinction from hub-spoke |
> **Heads up:** `hierarchical` resolves to the same edge structure as `hub-spoke` in `coordinator.ts:313-319`. Multi-level tree topology is not currently implemented — use pattern name for intent, but expect the same runtime graph.
### Additional Patterns (role-driven)
These 14 additional patterns exist in `SwarmPattern` (types.ts:114-139). The coordinator has role-based auto-selection heuristics (`coordinator.ts:51-165`), but they only fire when `swarm.pattern` is **omitted** — YAML validation requires it (`runner.ts:2105-2117`), so auto-selection is effectively a programmatic-API feature. In YAML, set `swarm.pattern` explicitly.
Topology is still resolved per-pattern once selected; the "Triggering roles" column reflects what the coordinator looks for to shape edges (per `coordinator.ts:250-450`):
| Pattern | Roles the topology keys off | Topology |
| ----------------- | ------------------------------------------------------- | ---------------------------------------------- |
| `map-reduce` | `mapper` + `reducer` | coordinator → mappers → reducers → coordinator |
| `scatter-gather` | — | hub → workers → hub |
| `supervisor` | `supervisor` | supervisor ↔ workers |
| `reflection` | `critic` or `reviewer` (auto-select uses `critic` only) | producers → critic → producers (loop) |
| `red-team` | `attacker`/`red-team` + `defender`/`blue-team` | adversarial mesh with optional judges |
| `verifier` | `verifier` | producers → verifiers → back to producers |
| `auction` | `auctioneer` | auctioneer → bidders → auctioneer |
| `escalation` | `tier-*` | tiered chain, escalate up / report down |
| `saga` | `saga-orchestrator`, `compensate-handler` | orchestrator ↔ participants |
| `circuit-breaker` | `primary` + `fallback`/`backup` | try primary, fallback on failure |
| `blackboard` | `blackboard` / `shared-workspace` | shared state hub |
| `swarm` | `hive-mind` / `swarm-agent` | stigmergy-style |
| `competitive` | — (declared explicitly) | independent parallel implementations + judge |
| `review-loop` | `implement*` + 2+ `reviewer*` | implementer ↔ reviewers |
### Structured Squad Review Loop
- Split the work into bounded implementation squads. Each squad owns a non-overlapping file or subsystem scope.
- Give each squad an implementer plus a shadow/review partner. The shadow follows the implementer in real time, checks alignment with the spec, and posts concise feedback before the work drifts.
- Require the implementer to self-reflect before external review: compare the final diff against the spec, AGENTS.md / CLAUDE.md, recent local conventions, tests, and declared non-goals.
- Run an independent self-review/fresh-eyes agent that reads the actual files and recent repo context, not just the chat transcript.
- Send that review back to the implementer for one repair round.
- After squads converge, run a final two-agent review team, usually one Claude reviewer and one Codex reviewer, independently. They compare notes, merge findings, and produce one final verdict.
- Spawn fresh fix agents for final-review findings. Those fix agents self-reflect, then the final reviewers re-check the post-fix state until the spec is fully satisfied or a blocker is documented.
- Use `supervisor` or `hub-spoke` when a lead needs to coordinate live squads.
- Use `review-loop` when the main risk is code quality and feedback iteration.
- Use `reflection` when critic feedback should loop directly back to producers.
- Use `verifier` when completion evidence matters more than design debate.
- Use `competitive` only when independent alternative implementations are useful; otherwise split by ownership scope.
### Pattern Details
The per-pattern YAML snippets below show only the pattern-relevant fields. A runnable YAML file also needs the required top-level `version` and `name`; see the [Complete YAML Example](#complete-yaml-example).
#### 1. fan-out — Parallel Workers
```yaml
swarm: { pattern: fan-out }
agents:
- { name: lead, cli: claude, role: lead }
- { name: auth-rev, cli: claude, role: worker, interactive: false }
- { name: db-rev, cli: claude, role: worker, interactive: false }
workflows:
- name: review
steps:
- { name: review-auth, agent: auth-rev, task: 'Review auth.ts' }
- { name: review-db, agent: db-rev, task: 'Review db.ts' }
```
#### 2. pipeline — Sequential Stages
```yaml
swarm: { pattern: pipeline }
agents:
- { name: designer, cli: claude }
- { name: implementer, cli: codex, interactive: false }
- { name: tester, cli: codex, interactive: false }
workflows:
- name: build
steps:
- {
name: design,
agent: designer,
task: 'Design the API schema',
verification: { type: output_contains, value: DONE },
}
- {
name: implement,
agent: implementer,
dependsOn: [design],
task: 'Implement: {{steps.design.output}}',
}
- { name: test, agent: tester, dependsOn: [implement], task: 'Write integration tests' }
```
#### 3. hub-spoke — Persistent Coordinator
```yaml
swarm:
pattern: hub-spoke
channel: swarm-api
agents:
- { name: lead, cli: claude, role: lead }
- { name: db-worker, cli: claude, role: worker }
- { name: api-worker, cli: claude, role: worker }
workflows:
- name: api-build
steps:
- { name: models, agent: db-worker, task: 'Build database models' }
- { name: routes, agent: api-worker, task: 'Build route handlers', dependsOn: [models] }
- { name: review, agent: lead, task: 'Review everything', dependsOn: [routes] }
```
#### 4. consensus — Cooperative Voting
```yaml
swarm: { pattern: consensus }
agents:
- { name: perf, cli: claude, role: reviewer }
- { name: dx, cli: claude, role: reviewer }
- { name: sec, cli: claude, role: reviewer }
coordination:
consensusStrategy: majority # declarative marker: majority | unanimous | quorum
votingThreshold: 0.66
workflows:
- name: decide
steps:
- { name: evaluate-perf, agent: perf, task: 'Evaluate perf of Fastify migration' }
- { name: evaluate-dx, agent: dx, task: 'Evaluate DX of Fastify migration' }
- { name: evaluate-sec, agent: sec, task: 'Evaluate security of Fastify migration' }
```
#### 5. mesh — Peer Collaboration
```yaml
swarm:
pattern: mesh
channel: swarm-debug
agents:
- { name: logs, cli: claude }
- { name: code, cli: claude }
- { name: repro, cli: claude }
workflows:
- name: debug-auth
steps:
- { name: logs, agent: logs, task: 'Check server logs' }
- { name: code, agent: code, task: 'Review auth code' }
- { name: repro, agent: repro, task: 'Write repro test' }
```
#### 6. handoff — Dynamic Routing
```yaml
swarm: { pattern: handoff }
agents:
- { name: triage, cli: claude }
- { name: billing, cli: claude }
- { name: tech, cli: claude }
workflows:
- name: support
steps:
- { name: triage, agent: triage, task: 'Triage: {{request}}' }
- { name: billing, agent: billing, dependsOn: [triage], task: 'Handle billing' }
- { name: tech, agent: tech, dependsOn: [triage], task: 'Handle tech issues' }
```
#### 7. cascade — Cost-Aware Fallthrough
```yaml
swarm: { pattern: cascade }
agents:
- { name: haiku, cli: claude, model: claude-haiku-4-5-20251001 }
- { name: sonnet, cli: claude, model: claude-sonnet-4-6 }
- { name: opus, cli: claude, model: claude-opus-4-7 }
workflows:
- name: answer
steps:
- { name: try-haiku, agent: haiku, task: '{{question}}' }
- name: try-sonnet
agent: sonnet
dependsOn: [try-haiku]
task: "If this is a complete answer, echo it verbatim. Otherwise answer anew:\n{{steps.try-haiku.output}}"
- name: try-opus
agent: opus
dependsOn: [try-sonnet]
task: "Final-tier answer, using prior attempts for context:\n{{steps.try-sonnet.output}}"
```
#### 8. dag — Directed Acyclic Graph
```yaml
swarm:
pattern: dag
maxConcurrency: 3
agents:
- { name: dev, cli: codex, role: worker }
workflows:
- name: fullstack
steps:
- { name: scaffold, agent: dev, task: 'Create project scaffold' }
- { name: frontend, agent: dev, task: 'Build React UI', dependsOn: [scaffold] }
- { name: backend, agent: dev, task: 'Build API', dependsOn: [scaffold] }
- { name: integrate, agent: dev, task: 'Wire together', dependsOn: [frontend, backend] }
```
#### 9. debate — Adversarial Refinement
```yaml
swarm: { pattern: debate }
agents:
- { name: pro, cli: claude, role: debater, task: 'Argue FOR monorepo' }
- { name: con, cli: claude, role: debater, task: 'Argue FOR polyrepo' }
- { name: judge, cli: claude, role: judge, task: 'Decide after 3 rounds' }
coordination:
barriers:
- { name: debate-done, waitFor: [pro-round-3, con-round-3] }
```
#### 10. hierarchical — Multi-Level (structurally hub-spoke today)
```yaml
swarm: { pattern: hierarchical }
agents:
- { name: lead, cli: claude, role: lead }
- { name: fe-coord, cli: claude, role: coordinator }
- { name: be-coord, cli: claude, role: coordinator }
- { name: fe-dev, cli: codex, role: worker, interactive: false }
- { name: be-dev, cli: codex, role: worker, interactive: false }
workflows:
- name: large-team
steps:
- { name: plan, agent: lead, task: 'Coordinate full-stack app' }
- { name: fe-plan, agent: fe-coord, task: 'Manage frontend', dependsOn: [plan] }
- { name: be-plan, agent: be-coord, task: 'Manage backend', dependsOn: [plan] }
- { name: fe-impl, agent: fe-dev, task: 'Build components', dependsOn: [fe-plan] }
- { name: be-impl, agent: be-dev, task: 'Build API', dependsOn: [be-plan] }
```
### Verification & Completion Signals
#### An agent step can complete in several ways (`runner.ts:5353-5395`, `runner.ts:4527-4538`):
```yaml
verification:
type: output_contains # or: exit_code | file_exists | custom
value: DONE # or: PLAN_COMPLETE, IMPLEMENTATION_COMPLETE, REVIEW_COMPLETE
```
### Agent Relay MCP — Correct Tool Names
The old category-expanded names are wrong. Current Agent Relay MCP tools are
flat names. In a client that decorates MCP tools, the prefix comes from the
configured server key. With the relay broker's `agent-relay` server key, Claude
Code users commonly see `mcp__agent-relay__send_dm`; Codex and opencode users
see the bare canonical name `send_dm`.
| Purpose | Canonical tool | Claude Code form with `agent-relay` key |
| ------------------------ | ----------------- | --------------------------------------- |
| Send DM to another agent | `send_dm` | `mcp__agent-relay__send_dm` |
| Check inbox | `check_inbox` | `mcp__agent-relay__check_inbox` |
| List agents | `list_agents` | `mcp__agent-relay__list_agents` |
| Post to a channel | `post_message` | `mcp__agent-relay__post_message` |
| Reply in a thread | `reply_to_thread` | `mcp__agent-relay__reply_to_thread` |
| Spawn sub-agent | `add_agent` | `mcp__agent-relay__add_agent` |
| Remove sub-agent | `remove_agent` | `mcp__agent-relay__remove_agent` |
> `interactive: false` agents run as non-interactive subprocesses with no relay connection. They must not call Relay MCP tools.
### Reflection (Trajectories)
#### Reflection is **not** a `reflectionThreshold` callback. It's configured via the `trajectories:` block:
```yaml
trajectories:
enabled: true
reflectOnBarriers: true # config flag exists but runner does NOT currently invoke this path
reflectOnConverge: true # fires at parallel convergence points (runner.ts:2762-2779)
autoDecisions: true # record retry/skip/fail decisions
```
### Common Mistakes
| Mistake | Why It Fails | Fix |
| -------------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Using mesh/debate for everything | Full-mesh blows up message volume past ~5 agents | Use hub-spoke or dag for most tasks |
| Pipeline for independent work | Sequential bottleneck | Use fan-out or dag |
| Hub-spoke for 2 agents | Hub is unnecessary overhead | Use pipeline or fan-out |
| Expecting `consensusStrategy` to tally votes | Runner has no vote-tally logic; field only affects coordinator auto-selection | Aggregate votes in a judge/lead step that reads `{{steps.*.output}}` |
| Handoff with "routing = skip other branches" | Skipping only fires on upstream **failure**, not routing decisions | Emit a routing token in triage output; downstream prompts self-no-op if token doesn't match |
| Cascade expecting skip-on-success | Runner has no cascade skip logic; failed upstream skips downstream | Chain downstream prompts to pass-through or redo based on `{{steps.previous.output}}` |
| Relying on `reflectOnBarriers` | Config flag exists but runner never calls it | Use `reflectOnConverge` for convergence reflection; use `reflection` pattern for critic loops |
| `interactive: false` agent calling MCP | Non-interactive subprocess has no relay | Use `interactive: true` (default) or emit output on stdout |
| Relying on multi-level `hierarchical` | Topology is single-level hub in current impl | Use pattern for naming; model levels via `dependsOn` graph |
| Writing `mcp__relaycast__send(...)` | Wrong tool name | Use `post_message` / `mcp__agent-relay__post_message` or `send_dm` / `mcp__agent-relay__send_dm` |
### Resume & Re-run
```ts
// Resume a failed run:
await runWorkflow('feature-dev.yaml', { resume: '<runId>' });
// Skip ahead, re-using cached outputs from an earlier run:
await runWorkflow('feature-dev.yaml', {
startFrom: 'review',
previousRunId: '<runId>',
});
```
### Complete YAML Example
```yaml
version: '1.0'
name: feature-dev
description: 'Blueprint-style feature development with quality gates.'
swarm:
pattern: hub-spoke
maxConcurrency: 2
timeoutMs: 3600000
channel: swarm-feature-dev
idleNudge: { nudgeAfterMs: 120000, escalateAfterMs: 120000, maxNudges: 1 }
agents:
- { name: lead, cli: claude, role: lead, permissions: { access: full } }
- { name: planner, cli: codex, role: planner, interactive: false, permissions: { access: readonly } }
- { name: developer, cli: codex, role: worker, interactive: false, permissions: { access: readwrite } }
- { name: reviewer, cli: claude, role: reviewer, permissions: { access: readonly } }
workflows:
- name: feature-delivery
onError: retry
preflight:
- { command: 'git status --porcelain', failIf: non-empty, description: 'Clean worktree' }
steps:
- name: plan
agent: planner
task: 'Plan: {{task}}'
verification: { type: output_contains, value: PLAN_COMPLETE }
- name: implement
agent: developer
dependsOn: [plan]
task: 'Implement: {{steps.plan.output}}'
verification: { type: output_contains, value: IMPLEMENTATION_COMPLETE }
- name: test
type: deterministic
dependsOn: [implement]
command: npm test
- name: review
agent: reviewer
dependsOn: [test]
task: 'Review implementation'
verification: { type: output_contains, value: REVIEW_COMPLETE }
coordination:
barriers:
- { name: delivery-ready, waitFor: [plan, implement, review], timeoutMs: 900000 }
trajectories:
enabled: true
reflectOnBarriers: true
reflectOnConverge: true
errorHandling:
strategy: retry
maxRetries: 2
retryDelayMs: 5000
```
### Source of Truth
| Claim | File |
| ----------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| Pattern enum (24 patterns) | `packages/sdk/src/workflows/types.ts:114-139` |
| Topology resolution per pattern | `packages/sdk/src/workflows/coordinator.ts:240-450` |
| Interactive-only topology edges | `packages/sdk/src/workflows/coordinator.ts:218-237` |
| Pattern auto-selection heuristics (programmatic API only) | `packages/sdk/src/workflows/coordinator.ts:51-165` |
| `runWorkflow(yamlPath, options)` | `packages/sdk/src/workflows/run.ts` |
| YAML validation requires `version` + `name` + `swarm.pattern` | `packages/sdk/src/workflows/runner.ts:2105-2117` |
| MCP tool names cited in convention-injection | `packages/sdk/src/relay-adapter.ts:29-36` |
| Completion modes (verification / evidence / owner / process-exit) | `packages/sdk/src/workflows/runner.ts:5353-5395`, `4527-4538` |
| Completion via PTY + summary fallback | `packages/sdk/src/workflows/runner.ts:6600-6615` |
| Downstream skip on upstream failure (not success) | `packages/sdk/src/workflows/runner.ts:7057-7088`, `step-executor.ts:329-334` |
| Trajectory reflection (only `reflectOnConverge` wired) | `packages/sdk/src/workflows/runner.ts:2762-2779`, `trajectory.ts:173-190` |
Attribution
Comments
Loading comments…