# How the SwarmForge Handoff Protocol Works: File-Based Agent Coordination

> Discover how SwarmForge agents coordinate tasks using a file-based handoff protocol. Learn about atomic file naming and directory-based state machines for seamless agent communication.

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

---

**SwarmForge agents coordinate work through a deterministic, file-based handoff protocol that uses atomic file naming and directory-based state machines to pass tasks between roles without relying on mutable logbooks or direct messaging.**

The unclebob/swarm-forge repository implements this lightweight handoff protocol to enable deterministic work exchange between autonomous agents. By leveraging the file system for state management rather than git history or ad-hoc messaging, the protocol ensures race-free delivery and precise ordering across multi-role development pipelines.

## The Five-Step Handoff Lifecycle

The protocol enforces a strict lifecycle from draft creation to completion, governed by three helper scripts that every role executes.

### Step 1: Drafting the Handoff

An agent initiates transfer by writing a temporary draft file describing the work to be passed. The draft must specify a `type` field—either `git_handoff` or `note`—and, for git handoffs, include a 10-character commit abbreviation. This draft serves as input to the outbound validation gate.

### Step 2: Validation and Queuing

The **[`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh)** script (located at [`swarmforge/scripts/swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarm_handoff.sh)) acts as the outbound gate. It validates the draft's type, resolves the commit abbreviation to a single commit, and verifies role and task fields. Upon validation, it atomically writes a final handoff file into `.swarmforge/handoffs/outbox/` using a deterministic filename format:

```

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

```

The `sequence` counter is maintained per-worktree, ensuring uniqueness even when timestamps collide.

### Step 3: Daemon Delivery

The **handoff daemon** (`handoffd.bb` in `swarmforge/scripts/handoffd.bb`) watches the outbox directory. When it detects a complete `.handoff` file, it copies the file into each recipient's `inbox/new` folder, writes delivery headers including `recipient` and `enqueued_at` timestamps, and sends a generic tmux wake-up notification to alert idle agents.

### Step 4: Work Acceptance

An idle agent invokes **[`ready_for_next.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next.sh)** ([`swarmforge/scripts/ready_for_next.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/ready_for_next.sh)) to pull work. This script selects the highest-priority handoff(s) from `inbox/new/`, moves them to `inbox/in_process/`, and configures the environment for the role to begin execution. Agents can operate in `task` mode (single handoff) or `batch` mode (all equal-priority handoffs).

### Step 5: Completion and Return

After finishing work, the agent runs **[`done_with_current.sh`](https://github.com/unclebob/swarm-forge/blob/main/done_with_current.sh)** ([`swarmforge/scripts/done_with_current.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/done_with_current.sh)). This moves the processed handoff to `inbox/completed/`, optionally creates a new outbound handoff (such as a return `git_handoff`), and notifies the daemon that the agent can accept the next assignment.

## Core Protocol Concepts

The handoff protocol relies on several architectural decisions that ensure reliability and determinism.

### File-Based State Management

All handoff metadata lives in the worktree under `.swarmforge/handoffs/` with specific subdirectories:
- `outbox/` – Validated handoffs awaiting delivery
- `inbox/new/` – Incoming handoffs ready for pickup
- `inbox/in_process/` – Currently active work
- `inbox/completed/` – Finished handoffs
- `sent/` – Archive of outbound handoffs

This design eliminates runtime dependency on git history or network services.

### Atomic Filename Conventions

The filename encodes critical metadata to prevent race conditions and enable deterministic ordering: priority level, ISO timestamp, sequence number, sender role, and recipient list. This convention allows the filesystem itself to act as a priority queue without requiring database locks or inter-process communication.

### Handoff Types

The protocol supports two distinct handoff types:

- **`git_handoff`** – Points recipients to a specific commit using an exact 10-hex-character abbreviation. The daemon validates that this abbreviation resolves to a single commit before queuing.
- **`note`** – A lightweight message without a commit reference. Notes must be read before agents accept further handoffs, ensuring critical communications are not skipped.

### Priority Handling

The `<priority>` prefix in filenames enables queue ordering. The daemon and pickup scripts respect these priorities, allowing urgent tasks to jump ahead of routine work. Priority values sort lexicographically, with `00` representing highest priority.

## Key Implementation Files

| File | Role |
|------|------|
| [`swarmforge/handoff-protocol.md`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/handoff-protocol.md) | Complete specification covering file naming, headers, validation rules, and lifecycle requirements |
| [`swarmforge/scripts/swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarm_handoff.sh) | Outbound gate handling draft validation, sequence updates, and atomic file writes |
| `swarmforge/scripts/handoffd.bb` | Delivery daemon watching outbox directories and managing inbox distribution |
| [`swarmforge/scripts/ready_for_next.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/ready_for_next.sh) | Inbound processor selecting and activating handoffs for idle agents |
| [`swarmforge/scripts/done_with_current.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/done_with_current.sh) | Completion handler archiving work and triggering return handoffs |
| `swarmforge/scripts/handoff_lib.bb` | Shared library for header parsing, timestamp generation, and common utilities |

## Practical Usage Example

The following workflow demonstrates an architect handing off frontend build work to a coder, who then returns the completed state:

```bash

# Create a draft git_handoff

cat > /tmp/handoff.txt <<'EOF'
type: git_handoff
task: build-frontend
from: architect
to: coder,cleaner,QA
priority: 00
commit: a1b2c3d4e5
EOF

# Validate and queue (creates atomic handoff file)

swarm_handoff.sh /tmp/handoff.txt

# (Daemon automatically delivers to coder's inbox/new)

# Coder accepts the work

ready_for_next.sh

# Coder completes work and commits

git commit -am "frontend built"
NEW_COMMIT=$(git rev-parse --short=10 HEAD)

# Create return handoff

cat > /tmp/return.txt <<EOF
type: git_handoff
task: build-frontend
from: coder
to: architect
priority: 00
commit: ${NEW_COMMIT}
EOF
swarm_handoff.sh /tmp/return.txt

# Mark original handoff complete

done_with_current.sh

```

## Summary

- **SwarmForge's handoff protocol** replaces mutable logbooks with atomic file operations in `.swarmforge/handoffs/`.
- **Five strict steps** govern the lifecycle: draft, validate/queue, daemon delivery, acceptance, and completion.
- **Atomic filenames** encode priority, timestamp, sequence, sender, and recipients to ensure deterministic ordering without race conditions.
- **Two handoff types**—`git_handoff` (10-char commit reference) and `note` (message-only)—support different coordination needs.
- **Three core scripts** ([`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh), [`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)) and one daemon (`handoffd.bb`) implement the protocol according to the specification in [`handoff-protocol.md`](https://github.com/unclebob/swarm-forge/blob/main/handoff-protocol.md).

## Frequently Asked Questions

### How does the protocol handle concurrent handoff creation?

The [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) script maintains a per-worktree sequence counter that it atomically increments when writing to the outbox. Combined with ISO timestamps in filenames, this ensures that even if two agents create handoffs simultaneously, each receives a unique, sortable filename, preventing collisions and preserving deterministic ordering.

### What happens if the handoff daemon crashes?

Because the protocol stores all state in the filesystem rather than memory, a crashed `handoffd.bb` simply stops delivery but does not lose data. When restarted, the daemon rescans the `outbox/` directory and processes any handoffs it missed, copying them to recipient inboxes and writing the appropriate delivery headers.

### Can agents reject or defer incoming handoffs?

Agents control acceptance through the [`ready_for_next.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next.sh) script, which they invoke only when idle. By not running this script, an agent effectively defers work. Additionally, agents can specify `batch` mode to accept multiple equal-priority handoffs at once, or `task` mode to accept single items, effectively filtering the queue based on workload capacity.

### Why does the protocol require 10-character commit abbreviations?

The 10-character requirement in `git_handoff` types prevents ambiguity from shorter abbreviations that might resolve to multiple commits in large repositories. The daemon validates that the abbreviation expands to exactly one commit object before queuing the handoff, ensuring recipients check out the correct state without hash collisions.