# What Is the Handoff Daemon (handoffd) in Swarm‑Forge?

> Discover the handoff daemon handoffd in Swarm-Forge. Learn how this background process manages handoff files, synchronizes the Kanban board, and notifies agents of new tasks.

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

---

**The handoff daemon (`handoffd`) is the central background process that watches for handoff files, routes them to recipient role inboxes, synchronizes the Kanban board, and notifies agents when new tasks are available.**

In the **Swarm‑Forge** repository (`unclebob/swarm-forge`), `handoffd` serves as the orchestration backbone for multi-agent workflows. It enables autonomous roles—**Coder**, **Cleaner**, **QA**, and others—to pass work to each other through a file-based protocol without direct coordination. This article explains the daemon's architecture, lifecycle, and integration points based on the source code implementation.

---

## Core Responsibilities of handoffd

The daemon performs four critical functions that keep the Swarm‑Forge pipeline moving:

### Outbox Monitoring

`handoffd` continuously scans the `.swarmforge/daemon/outbox/` directory for new handoff files. These JSON files are created by agents when they complete a task and need to transfer ownership. The daemon uses a polling loop (implemented in `handoffd.bb`) to detect files that have finished being written.

### Validation and Routing

Each handoff file is parsed using functions from [`handoff_lib.bb`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/handoff_lib.bb). The daemon:

- Extracts the `recipients` list from the handoff headers
- Determines the target role inboxes under `.swarmforge/role-inbox/`
- Copies the validated handoff to each recipient's directory

Invalid or malformed handoffs are logged to `.swarmforge/daemon/handoffd.log` and left in a quarantine subdirectory for inspection.

### Board Synchronization

After successful delivery, `handoffd` updates the Kanban board via the board API. According to [[`handoff-protocol.md`](https://github.com/unclebob/swarm-forge/blob/main/handoff-protocol.md)](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/handoff-protocol.md), terminal handoffs (those with no downstream recipients) trigger the card to move to the **Done** lane. Non-terminal handoffs advance the card to the next role's lane.

### Agent Notification

Rather than deliver the full payload to agents, `handoffd` emits a lightweight **wake‑up signal**—a tmux ping sent to the recipient agent's session. This notification pattern keeps agents decoupled; they poll their own inbox when awakened rather than maintaining persistent connections to the daemon.

---

## Running the Handoff Daemon

### Standard Operation

The daemon is launched automatically by [`swarmforge.bb`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarmforge.bb) when a project is opened. It writes its process ID to `.swarmforge/daemon/handoffd.pid` and runs until explicitly stopped.

```bash

# The daemon starts automatically with:

bb swarmforge/scripts/swarmforge.bb /path/to/project

# Manual start (if needed):

bb swarmforge/scripts/handoffd.bb /path/to/project

```

### Single-Pass Mode

For testing or CI pipelines, run `handoffd` with `--once` to process pending handoffs and exit:

```bash
bb swarmforge/scripts/handoffd.bb --once /path/to/project

```

This mode is used by the test suite to verify handoff routing without leaving a persistent process.

### Stopping the Daemon

```bash
bb swarmforge/scripts/stop_handoff_daemon.bb /path/to/project

```

The stop script reads the PID file, sends **SIGTERM**, and cleans up the socket and lock files.

---

## Programmatic Integration

Test suites and custom tooling can invoke `handoffd` through Babashka process functions:

```clojure
(require '[babashka.process :refer [process check]])

(defn handoffd-once [project-root]
  (-> (process ["bb" "swarmforge/scripts/handoffd.bb" 
                "--once" 
                (str project-root)]
               {:inherit true})
      check))

```

The `handoff_lib.bb` namespace provides lower-level functions for parsing handoffs without running the full daemon:

```clojure
(require '[swarmforge.scripts.handoff-lib :as hl])

;; Parse a handoff file directly
(hl/parse-handoff "/project/.swarmforge/daemon/outbox/handoff-123.json")
;; => {:sender "coder"
;;     :recipients ["cleaner"]
;;     :task-id "TASK-456"
;;     :payload {...}}

```

---

## Key Source Files

| File | Purpose | Location |
|------|---------|----------|
| `handoffd.bb` | Main daemon implementation with polling loop and lifecycle management | [`swarmforge/scripts/handoffd.bb`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/handoffd.bb) |
| `handoff_lib.bb` | Shared library for handoff parsing, recipient resolution, and message rendering | [`swarmforge/scripts/handoff_lib.bb`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/handoff_lib.bb) |
| `swarmforge.bb` | Project entry point that starts `handoffd` among other agents | [`swarmforge/scripts/swarmforge.bb`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarmforge.bb) |
| `stop_handoff_daemon.bb` | Graceful shutdown utility | [`swarmforge/scripts/stop_handoff_daemon.bb`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/stop_handoff_daemon.bb) |
| [`handoff-protocol.md`](https://github.com/unclebob/swarm-forge/blob/main/handoff-protocol.md) | Specification of file format and daemon behavior | [[`swarmforge/handoff-protocol.md`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/handoff-protocol.md)](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/handoff-protocol.md) |

---

## Summary

- **handoffd** is the file-based message broker of Swarm‑Forge, enabling agent-to-agent handoffs without direct coupling
- It guarantees **exactly-once delivery** per handoff through atomic file moves and PID-based singleton enforcement
- The daemon integrates with the **Kanban board** to visualize workflow state
- Run it in **daemon mode** for production or **single-pass mode** for testing
- All handoff logic is centralized in `handoff_lib.bb`, making the daemon itself a thin orchestration layer

---

## Frequently Asked Questions

### How does handoffd prevent duplicate handoff processing?

`handoffd` uses **atomic file operations**—handoffs are moved from `outbox/` to `processing/` before parsing, and only deleted after successful delivery. If the daemon crashes mid-operation, orphaned files in `processing/` are re-scanned on restart and reprocessed idempotently.

### Can multiple handoffd instances run on the same project?

**No.** The daemon acquires an exclusive lock on a tmux socket at `.swarmforge/daemon/handoffd.sock`. Subsequent startup attempts detect the lock and exit with an error. This singleton pattern prevents race conditions when multiple agents write to shared inboxes.

### What happens if a recipient role has no running agent?

The handoff remains in the recipient's inbox directory indefinitely. `handoffd` does not implement timeout or retry logic—availability is the responsibility of each role's agent. The board still updates to show the task as "waiting," and manual intervention can reassign stuck handoffs through the board interface.

### Is handoffd required for single-agent workflows?

**Not strictly.** Tools can call `handoff_lib.bb` functions directly to create and consume handoffs. However, `handoffd` is required for **board synchronization** and **cross-agent wake‑up notifications**. Without it, the Kanban view falls out of sync and agents must poll inboxes aggressively.