# SwarmForge Handoff Protocol: Complete File-Based Communication System for AI Agents

> Explore the SwarmForge handoff protocol, a robust file-based system enabling AI agents to share work items seamlessly. Discover its advantages for distributed AI communication.

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

---

**The SwarmForge handoff protocol is a durable, file-based communication channel that lets AI agents exchange work-items without direct tmux socket access or manual git coordination.**

The handoff protocol, as implemented in `unclebob/swarm-forge`, replaces legacy log-book and resend-queue mechanisms with an atomic, auditable system. Agents communicate through structured files that survive crashes and provide complete lifecycle tracking.

## Core Architecture of the SwarmForge Handoff Protocol

### Directory Layout and File Organization

Each agent's work-tree contains a hidden `.swarmforge/handoffs/` directory with four subdirectories:

- `outbox/` — staging area for outbound handoffs
- `sent/` — archive of successfully delivered handoffs
- `failed/` — quarantine for undeliverable handoffs
- `inbox/` — incoming messages with `new/`, `in_process/`, and `completed/` subfolders

The `handoffd` daemon watches `outbox/` and copies new handoffs into recipients' `inbox/new/` directories. This design, documented in [`swarmforge/handoff-protocol.md`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/handoff-protocol.md) lines 24-38, ensures durability through simple filesystem operations.

### Handoff Filename Format

Every handoff uses a strictly ordered filename that enables deterministic processing:

```

<priority>_<timestamp>_<seq>_from_<sender>_to_<recipients>.handoff

```

The **sequence number** guarantees total ordering when timestamps collide. Priority prefixes allow high-urgency handoffs to surface first without complex queue management.

### File Structure: Headers and Body

A handoff file contains two sections separated by a blank line:

```text
id: hand-20260615-140531-abc123
from: architect
to: cleaner
type: git_handoff
priority: 50
created_at: 2026-06-15T14:05:31Z

[opaque body content — not parsed by the system]

```

Critical headers (`id`, `from`, `to`, `type`) are injected by [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh). The body is treated as opaque payload—agents may include shell commands, commit hashes, or free-form instructions without protocol constraints.

### Supported Message Types

