Understanding the Handoff Daemon (handoffd.bb) in SwarmForge
The handoff daemon (handoffd.bb) is the core background service in SwarmForge that continuously monitors role-specific outboxes, routes handoff messages between distributed work-trees, manages approval workflows for git handoffs, and synchronizes the project task board.
The SwarmForge project (unclebob/swarm-forge) relies on handoffd.bb to act as the central message router that keeps multiple role-based repositories synchronized. Written in Babashka, this daemon operates as a persistent process (or one-shot scanner) that bridges communication gaps between isolated work-trees without requiring manual file copying.
Configuration and Daemon Initialization
When handoffd.bb starts, the configure! function parses command-line arguments to determine operational mode. The daemon accepts either a path for continuous operation or the --once flag for single-scan execution. During initialization, it establishes critical filesystem paths for the project state, role definitions, tmux socket location, and logging directories.
According to the source code in swarmforge/scripts/handoffd.bb, the configuration phase sets up:
- The daemon folder for PID and log files (
.swarmforge/daemon/) - Path resolution for the tmux socket used for notifications
- Role table loading via
load‑rolesfrom.swarmforge/roles.tsv
The Polling Loop and Message Detection
In continuous mode, the run-daemon! function implements the main event loop. The daemon repeatedly invokes poll-once! followed by a configurable sleep interval (poll-ms), creating a lightweight polling mechanism that minimizes resource usage while maintaining responsiveness.
The poll-once! function (lines 99-107) performs three critical actions:
- Loads the role table to understand the project topology
- Reads the current tmux socket configuration
- Recursively scans all
.handofffiles in each role's outbox directory and the project-wide outbox
This scanning approach ensures that any role—from master to specialized development roles—can deposit handoff files that the daemon will detect and route.
Message Processing and Delivery Pipeline
For each detected handoff file, the daemon calls process-outbox-file!, which initiates a sophisticated delivery workflow defined in deliver! (lines 50-71).
The Delivery Workflow
The deliver! function executes several sequential operations:
- Parsing: Extracts headers using
parse-messageto determine routing (from, to, type) - Board Updates: For
git_handofftypes, triggersupdate-board!which callspack_board.shto move tasks between lanes on the physical board - Routing: Copies the message to each recipient's inbox (
target-path) and appends delivery timestamps viaadd‑delivery‑headers - Notification: Sends tmux alerts to recipient sessions using
notify! - Archival: Moves the original to the sender's sent folder and archives the sender's board state if needed
Approval and Hold Logic
Not all handoffs proceed immediately. The should-hold? function (lines 92-98) implements a gatekeeping mechanism specifically for git_handoff messages originating from the master role. When such a handoff targets a single recipient who hasn't yet approved the change, the daemon moves the file to the pending_approval directory via hold!, preventing premature delivery until explicit approval is granted.
Task Board Integration
The handoff daemon maintains synchronization between code changes and project management through update-board!. This function invokes pack_board.sh to manipulate the task board data stored in .swarmforge/board/tasks.tsv, automatically moving tasks between columns (such as from "In Progress" to "Done") when associated handoffs are processed.
Running the Handoff Daemon
The daemon supports two operational modes depending on your workflow requirements.
Continuous mode (default behavior):
# From the root of a SwarmForge project
./swarmforge/scripts/handoffd.bb /path/to/project
One-shot execution (ideal for CI/CD pipelines):
./swarmforge/scripts/handoffd.bb --once /path/to/project
Manual handoff creation for testing:
cat > .swarmforge/handoffs/outbox/example.handoff <<EOF
id: 12345
from: master
to: dev
type: git_handoff
task_id: task-42
message: Deploy latest changes
EOF
Monitoring daemon activity:
tail -f .swarmforge/daemon/handoffd.log
Graceful Shutdown and Process Management
The daemon implements clean shutdown semantics through shutdown! and the should-stop? predicate. When a stop file is detected or an internal flag is set, the daemon:
- Completes the current polling cycle
- Removes its PID file
- Writes final entries to
.swarmforge/daemon/handoffd.log
This ensures that no handoff files are left in a partially processed state during restart or deployment operations.
Summary
- The handoff daemon (
handoffd.bb) serves as the central message bus for SwarmForge, routing.handofffiles between role-specific work-trees. - Continuous polling via
poll-once!andrun-daemon!ensures real-time message delivery with configurable intervals. - Approval workflows prevent master-to-single-recipient git handoffs from auto-delivering until explicitly approved.
- Board synchronization occurs automatically through
update-board!and thepack_board.shhelper script. - Tmux integration provides real-time notifications to developers when handoffs arrive in their inboxes.
Frequently Asked Questions
What triggers the handoff daemon to process a message?
The daemon processes messages based on filesystem polling. When poll-once! detects .handoff files in any role's outbox directory (.swarmforge/handoffs/outbox/), it immediately queues them for processing through process-outbox-file!. Unlike event-driven systems, SwarmForge uses intentional polling to avoid filesystem watcher limitations across different operating systems.
How does the daemon handle messages that require approval?
When should-hold? determines a git handoff from the master role requires approval (typically when sent to a single recipient who hasn't approved), the daemon moves the file to pending_approval/ using hold! instead of calling deliver!. The message remains there until manual approval moves it back to the outbox or the recipient status changes.
Can I run the handoff daemon without continuous polling?
Yes. Pass the --once flag when invoking the script. This executes a single scan of all outboxes, processes any pending handoffs, and immediately exits. This mode is particularly useful in automated CI pipelines where you want to trigger handoff processing as a discrete step rather than maintaining a background process.
Where does the daemon store its logs and PID information?
The daemon writes operational logs to .swarmforge/daemon/handoffd.log and maintains its process ID in .swarmforge/daemon/handoffd.pid. These paths are established during the configure! phase and are used by shutdown! to manage graceful termination and debug logging.
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 →