# How the SwarmForge Daemon Validates Delivery Targets: A Complete Code Walkthrough

> Understand how the SwarmForge daemon validates delivery targets. Learn about header parsing, duplicate detection, role verification, and more in this code walkthrough.

- Repository: [Robert C. Martin/swarm-forge](https://github.com/unclebob/swarm-forge)
- Tags: deep-dive
- Published: 2026-09-02

---

**The SwarmForge daemon validates delivery targets by parsing the `to` header, checking for empty entries, banning underscores, detecting duplicates, and verifying each role exists in the repository's role definitions.**

The SwarmForge daemon is responsible for routing **handoff files** between AI agents in a multi-agent coding workflow. Before any handoff reaches its destination, the daemon enforces strict validation rules on the delivery targets specified in the `to` header. This article examines the exact validation logic implemented in `unclebob/swarm-forge`.

## The `validate-recipients` Function

All delivery target validation occurs in the **`validate-recipients`** function within [`swarmforge/scripts/swarm_handoff.bb`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarm_handoff.bb). This function is invoked as part of the broader `validate` function that checks handoff headers before delivery proceeds.

The validation follows six sequential steps:

### Step 1: Parse the `to` Header

The daemon extracts the raw string from the `to` header and splits it on commas to obtain individual recipient role names.

```clojure
;; From swarm_handoff.bb:50
(defn validate-recipients [to]
  (let [recipients (str/split to #"," -1)] ;; split on commas, keep empty
    ;; ... validation continues

```

The `-1` argument to `str/split` preserves empty strings, enabling explicit detection of malformed entries.

### Step 2: Detect Empty Recipients

If any entry is an empty string, the daemon flags an error:

```clojure
;; From swarm_handoff.bb:55
(when (str/blank? recipient)
  (conj errors "Header 'to' contains an empty recipient."))

```

This catches cases like `to: "coder,,reviewer"` where double commas create an empty slot.

### Step 3: Disallow Underscores in Role Names

Role names may **not** contain underscores. This is a deliberate naming convention enforced by the protocol.

```clojure
;; From swarm_handoff.bb:58
(when (str/includes? recipient "_")
  (conj errors (format "Recipient role '%s' is invalid; role names may not contain underscores." recipient)))

```

This ensures role names use hyphens (`senior-coder`) rather than underscores (`senior_coder`).

### Step 4: Detect Duplicate Recipients

The function maintains a **`seen`** set to catch repeated role names:

```clojure
;; From swarm_handoff.bb:60
(when (contains? seen recipient)
  (conj errors (format "Duplicate recipient '%s'." recipient)))

```

Duplicates are rejected even if the role is otherwise valid.

### Step 5: Verify Role Existence

For every non-blank, non-duplicate role, the daemon calls **`role-known?`**—a lookup against the repository's role definitions:

```clojure
;; From swarm_handoff.bb:62
(when (and (not (str/blank? recipient))
           (not (role-known? recipient)))
  (conj errors (format "Unknown recipient role '%s'." recipient)))

```

The `role-known?` function checks against files like [`roles/lieutenant.prompt`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/roles/lieutenant.prompt) to confirm the role is defined.

### Step 6: Return Validated List

After processing all recipients, the function returns a vector for further handling:

```clojure
;; Structure built in validate (swarm_handoff.bb:52-63)
[recipients errors] ;; extracted and wrapped in {:recipients ... :errors ...}

```

The parent `validate` function incorporates these into the overall handoff validation result.

## Daemon Execution Flow

The validation hook is in [`swarmforge/scripts/handoffd.bb`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/handoffd.bb), the handoff daemon itself. The execution flow is:

1. **Parse handoff file** → headers map
2. **Call `swarm-handoff/validate`** (which invokes `validate-recipients`)
3. **If errors** → `log!` the failure and move file to `failed/`
4. **If OK** → `add-delivery-headers` and copy to each recipient's inbox

Only when the recipient list passes every check does the daemon proceed with delivery.

## Practical Example

```clojure
;; Validating a handoff's "to" header
(require '[swarm-handoff :as sh])

(let [{:keys [recipients errors]} (sh/validate
                                   {"type" "git_handoff"
                                    "to"   "coder,reviewer,invalid_role"}
                                   [:type :to])]
  (if (empty? errors)
    (println "Delivery targets OK:" recipients)
    (println "Validation failed:" errors)))
;; → Validation failed: ("Unknown recipient role 'invalid_role'.")

```

Multiple errors can accumulate. For example, `to: "coder,_senior,coder"` would produce:

- `"Recipient role '_senior' is invalid; role names may not contain underscores."`
- `"Duplicate recipient 'coder'."`

## Key Source Files

| File | Purpose |
|------|---------|
| [`swarm_handoff.bb`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarm_handoff.bb) | Contains `validate-recipients` and the overall `validate` function |
| [`handoffd.bb`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/handoffd.bb) | Daemon that reads outbound handoffs and orchestrates delivery |
| [[`handoff-protocol.md`](https://github.com/unclebob/swarm-forge/blob/main/handoff-protocol.md)](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/handoff-protocol.md) | Protocol specification requiring target validation |
| [`roles/*.prompt`](https://github.com/unclebob/swarm-forge/tree/main/swarmforge/roles) | Role definitions used by `role-known?` |

## Summary

- **Entry point**: `validate-recipients` in `swarm_handoff.bb` handles all delivery target validation
- **Parsing**: Comma-split with empty-string preservation using `str/split`
- **Syntax rules**: No empty entries, no underscores in role names
- **Semantic rules**: No duplicates, all roles must be known
- **Failure handling**: Errors logged via `log!`, handoff moved to `failed/`
- **Success handling**: `add-delivery-headers` adds recipient header and timestamps before inbox delivery

## Frequently Asked Questions

### What happens if a handoff has an unknown recipient role?

The daemon logs the error `"Unknown recipient role 'X'."` via `log!` in `handoffd.bb`, moves the handoff file to the `failed/` directory, and does not attempt delivery. The handoff must be corrected and resubmitted.

### Why does SwarmForge ban underscores in role names?

As enforced in `swarm_handoff.bb:58`, underscores are prohibited to maintain consistent naming conventions across the codebase. The protocol mandates hyphen-separated role names (`senior-coder`) rather than snake_case (`senior_coder`).

### Can a handoff be delivered to multiple recipients?

Yes. The `to` header accepts comma-separated role names. The daemon validates each recipient independently and, if all checks pass, copies the handoff into each recipient's inbox with appropriate `recipient` headers added by `add-delivery-headers`.

### Where is the role validity checked against actual role definitions?

The `role-known?` function performs this lookup. It references the repository's role definition files such as `roles/lieutenant.prompt` to confirm a role exists before permitting delivery.