Skip to content

Multi-Agent Workflows

BMO’s multi-agent tools (spawn_agent, wait_agent, list_agents, create_team, and spawn_agents_on_csv) let you run parallel workloads, fan out over data, and coordinate teams inside one session.

See the full technical reference in Advanced: Multi-Agent.

I want to do three things in parallel:
1. Write tests for internal/api/
2. Write tests for internal/auth/
3. Write tests for internal/db/
Spawn one sub-agent per task and wait for them all.

BMO will typically spawn three agents and then wait on them with wait_agent(ids=[...]).

What you should see: In the TUI, the main chat shows a Multi-agent accountability card for the current session. It lists each local child ID, status, and short progress label, plus active/done/degraded counts. Raw spawn_agent and wait_agent rows stay available through expansion, so the tool inputs and outputs remain inspectable without dominating the default view. /agent-family is the richer drill-in: it opens a ranked conductor view and can toggle into a grouped board view for active parallel work.

Multi-agent accountability card
Live BMO TUI: the in-chat Multi-agent accountability card near the spawn/wait tool rows. It rolls up active/complete/need-review counts, lists each child by status, detail, and id, and points to /agent-family and /agents for the drill-in.
Agent family conductor
Live BMO TUI: the /agent-family conductor surface, the ranked drill-in that the accountability card points to. Each local child shows status and progress; it can toggle into a grouped board view for active parallel work.

Verify it worked: Use list_agents, /agents, or /agent-family to confirm child status and inspect raw outputs. /team is for team or CSV-job surfaces, not the primary local child-agent inspector.

Recovery: Press esc to interrupt the parent agent. Spawned agents that are still running will be cancelled. No files are written by sub-agents until you approve tool calls (unless --auto-approve-tools is set).

Create a CSV with one task per row, then:

Fan out functions.csv to parallel agents.
Each agent should implement the function described in its row.

functions.csv:

function,file,description
ParseJWT,internal/auth/jwt.go,Parse and validate a JWT string
ValidateToken,internal/auth/token.go,Validate token expiry and claims
HashPassword,internal/auth/password.go,Bcrypt hash a password

Agents process each row independently. Results are collated when all complete.

What you should see: One agent is spawned per CSV row. Progress updates appear as each agent finishes its row.

Safety note: For large CSVs, check options.agent_max_threads first to avoid spawning more agents than your machine can handle.

Recovery: If a fan-out is interrupted, already-completed agents’ changes remain on disk. Inspect and revert with git diff and git checkout as needed.

Coordinate shared files with workspace claims

Section titled “Coordinate shared files with workspace claims”

When multiple sessions or agents need the same checkout, use workspace_claim to make file ownership visible before mutation. Claims use a three-tier model:

TierOperator termWhat it is
T1Session path claimsIn-memory rows in this BMO process (/workspace-claims primary table)
T2Shared path mirrorSQLite workspace_path rows when durable hard claims are enabled
T3Resource leasesSQLite ports, lanes, and other runtime_claim resources

A soft claim advertises intent; a hard claim blocks overlapping mutation tools from other sessions until the claim is released, yielded, expires, or is negotiated. When durable hard claims are explicitly enabled, hard T1 claims also create a T2 mirror so sibling BMO processes sharing the same data directory block overlapping BMO mutation tools; shell commands and external editors remain outside that guard. CLI prelaunch bmo workspace claims acquire does not seed T1 rows in a later process — use in-session workspace_claim for doc captures.

Use workspace_claim to take a hard claim on internal/db/ while the migration is
being edited. List active claims before assigning follow-up workers.

Use /workspace-claims to inspect claims by tier in the TUI. Session path claims (T1) appear in the primary section; shared path mirror (T2) and resource leases (T3) follow with tier labels. The header shows hard (mode label) separately from blocking hard (claims that currently deny overlapping mutation tools). Yielded or blocked_by_claim hard rows still count toward hard but not blocking hard. The same claim summary is available through workspace_snapshot, so agents can see active hard locks and negotiation states before choosing a write scope.

Live captures below require a successful setup turn that calls workspace_claim; when setup does not acquire a claim, Total active may read 0 even though recent coordination events still show claim lifecycle history.

Workspace claims table
Live BMO TUI: the /workspace-claims table. When setup acquires a hard claim, active path claims show mode (HARD/SOFT), state, and owner; the header separates hard (mode) from blocking hard (currently denies overlapping mutation tools).

Claims are path-scoped. If the overlap is semantic rather than path-local, split the scope, request a merger, or use isolated worktrees.

When a multi-agent workflow needs to be evaluated after the fact, bind it to a workstream and package the evidence. A defensible factory-shaped run records:

  • the workstream id and plan or recipe;
  • the parent session and delegated lanes;
  • workspace claims or worktree posture when shared mutation risk exists;
  • review findings, verification results, and residual work;
  • whether remote output was applied locally or remains a proposal.

For longer-lived coordination, ask BMO to create a team inside the run:

Create a team called "dev-team" with this goal:
- planner: design the migration
- coder: implement the code changes
- reviewer: review the final diff

Use /team in the TUI to monitor the team and background CSV jobs. If you need to point BMO at a remote team backend, configure [options.teams] with server_url and token.

What you should see: Each team member appears in the /team panel with its role and current status.

Team panel
Live BMO TUI: the /team Mission Control surface. The Teams section lists dev-team as active, tied to the current session, with four members loaded.

Recovery: To stop a team, press esc or close the session. Team state is session-scoped and does not persist after the session ends.

For agents that must not conflict on the filesystem, ask BMO to spawn them with worktree=true, or set the default on the task agent:

[agents.task.delegation]
worktree_isolation = true
worktree_auto_remove = false

Each sub-agent gets its own git worktree. See Worktree Isolation for merge strategies.

Verify it worked: Run git worktree list to see active worktrees created by sub-agents.

Recovery: If worktrees are left behind after a crash, remove them with git worktree remove <path> or git worktree prune. Set worktree_auto_remove = true to have BMO clean them up automatically.

Use sub-agents, teams, and CSV fan-out when work stays inside one BMO session. Use Agent mesh + A2A when a remote A2A peer should run the task:

  • mesh_resolve — list candidates for a capability (discovery only).
  • invoke_a2a — resolve (when needed) and run the remote task.
  • orchestrate_workflow / execute_dag — ordered multi-remote graphs; input-required on a node pauses until you continue that task.

Which surface to use when (spawn vs mesh vs nanite vs automation): maintainer multi-agent orchestration surface.

  • Spawn depth: options.agent_max_depth (default 1 = one hop root → child). Per-agent [agents.<id>.delegation].max_depth tightens and is clamped to global. Global 0 resets to default; per-agent 0 disables spawning for that agent. -1 disables spawns. See Multi-Agent — Spawn depth and maintainer spawning.md.
  • Concurrency: options.agent_max_threads caps parallel siblings; nanites use nanite_max_threads separately.
  • Agents share the same provider config and model settings as the parent unless overridden
  • CSV fan-out workers report structured results via report_agent_job_result