What Is the Handoff Daemon (handoffd.bb) in SwarmForge?

The handoff daemon (handoffd.bb) is a babashka-based background service that automates task handoffs in SwarmForge by scanning outbox directories, validating .handoff message files, updating the project board, and delivering notifications to recipient tmux sessions.

The handoff daemon sits at the heart of SwarmForge's collaborative workflow. When team members working in different roles—such as "master," "specifier," or "implementer"—need to pass work to each other, they create .handoff files describing Git changes, task moves, or artifacts. The daemon, implemented in swarmforge/scripts/handoffd.bb, continuously monitors these handoff messages and orchestrates their delivery according to the SwarmForge protocol.

This article explains the daemon's architecture, key functions, and operational patterns drawn directly from the SwarmForge source code.

Core Responsibilities of the Handoff Daemon

The handoff daemon performs eight primary functions that together enable automated, turn-based collaboration:

Configuration and Path Setup

The configure! function establishes the daemon's operating environment. It parses command-line arguments including the optional --once flag, determines the project-root, and initializes paths for state-dir, daemon-dir, socket-path, and PID tracking.

;; From swarmforge/scripts/handoffd.bb
(configure! project-root)
;; Sets globals: project-root, state-dir, daemon-dir, etc.

This configuration runs at startup and ensures all file operations stay scoped to the correct project context.

Polling Loop and Execution Modes

The daemon operates in two execution modes controlled by the -main entry point:

Mode Trigger Behavior
Continuous daemon Default (no flags) run-daemon! loops indefinitely, calling poll-once! and sleeping for poll-ms milliseconds between iterations
Single-pass execution --once flag Executes poll-once! exactly once, then exits—ideal for CI pipelines or debugging

The run-daemon! function includes graceful shutdown handling through should-stop?, which checks for a stop flag file or internal termination signal.

Outbox Discovery and Message Collection

During each poll cycle, poll-once! gathers .handoff files from every role's outbox directory, including the master project root. It builds a comprehensive paths collection that the daemon will process sequentially.


# Typical outbox locations scanned by the daemon

./.swarmforge/handoffs/outbox/          # role-specific outboxes

./.swarmforge/handoffs/master/outbox    # master role outbox

Message Parsing and Validation

Each .handoff file undergoes parsing via parse-message, which splits the file into headers and body, returning a structured map with :headers and :body keys. The header section defines routing, task association, and approval status.

Example handoff file structure:

id: 12345
type: git_handoff
from: specifier
to: master
task_id: TASK-42
approved: true
message: Implement feature X with tests

<optional detailed body follows blank line>

Delivery Decision Logic

Before delivery, should-hold? evaluates whether a handoff requires manual approval. The daemon holds messages when:

  • The sender is a specifier (creates packs)
  • The sender holds the master role
  • The message has multiple recipients
  • The approved header is missing or false

Held handoffs remain in the outbox pending human review; approved messages proceed to deliver!.

Message Delivery and File Operations

The deliver! function executes the complete handoff sequence:

  1. Renders the final message with delivery timestamps
  2. Writes the handoff to the recipient's inbox directory
  3. Moves the source file to the sender's sent folder
  4. Updates the project board via update-board!
  5. Archives the sender's board entry via archive-sender!
  6. Notifies the recipient's tmux session via notify!

This atomic sequence ensures board state and file system remain synchronized.

Board State Management

SwarmForge maintains a visible task board that tracks work progress. The daemon integrates with board operations through two key functions:

  • update-board! – For Git handoffs, moves tasks between lanes (e.g., "In Progress" → "Review") or marks them complete
  • archive-sender! – Archives the originating role's board entry after successful delivery

Both functions invoke pack_board.bb (swarmforge/scripts/pack_board.bb) to apply board transformations.

Tmux Session Notification

The notify! function wakes recipient tmux panes by sending keystrokes that display a configurable wake-message. This ensures team members receive immediate, in-terminal alerts when work arrives.

;; Notification implementation from swarmforge/scripts/handoffd.bb
(notify! session message)
;; Sends tmux keystrokes to display wake-message

Operational Commands and Workflows

Starting and Stopping the Daemon


# Start continuous daemon for a project

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

# Single execution for testing or CI

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

Graceful Shutdown

Create the stop flag file to trigger clean termination:

touch /path/to/project/.swarmforge/daemon/stop

The daemon detects this file via should-stop?, removes its PID file, logs termination, and exits on the next poll cycle.

Log Inspection

Human-readable operational logs write to:

cat /path/to/project/.swarmforge/daemon/handoffd.log

Creating and Sending Handoffs

Roles initiate handoffs by writing .handoff files to their outbox:


# Example: specifier creates a handoff for master

cat > .swarmforge/handoffs/outbox/feature-x.handoff << 'EOF'
id: 2024-001
type: git_handoff
from: specifier
to: master
task_id: TASK-42
message: Specification complete; ready for implementation

Branch: spec/TASK-42-feature-x
Commits: 3 files changed, 47 insertions
EOF

The daemon automatically processes this file, delivers it to master's inbox, updates the board, and notifies the master tmux pane.

File Purpose
swarmforge/scripts/handoffd.bb Daemon implementation (primary source file)
swarmforge/scripts/handoff_lib.bb Shared library for handoff parsing and utilities
swarmforge/handoff-protocol.md Formal message format specification
swarmforge/scripts/pack_board.bb Board manipulation commands
swarmforge/scripts/ready_for_next.sh Post-notification task preparation helper

Summary

  • The handoff daemon (handoffd.bb) is a babashka script that runs continuously to automate SwarmForge's message-driven workflow
  • It polls outbox directories, parses .handoff files, and delivers messages to recipient inboxes while updating the project board
  • Approval routing via should-hold? ensures sensitive handoffs from specifiers and masters receive human review
  • Tmux notifications provide immediate, in-terminal alerts to receiving team members
  • The daemon supports continuous daemon mode for production use and --once execution for CI/testing scenarios
  • All board updates and archiving operations integrate with pack_board.bb to maintain consistent project state

Frequently Asked Questions

What triggers a handoff to be held for approval?

A handoff is held when should-hold? determines the sender is a specifier pack creator, holds the master role, addresses multiple recipients, or omits the approved: true header. These criteria prevent premature delivery of impactful changes without explicit authorization.

Can the daemon run without staying resident?

Yes. Pass the --once flag to execute a single poll cycle and exit immediately. This mode supports CI pipelines, pre-commit hooks, and diagnostic debugging without the overhead of a persistent process.

How does the daemon know which tmux session to notify?

The notify! function targets the recipient role's tmux session as specified in the handoff's to header. It sends keystrokes to that session's window, displaying the configured wake-message defined in the SwarmForge configuration.

Where are handoff files stored during processing?

Handoffs follow a three-stage lifecycle: they originate in the sender's outbox, move to the recipient's inbox upon delivery, and the source copy archives to the sender's sent folder. Failed or held handoffs remain in outbox pending resolution.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →