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
approvedheader 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:
- Renders the final message with delivery timestamps
- Writes the handoff to the recipient's inbox directory
- Moves the source file to the sender's sent folder
- Updates the project board via
update-board! - Archives the sender's board entry via
archive-sender! - 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 completearchive-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.
Related Source Files
| 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
.handofffiles, 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
--onceexecution for CI/testing scenarios - All board updates and archiving operations integrate with
pack_board.bbto 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →