# How `merge_and_process.sh` and `swarm_handoff.sh` Interact During a Swarm Handoff

> Understand how merge_and_process.sh and swarm_handoff.sh interact during a swarm handoff. Learn how they merge configurations and signal completion for the daemon.

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

---

**[`merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/merge_and_process.sh) sources [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) to consume handoff artifacts, merges incoming swarm configuration, and signals completion via a shared `.handoff` status file that the handoff daemon monitors.**

The `unclebob/swarm-forge` repository implements a deterministic handoff protocol through two core shell scripts that coordinate the transfer of swarm state between nodes. Understanding how [`merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/merge_and_process.sh) and [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) interact during handoff is essential for debugging handoff failures and extending the protocol. This article examines their four-step coordination pipeline, shared environment contract, and file-based signaling mechanism.

## The Four-Step Handoff Pipeline

The interaction between these scripts follows a strict producer-consumer pattern with feedback signaling.

### Step 1: [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) Initiates the Handoff

[`swarmforge/scripts/swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarm_handoff.sh) begins the handoff by packaging the current swarm state—tasks, logs, and environment—into a temporary payload directory. It spawns the `handoffd` daemon and writes a `.handoff` metadata file containing:

- The daemon's **PID**
- The **temporary directory** path for the payload
- A **status flag** (`IN_PROGRESS`)

At this stage, [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) operates independently. No direct call to [`merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/merge_and_process.sh) occurs; the script simply creates the artifacts that the merge process will later consume.

### Step 2: [`merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/merge_and_process.sh) Sources and Validates

When execution reaches [`swarmforge/scripts/merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/merge_and_process.sh), it **sources [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh)** directly:

```bash
source "$SWARM_FORGE_PATH/scripts/swarm_handoff.sh"

```

This sourcing provides access to helper functions defined in [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh), including:

- `handoff_cleanup()` — removes temporary directories
- `handoff_success?()` — validates handoff completion status

[`merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/merge_and_process.sh) reads the `.handoff` file, verifies the `IN_PROGRESS` status, and confirms the payload was correctly produced before proceeding.

### Step 3: Merge, Process, and Signal Completion

After validation, [`merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/merge_and_process.sh) performs three operations:

1. **Merges** the incoming swarm configuration (new lieutenant definitions, updated task queues) into the local repository
2. **Normalizes** the merged data—re-indexing tasks, updating the constitution, rebuilding `forge.bb` artifacts
3. **Writes** `DONE=1` into the `.handoff` file to signal completion

This step relies on environment variables exported by [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh): `SWARM_FORGE_PATH` and `HANDOFF_DIR`. Both scripts share identical views of the swarm state through these variables.

### Step 4: The Handoff Daemon Completes Cleanup

The `handoffd` daemon—spawned in Step 1—runs a watchdog loop monitoring the `.handoff` file. When it detects `DONE=1` written by [`merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/merge_and_process.sh), it:

- Removes the temporary directory
- Signals the original swarm that handoff is complete
- Exits cleanly

This tight coupling ensures the handoff only finalizes after the merge has been fully applied.

## Environment Contract and Shared State

The scripts coordinate through a minimal, well-defined contract:

| Element | Purpose | Set By |
|---------|---------|--------|
| `SWARM_FORGE_PATH` | Base installation directory | [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) |
| `HANDOFF_DIR` | Temporary payload directory | [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) |
| `.handoff` file | Status file with PID, paths, flags | [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) (write), [`merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/merge_and_process.sh) (update) |

This design avoids complex IPC—both scripts read from and write to the same filesystem location, making the protocol resilient to network interruptions and easy to inspect manually.

## Practical Code Examples

Initiate a handoff from a swarm node:

```bash
./swarmforge/scripts/swarm_handoff.sh start

```

This creates `.handoff` with `STATUS=IN_PROGRESS` and launches `handoffd`. After the daemon finishes payload preparation, run the merge:

```bash
./swarmforge/scripts/merge_and_process.sh

```

The second command sources [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh), performs the merge, and writes `DONE=1`. The daemon detects this automatically and cleans up—no additional command required.

## Key Source Files

| File | Role in Handoff Interaction |
|------|----------------------------|
| [`swarmforge/scripts/swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarm_handoff.sh) | Producer: sets up payload, spawns daemon, exports environment |
| [`swarmforge/scripts/merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/merge_and_process.sh) | Consumer: sources helper functions, merges state, signals completion |
| `swarmforge/scripts/handoffd` | Runtime-generated daemon; watches `.handoff` for `DONE` flag |
| [`swarmforge/scripts/ready_for_next.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/ready_for_next.sh) | Post-handoff: resumes normal swarm operation |

The protocol specification in [`swarmforge/handoff-protocol.md`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/handoff-protocol.md) documents this contract formally for implementers extending the system.

## Summary

- **[`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh)** prepares handoff artifacts and spawns a monitoring daemon without directly invoking the merge process
- **[`merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/merge_and_process.sh)** sources [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) to reuse validation functions and environment variables, then consumes the prepared payload
- **The `.handoff` file** serves as the contract: [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) writes initial state, [`merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/merge_and_process.sh) updates with `DONE=1`, and `handoffd` observes the change
- **Environment variables** (`SWARM_FORGE_PATH`, `HANDOFF_DIR`) ensure both scripts operate on identical paths without hardcoded dependencies

## Frequently Asked Questions

### Does [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) directly execute [`merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/merge_and_process.sh)?

No. [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) creates artifacts and exits after spawning `handoffd`. [`merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/merge_and_process.sh) runs separately—either manually or via automation—and sources [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) only for helper functions, not to trigger execution. This decoupling allows flexible deployment topologies.

### What happens if [`merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/merge_and_process.sh) fails before writing `DONE=1`?

The `handoffd` daemon continues monitoring indefinitely. The swarm remains in handoff state until the daemon is killed manually or the `.handoff` file is repaired. This blocking behavior prevents partial merges from corrupting swarm state.

### Why does [`merge_and_process.sh`](https://github.com/unclebob/swarm-forge/blob/main/merge_and_process.sh) source rather than execute [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh)?

Sourcing preserves the environment variable exports and function definitions in the current shell context. Executing as a subprocess would isolate these definitions, forcing duplication of path logic and increasing maintenance burden across the repository.

### Can the handoff protocol operate across network-mounted filesystems?

Yes. The file-based contract relies only on atomic write visibility, not local filesystem specifics. As long as `$HANDOFF_DIR` and `.handoff` reside on a filesystem visible to both nodes (NFS, distributed storage, etc.), the signaling mechanism functions identically.