What Is the Handoff Daemon (handoffd) in Swarm‑Forge?
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. The daemon:
- Extracts the
recipientslist 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/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 when a project is opened. It writes its process ID to .swarmforge/daemon/handoffd.pid and runs until explicitly stopped.
# 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:
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
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:
(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:
(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 |
handoff_lib.bb |
Shared library for handoff parsing, recipient resolution, and message rendering | swarmforge/scripts/handoff_lib.bb |
swarmforge.bb |
Project entry point that starts handoffd among other agents |
swarmforge/scripts/swarmforge.bb |
stop_handoff_daemon.bb |
Graceful shutdown utility | swarmforge/scripts/stop_handoff_daemon.bb |
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) |
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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →