choosing-swarm-patterns · diff
git:20260331.ec0ff18 to git:20260504.ff3e8c9
306 added, 160 removed. Audit A to A.
---
name: choosing-swarm-patterns
- description: Use when coordinating multiple AI agents and need to pick the right orchestration pattern - covers 10 patterns (fan-out, pipeline, hub-spoke, consensus, mesh, handoff, cascade, dag, debate, hierarchical) with decision framework and reflection protocol
+ 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
- 10 orchestration patterns for multi-agent workflows. Pick the simplest pattern that solves the problem — add complexity only when the system proves it's insufficient.
+ The Agent Relay SDK (`@agent-relay/sdk`) supports 24 swarm patterns via a single `swarm.pattern` field. Patterns are configured declaratively in YAML or programmatically via the `workflow()` fluent builder — 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.
+ ### Two ways to run a pattern
+
+ #### **1. YAML (portable):**
+
+ ```ts
+ import { runWorkflow } from '@agent-relay/sdk/workflows';
+
+ const run = await runWorkflow('workflows/feature-dev.yaml', {
+ vars: { task: 'Add OAuth login' },
+ });
+ ```
+
### Quick Decision Framework
#### ```
```
Is the task independent per agent?
- YES → fan-out (parallel workers)
+ 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)
+ NO → dag (parallel where possible, `dependsOn` edges)
Does a coordinator need to stay alive and adapt?
- YES → Is there one level of management?
- YES → hub-spoke
- NO → hierarchical (multi-level)
+ 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)
- NO → consensus (cooperative voting)
+ YES → debate (adversarial, full mesh)
+ NO → consensus (cooperative, full mesh + coordination.consensusStrategy)
Does the right specialist emerge during processing?
- YES → handoff (dynamic routing)
+ YES → handoff (sequential chain, one active at a time)
Do all agents need to freely collaborate?
- YES → mesh (peer-to-peer)
+ YES → mesh (full peer-to-peer edges)
Is cost the primary concern?
- YES → cascade (cheap model first, escalate if needed)
+ 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 Reference
+ | # | 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 |
- | # | Pattern | Topology | Agents | Best For |
- |---|---------|----------|--------|----------|
- | 1 | **fan-out** | Star (SDK center) | N parallel | Independent subtasks (reviews, research, tests) |
- | 2 | **pipeline** | Linear chain | Sequential | Ordered stages (design → implement → test) |
- | 3 | **hub-spoke** | Star (live hub) | 1 lead + N workers | Dynamic coordination, lead reviews/adjusts |
- | 4 | **consensus** | Broadcast + vote | N voters | Architecture decisions, approval gates |
- | 5 | **mesh** | Fully connected | N peers | Brainstorming, collaborative debugging |
- | 6 | **handoff** | Routing chain | 1 active at a time | Triage, specialist routing, support flows |
- | 7 | **cascade** | Tiered escalation | Cheapest → most capable | Cost optimization, production workloads |
- | 8 | **dag** | Dependency graph | Parallel + joins | Complex projects with mixed dependencies |
- | 9 | **debate** | Adversarial rounds | 2+ debaters + judge | Rigorous evaluation, architecture trade-offs |
- | 10 | **hierarchical** | Tree (multi-level) | Lead → coordinators → workers | Large teams, domain separation |
+ > **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 |
+
### Pattern Details
#### 1. fan-out — Parallel Workers
```ts
- fanOut([
- { task: "Review auth.ts", name: "AuthReviewer" },
- { task: "Review db.ts", name: "DbReviewer" },
- ], { cli: "claude" });
+ await workflow('review')
+ .pattern('fan-out')
+ .agent('lead', { cli: 'claude', role: 'lead' })
+ .agent('auth-rev', { cli: 'claude', role: 'worker', interactive: false })
+ .agent('db-rev', { cli: 'claude', role: 'worker', interactive: false })
+ .step('review-auth', { agent: 'auth-rev', task: 'Review auth.ts' })
+ .step('review-db', { agent: 'db-rev', task: 'Review db.ts' })
+ .run();
```
#### 2. pipeline — Sequential Stages
- ```ts
- pipeline([
- { task: "Design the API schema", name: "Designer" },
- { task: "Implement the endpoints", name: "Implementer" },
- { task: "Write integration tests", name: "Tester" },
- ]);
+ ```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
```ts
- hubAndSpoke({
- hub: { task: "Coordinate building a REST API", name: "Lead" },
- workers: [
- { task: "Build database models", name: "DbWorker" },
- { task: "Build route handlers", name: "ApiWorker" },
- ],
- });
+ await workflow('api-build')
+ .pattern('hub-spoke')
+ .channel('swarm-api')
+ .agent('lead', { cli: 'claude', role: 'lead' })
+ .agent('db-worker', { cli: 'claude', role: 'worker' }) // interactive by default — hub DMs it
+ .agent('api-worker', { cli: 'claude', role: 'worker' }) // interactive by default — hub DMs it
+ .step('models', { agent: 'db-worker', task: 'Build database models' })
+ .step('routes', { agent: 'api-worker', task: 'Build route handlers', dependsOn: ['models'] })
+ .step('review', { agent: 'lead', task: 'Review everything', dependsOn: ['routes'] })
+ .run();
```
#### 4. consensus — Cooperative Voting
- ```ts
- consensus({
- proposal: "Should we migrate to Fastify?",
- voters: [
- { task: "Evaluate performance", name: "PerfExpert" },
- { task: "Evaluate DX", name: "DxExpert" },
- ],
- consensusType: "majority",
- });
+ ```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
```ts
- mesh({
- goal: "Debug the auth flow returning 500",
- agents: [
- { task: "Check server logs", name: "LogAnalyst" },
- { task: "Review auth code", name: "CodeReviewer" },
- { task: "Write repro test", name: "Tester" },
- ],
- });
+ await workflow('debug-auth')
+ .pattern('mesh')
+ .channel('swarm-debug')
+ .agent('logs', { cli: 'claude' })
+ .agent('code', { cli: 'claude' })
+ .agent('repro', { cli: 'claude' })
+ .step('logs', { agent: 'logs', task: 'Check server logs' })
+ .step('code', { agent: 'code', task: 'Review auth code' })
+ .step('repro', { agent: 'repro', task: 'Write repro test' })
+ .run();
```
#### 6. handoff — Dynamic Routing
- ```ts
- handoff({
- entryPoint: { task: "Triage the request", name: "Triage" },
- routes: [
- { agent: { task: "Handle billing", name: "Billing" }, condition: "billing, payment" },
- { agent: { task: "Handle tech issues", name: "TechSupport" }, condition: "error, bug" },
- ],
- maxHandoffs: 3,
- });
+ ```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 Escalation
+ #### 7. cascade — Cost-Aware Fallthrough
```ts
- cascade({
- tiers: [
- { agent: { task: "Answer this", cli: "claude" }, confidenceThreshold: 0.7, costWeight: 1 },
- { agent: { task: "Answer this", cli: "claude" }, confidenceThreshold: 0.85, costWeight: 5 },
- { agent: { task: "Answer this", cli: "claude" }, costWeight: 20 },
- ],
- });
+ await workflow('answer')
+ .pattern('cascade')
+ .agent('haiku', { cli: 'claude', model: 'claude-haiku-4-5-20251001' })
+ .agent('sonnet', { cli: 'claude', model: 'claude-sonnet-4-6' })
+ .agent('opus', { cli: 'claude', model: 'claude-opus-4-7' })
+ .step('try-haiku', { agent: 'haiku', task: '{{question}}' })
+ .step('try-sonnet', {
+ agent: 'sonnet',
+ task: 'If this is a complete answer, echo it verbatim. Otherwise answer anew:\n{{steps.try-haiku.output}}',
+ dependsOn: ['try-haiku'],
+ })
+ .step('try-opus', {
+ agent: 'opus',
+ task: 'Final-tier answer, using prior attempts for context:\n{{steps.try-sonnet.output}}',
+ dependsOn: ['try-sonnet'],
+ })
+ .run();
```
#### 8. dag — Directed Acyclic Graph
```ts
- dag({
- nodes: [
- { id: "scaffold", task: "Create project scaffold" },
- { id: "frontend", task: "Build React UI", dependsOn: ["scaffold"] },
- { id: "backend", task: "Build API", dependsOn: ["scaffold"] },
- { id: "integrate", task: "Wire together", dependsOn: ["frontend", "backend"] },
- ],
- maxConcurrency: 3,
- });
+ await workflow('fullstack')
+ .pattern('dag')
+ .maxConcurrency(3)
+ .agent('dev', { cli: 'codex', role: 'worker' })
+ .step('scaffold', { agent: 'dev', task: 'Create project scaffold' })
+ .step('frontend', { agent: 'dev', task: 'Build React UI', dependsOn: ['scaffold'] })
+ .step('backend', { agent: 'dev', task: 'Build API', dependsOn: ['scaffold'] })
+ .step('integrate', { agent: 'dev', task: 'Wire together', dependsOn: ['frontend', 'backend'] })
+ .run();
```
#### 9. debate — Adversarial Refinement
- ```ts
- debate({
- topic: "Monorepo vs polyrepo for the new platform?",
- debaters: [
- { task: "Argue for monorepo", position: "monorepo" },
- { task: "Argue for polyrepo", position: "polyrepo" },
- ],
- judge: { task: "Judge and decide", name: "ArchJudge" },
- maxRounds: 3,
- });
+ ```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 Delegation
+ #### 10. hierarchical — Multi-Level (structurally hub-spoke today)
```ts
- hierarchical({
- agents: [
- { id: "lead", task: "Coordinate full-stack app", role: "lead" },
- { id: "fe-coord", task: "Manage frontend", role: "coordinator", reportsTo: "lead" },
- { id: "be-coord", task: "Manage backend", role: "coordinator", reportsTo: "lead" },
- { id: "fe-dev", task: "Build components", role: "worker", reportsTo: "fe-coord" },
- { id: "be-dev", task: "Build API", role: "worker", reportsTo: "be-coord" },
- ],
- });
+ await workflow('large-team')
+ .pattern('hierarchical')
+ .agent('lead', { cli: 'claude', role: 'lead' })
+ .agent('fe-coord', { cli: 'claude', role: 'coordinator' })
+ .agent('be-coord', { cli: 'claude', role: 'coordinator' })
+ .agent('fe-dev', { cli: 'codex', role: 'worker', interactive: false })
+ .agent('be-dev', { cli: 'codex', role: 'worker', interactive: false })
+ .step('plan', { agent: 'lead', task: 'Coordinate full-stack app' })
+ .step('fe-plan', { agent: 'fe-coord', task: 'Manage frontend', dependsOn: ['plan'] })
+ .step('be-plan', { agent: 'be-coord', task: 'Manage backend', dependsOn: ['plan'] })
+ .step('fe-impl', { agent: 'fe-dev', task: 'Build components', dependsOn: ['fe-plan'] })
+ .step('be-impl', { agent: 'be-dev', task: 'Build API', dependsOn: ['be-plan'] })
+ .run();
```
-
- ### Reflection Protocol
+ ### Verification & Completion Signals
- #### All patterns support reflection — periodic synthesis that enables course correction. Enabled via `reflectionThreshold` on WorkflowOptions.
+ #### An agent step can complete in several ways (`runner.ts:5353-5395`, `runner.ts:4527-4538`):
- ```ts
- {
- reflectionThreshold: 10, // trigger after 10 agent messages
- onReflect: async (ctx) => {
- // Examine ctx.recentMessages, ctx.agentStatuses
- // Return adjustments or null
- },
- }
+ ```yaml
+ verification:
+ type: output_contains # or: exit_code | file_exists | custom
+ value: DONE # or: PLAN_COMPLETE, IMPLEMENTATION_COMPLETE, REVIEW_COMPLETE
```
+ ### Relaycast MCP — Correct Tool Names
- ### Common Mistakes
+ The skill previously referenced `mcp__relaycast__send` / `mcp__relaycast__dm` — those names are wrong. The real tools (the first three are cited in the workflow convention-injection at `relay-adapter.ts:31-35`; the rest are exposed by the live `relaycast` MCP server):
- | Mistake | Why It Fails | Fix |
- |---------|-------------|-----|
- | Using mesh for everything | O(n^2) communication, debugging nightmare | Use hub-spoke for most tasks |
- | Pipeline for independent work | Sequential bottleneck | Use fan-out or dag |
- | Hub-spoke for simple parallel tasks | Hub is unnecessary overhead | Use fan-out |
- | Consensus for non-decisions | Voting on implementation tasks wastes time | Use hub-spoke, let lead decide |
- | No circuit breaker on handoff | Infinite routing loops | Always set maxHandoffs |
- | Cascade without confidence parsing | Agents don't report confidence | Convention injection handles this |
- | Hierarchical for 3 agents | Management overhead exceeds benefit | Use hub-spoke for small teams |
+ | Purpose | Tool | Source |
+ | ------------------------ | ------------------------------------- | --------------------- |
+ | Send DM to another agent | `mcp__relaycast__message_dm_send` | `relay-adapter.ts:31` |
+ | Check inbox | `mcp__relaycast__message_inbox_check` | `relay-adapter.ts:35` |
+ | List agents | `mcp__relaycast__agent_list` | `relay-adapter.ts:35` |
+ | Post to a channel | `mcp__relaycast__message_post` | relaycast MCP server |
+ | Reply in a thread | `mcp__relaycast__message_reply` | relaycast MCP server |
+ | Spawn sub-agent | `mcp__relaycast__agent_add` | relaycast MCP server |
+ | Remove sub-agent | `mcp__relaycast__agent_remove` | relaycast MCP server |
- ### DAG Executor — Proven Pattern
+ > `interactive: false` agents run as non-interactive subprocesses with no relay connection — they must NOT call any `mcp__relaycast__*` tool (validator warns on this at `validator.ts:138-150`, check `NONINTERACTIVE_RELAY`).
- #### Agent Completion: Detect → Release → Collect
+ ### Reflection (Trajectories)
- ```
- Agent writes summary file → Orchestrator polls (5s) → Detects new mtime →
- Reads summary → Calls client.release(agent) → agent_exited fires → Node marked complete
+ #### 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
```
- #### State & Resume
+ ### 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 `mcp__relaycast__message_post` or `message_dm_send` |
+
+ ### Resume & Re-run
+
+ #### ```ts
+
```ts
- saveState(completed, depsOutput, results, startTime);
- // Restart with --resume to skip completed nodes
- ```
+ // 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>',
+ });
+ ```
- ### YAML Workflow Definition
+ ### Complete YAML Example
- #### Any pattern can be defined in YAML for portability:
+ #### ```yaml
```yaml
- version: "1.0"
+ version: '1.0'
name: feature-dev
- pattern: hub-spoke
+ 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:
- - id: lead
- role: lead
- cli: claude
- - id: developer
- role: worker
- cli: codex
- reportsTo: lead
- steps:
- - id: plan
- agent: lead
- prompt: "Create a development plan for: {{task}}"
- expects: "PLAN_COMPLETE"
- - id: implement
- agent: developer
- dependsOn: [plan]
- prompt: "Implement: {{steps.plan.output}}"
- expects: "DONE"
- reflection:
- enabled: true
- threshold: 10
- trajectory:
+ - { 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` |
+ | `WorkflowBuilder` fluent API | `packages/sdk/src/workflows/builder.ts` |
+ | `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` |