# How SwarmForge Handles Inter-Agent Communication: A File-Based Handoff Protocol Explained

> Discover how SwarmForge handles inter-agent communication using a robust file-based handoff protocol for reliable, auditable AI agent swarm collaboration.

- Repository: [Robert C. Martin/swarm-forge](https://github.com/unclebob/swarm-forge)
- Tags: deep-dive
- Published: 2026-08-29

---

**SwarmForge uses a durable, file-based handoff protocol that decouples agents from direct tmux or network interactions, enabling reliable, auditable collaboration across AI agent swarms.**

SwarmForge's inter-agent communication system is built on a **handoff protocol** that treats messages as persistent files rather than ephemeral messages. This architecture, implemented in the `unclebob/swarm-forge` repository, ensures that agent communication survives crashes, supports human audit workflows, and eliminates fragile direct socket connections between agents.

## The Core Components of SwarmForge Inter-Agent Communication

SwarmForge's communication layer consists of three coordinated components: handoff generation, a central delivery daemon, and inbox management scripts. Each component is implemented as a discrete script or process with clear responsibilities.

### Agent-Generated Handoffs via [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh)

When an agent completes work and needs to notify another agent, it creates a handoff file draft. The **[`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh)** script in [`swarmforge/scripts/swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarm_handoff.sh) handles validation and queuing.

The script performs several critical functions:

- Validates draft handoff files (type `git_handoff` or `note`)
- Adds required metadata headers: `id`, `created_at`, `priority`
- Implements an **audit gate**: the first valid call returns `AUDIT_REQUIRED`; only an unchanged second call atomically writes the handoff to `.swarmforge/handoffs/outbox/`

This audit requirement ensures human review before code changes propagate through the swarm.

```bash

# Agent creates a git handoff draft in its worktree

cat > ./tmp/handoff.txt <<EOF
type: git_handoff
to: cleaner
priority: 50
task: add-api-endpoint
commit: a1b2c3d9e8
EOF

# Validate and queue the handoff

swarm_handoff.sh ./tmp/handoff.txt

# → prints "AUDIT_REQUIRED" the first time, then queues on second unchanged call

```

### The Handoff Daemon: `handoffd.bb`

The **`handoffd.bb`** daemon (a Babashka script) provides the reliable transport layer for inter-agent communication. According to [`swarmforge/handoff-protocol.md`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/handoff-protocol.md), it watches all agent outboxes and performs five sequential operations for each complete `.handoff` file:

1. **Verifies** file headers for protocol compliance
2. **Copies** the handoff into every recipient's `inbox/new/` directory
3. **Adds** a `recipient` header and `enqueued_at` timestamp
4. **Sends** a generic tmux wake-up message to the recipient's session
5. **Moves** the original outbox file to `sent/` (or `failed/` on error)

The daemon's file-based approach provides durability across restarts and eliminates any direct tmux command usage by agents themselves. This decoupling prevents permission issues and enables crash recovery.

### Agent Inbox Processing: [`ready_for_next.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next.sh) and [`done_with_current.sh`](https://github.com/unclebob/swarm-forge/blob/main/done_with_current.sh)

Each agent runs **[`ready_for_next.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next.sh)** to poll for work. This script:

- Reads the agent's role configuration from `.swarmforge/roles.tsv`
- Determines receive mode (`task` or `batch`) for that role
- Dispatches to **[`ready_for_next_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next_task.sh)** or **[`ready_for_next_batch.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next_batch.sh)**

The task-mode helper selects the earliest handoff in `inbox/new/` (sorted by priority, then timestamp), moves it to `inbox/in_process/`, and prints a structured `TASK:` line with the payload:

```bash

# Agent checks for new work (run inside its worktree)

ready_for_next.sh

# Example output when a task is available

TASK: .swarmforge/handoffs/inbox/in_process/00_20260615T140531Z_000042_from_architect_to_coder.handoff
FROM: architect
TYPE: git_handoff
PRIORITY: 50
TASK_NAME: add-api-endpoint
PAYLOAD:
Re-read your role and constitution.

merge_and_process.sh architect a1b2c3d9

```

Upon completion, agents call **[`done_with_current.sh`](https://github.com/unclebob/swarm-forge/blob/main/done_with_current.sh)**, which forwards to **[`done_with_current_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/done_with_current_task.sh)** or **[`done_with_current_batch.sh`](https://github.com/unclebob/swarm-forge/blob/main/done_with_current_batch.sh)** to:

- Record a `completed_at` timestamp
- Move the handoff file to `inbox/completed/`
- Optionally emit `MAIL_WAITING` to trigger immediate re-polling

```bash

# After finishing the task, the agent marks it complete

done_with_current.sh

# Output:

COMPLETED: .swarmforge/handoffs/inbox/completed/00_20260615T140531Z_000042_from_architect_to_coder.handoff
MAIL_WAITING   # → triggers another ready_for_next.sh if more mail exists

```

## The SwarmForge Communication Lifecycle

The complete inter-agent communication flow follows this deterministic path:

```

Agent → swarm_handoff.sh → outbox/
handoffd daemon → inbox/new/ (recipients) → tmux wake-up
Agent → ready_for_next.sh → inbox/in_process/
Agent → done_with_current.sh → inbox/completed/

```

This lifecycle provides complete visibility into message state at every stage.

## Key Architectural Properties of SwarmForge Communication

**Durability** — All handoffs persist as files under `.swarmforge/handoffs/`, creating a complete audit trail with headers: `id`, `from`, `to`, `priority`, `created_at`, `enqueued_at`, `dequeued_at`, `completed_at`.

**Human Auditing** — Git handoffs require explicit double-verification before entering the queue, enforcing code review workflows before merge operations proceed.

**Transport Decoupling** — Agents never communicate via tmux sockets or network RPCs. They write and read files exclusively, reacting only to generic wake-up notifications. This design eliminates tmux permission complexity and enables robust crash recovery.

**Configurable Processing Modes** — The [`swarmforge.conf`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge.conf) file supports per-role configuration of:
- Receive modes: `task` (sequential) or `batch` (parallel same-priority processing)
- Propagation tokens: `forward-only`, `back-one`, `back-all` controlling copy-on-forward behavior

## Key Source Files for SwarmForge Communication

| Purpose | Path |
|---------|------|
| Protocol specification | [`swarmforge/handoff-protocol.md`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/handoff-protocol.md) |
| Outbound validation & audit gate | [`swarmforge/scripts/swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarm_handoff.sh) |
| Delivery daemon | `swarmforge/scripts/handoffd.bb` |
| Task-mode inbox selector | [`swarmforge/scripts/ready_for_next_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/ready_for_next_task.sh) |
| Batch-mode inbox selector | [`swarmforge/scripts/ready_for_next_batch.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/ready_for_next_batch.sh) |
| Task completion handler | [`swarmforge/scripts/done_with_current_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/done_with_current_task.sh) |
| Batch completion handler | [`swarmforge/scripts/done_with_current_batch.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/done_with_current_batch.sh) |
| Role and window configuration | [`swarmforge/swarmforge.conf`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/swarmforge.conf) |

## Summary

- **SwarmForge inter-agent communication** uses a file-based handoff protocol with three main components: [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) for outbound messages, `handoffd.bb` for reliable delivery, and inbox helper scripts for processing.
- **Durability and auditability** are built into the design through persistent files, complete header metadata, and mandatory audit gates for code changes.
- **Decoupled transport** eliminates direct agent-to-agent connections; agents communicate exclusively through the filesystem with generic wake-up notifications.
- **Configurable receive modes** (`task`/`batch`) and propagation tokens allow fine-grained control over how agents process and forward messages.

## Frequently Asked Questions

### How does SwarmForge ensure messages aren't lost during crashes?

All handoffs are written to disk before acknowledgment. The `handoffd.bb` daemon moves files atomically between `outbox/`, `inbox/new/`, and `sent/` directories, ensuring that interrupted operations can be resumed. The file-based design provides natural durability that survives process restarts and system crashes without message loss.

### Why does [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) require two identical calls for git handoffs?

The `AUDIT_REQUIRED` mechanism enforces human review before code merges propagate through the agent swarm. The first call validates the handoff draft and returns `AUDIT_REQUIRED`. Only a second call with identical content actually queues the handoff, ensuring that a human has reviewed the proposed changes. This design prevents automated agents from inadvertently merging unreviewed code.

### Can agents communicate without the handoffd daemon running?

No. While agents can create handoff drafts and poll their inboxes, the `handoffd.bb` daemon is required to move handoffs between outboxes and inboxes. Without the daemon, handoffs accumulate in `outbox/` undelivered. However, the daemon's stateless, file-based operation means it can be restarted at any time and resume processing from where it left off.

### What's the difference between task and batch receive modes?

**Task mode** (handled by [`ready_for_next_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next_task.sh)) processes one handoff at a time, moving the highest-priority (or earliest) item to `inbox/in_process/` and waiting for completion before selecting the next. **Batch mode** (handled by [`ready_for_next_batch.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next_batch.sh)) selects all handoffs at the highest priority level together, allowing parallel processing. The mode is configured per-role in `.swarmforge/roles.tsv`.