The protocol currently defines two message types per [`handoff-protocol.md`](https://github.com/unclebob/swarm-forge/blob/main/handoff-protocol.md) lines 30-48:

| Type | Purpose | Authorization |
|---|---|---|
| `git_handoff` | Request to forward a commit to another role | Automatic for role transitions |
| `note` | Short free-form message | Explicit authorization only |

## Key Components of the SwarmForge Handoff Protocol

### Outbound Gate: [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh)

Located at [`swarmforge/scripts/swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarm_handoff.sh), this script is the **sole entry point** for creating handoffs. It performs:

1. Draft validation against protocol schema
2. Reserved header injection (`id`, `from`, `created_at`)
3. Commit hash validation (10 hexadecimal characters)
4. Atomic write to `outbox/tmp/`, then rename to `outbox/*.handoff`

The atomic rename guarantees that `handoffd` never sees partially written files.

### Delivery Daemon: `handoffd.bb`

The Babashka-based daemon (`swarmforge/scripts/handoffd.bb`) implements the delivery engine:

- Scans each agent's `outbox/` for new `.handoff` files
- Validates file integrity and recipient existence
- Copies to each recipient's `inbox/new/` with injected `recipient` and `enqueued_at` headers
- Sends tmux wake-up: `"You have new handoff mail. If idle, run ready_for_next.sh."`
- Moves source file to `sent/` on success, `failed/` on error

All operations are idempotent—duplicates are detected via `id` header and skipped.

### Queue Helper Scripts

The protocol provides abstraction scripts that shield agents from directory manipulation:

- [`ready_for_next.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next.sh) — entry point that dispatches based on role's receive mode
- [`ready_for_next_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next_task.sh) — single-task processing
- [`ready_for_next_batch.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next_batch.sh) — batch processing
- [`done_with_current.sh`](https://github.com/unclebob/swarm-forge/blob/main/done_with_current.sh) — completion dispatcher
- [`done_with_current_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/done_with_current_task.sh) / [`done_with_current_batch.sh`](https://github.com/unclebob/swarm-forge/blob/main/done_with_current_batch.sh) — specific completion handlers

These helpers, defined in [`handoff-protocol.md`](https://github.com/unclebob/swarm-forge/blob/main/handoff-protocol.md) lines 62-70, own all inbox state transitions and timestamp injection.

## SwarmForge Handoff Protocol Lifecycle

### Step 1: Create

Agents draft handoffs in `./tmp/` and submit via [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh):

```bash

# Draft a git_handoff request

cat > ./tmp/handoff.txt <<'EOF'
type: git_handoff
to: cleaner
priority: 50
task: task-1-cave-setup
commit: a1b2c3d9e8
EOF

# Validate and queue

swarm_handoff.sh ./tmp/handoff.txt

```

Output: `.swarmforge/handoffs/outbox/50_20260615T140531Z_000042_from_architect_to_cleaner.handoff`

### Step 2: Deliver

`handoffd.bb` detects the file, copies to recipient inbox, injects delivery metadata, and notifies via tmux. No agent action required.

### Step 3: Consume

Recipient fetches work through the helper chain:

```bash
ready_for_next.sh

```

This reads `.swarmforge/roles.tsv` to determine receive mode, then executes the appropriate handler. The helper moves file(s) to `inbox/in_process/` and adds `dequeued_at` timestamp.

Example output:

```

TASK: .swarmforge/handoffs/inbox/in_process/50_20260615T140531Z_000042_from_architect_to_cleaner.handoff
FROM: architect
TYPE: git_handoff
PRIORITY: 50
TASK_NAME: task-1-cave-setup
PAYLOAD:
Re-read your role and constitution.
merge_and_process.sh architect a1b2c3d9

```

### Step 4: Complete

After work finishes:

```bash
done_with_current.sh

```

Updates `completed_at`, archives to `inbox/completed/`, and prints status: `COMPLETED`, `MAIL_WAITING`, or `NO_TASK`.

## Audit Trail and Header Lifecycle

The SwarmForge handoff protocol captures complete provenance through header evolution:

| Stage | Header Added | Script Responsible |
|---|---|---|
| Creation | `id`, `from`, `created_at`, `type` | [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) |
| Delivery | `recipient`, `enqueued_at` | `handoffd.bb` |
| Consumption | `dequeued_at` | `ready_for_next_*.sh` |
| Completion | `completed_at` | `done_with_current_*.sh` |

This immutable progression enables forensic reconstruction of any handoff's journey.

## Design Goals and Protocol Guarantees

The SwarmForge handoff protocol achieves four primary objectives:

- **Durability** — Handoff files survive process restarts; filesystem is the single source of truth
- **Auditability** — Complete timestamp chain from creation through completion
- **Agent Simplicity** — No tmux socket manipulation, no direct git operations, minimal API surface
- **Extensibility** — New message types via header schema extension and daemon validation updates

Atomic operations prevent duplicate deliveries even if `handoffd` crashes mid-transaction. State transitions use move/rename semantics that are atomic on POSIX filesystems.

## Summary

- **SwarmForge handoff protocol** replaces fragile log-based coordination with durable file-system queues
- **Four directory states** (`outbox/`, `sent/`, `failed/`, `inbox/` with subfolders) provide clear handoff lifecycle management
- **[`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh)** is the only valid entry point for creating handoffs; it enforces schema and performs atomic writes
- **`handoffd.bb`** delivers handoffs idempotently, injecting delivery metadata and sending tmux notifications
- **Helper scripts** ([`ready_for_next.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next.sh), [`done_with_current.sh`](https://github.com/unclebob/swarm-forge/blob/main/done_with_current.sh)) abstract queue state machines from agent logic
- **Complete audit trail** through headers: `created_at` → `enqueued_at` → `dequeued_at` → `completed_at`

## Frequently Asked Questions

### How does the SwarmForge handoff protocol prevent duplicate deliveries?

The daemon uses the `id` header as a deduplication key. Before copying to any inbox, `handoffd.bb` checks if a handoff with that `id` already exists in the recipient's `new/`, `in_process/`, or `completed/` folders. Combined with atomic file moves and POSIX rename semantics, this ensures exactly-once delivery semantics even across daemon restarts.

### What happens if a handoff fails to deliver?

Failed deliveries move the source file from `outbox/` to `failed/` with an appended error reason. The original handoff remains intact for manual inspection or retry. Recipients that are temporarily unreachable do not block delivery to other recipients—each copy operation is independent.

### Can agents send handoffs to multiple recipients simultaneously?

Yes. The filename `to` field and internal `to` header support comma-separated recipient lists. The daemon iterates through recipients, creating independent copies with individualized `recipient` headers. Each recipient receives their own `enqueued_at` timestamp reflecting actual delivery time.

### How do agents know when new handoffs arrive?

The `handoffd` daemon sends a generic tmux message to the recipient's pane: `"You have new handoff mail. If idle, run ready_for_next.sh."` Agents poll by running [`ready_for_next.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next.sh) when convenient—there is no forced interrupt or callback mechanism, keeping agent logic simple and synchronous.