# How `swarm_handoff.sh` Validates Hand‑off Draft Files: A Deep Dive into SwarmForge's 7-Step Pipeline

> Uncover the validation logic in swarm_handoff.sh for handoff drafts. Discover SwarmForge's 7-step pipeline checking syntax, semantics, type compatibility, and Git integrity.

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

---

**The validation logic in [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) is actually implemented in the accompanying `swarm_handoff.bb` Clojure/Babashka script, which runs a deterministic 7-step pipeline checking syntax, semantics, type compatibility, and Git integrity before queuing any hand‑off.**

[`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) in the **unclebob/swarm-forge** repository serves as a thin Bash wrapper that delegates all work to `swarm_handoff.bb`. Understanding this validation logic is essential for developers creating hand‑off drafts that pass inspection and reach the outbox. Each validation step in `swarm_handoff.bb` is designed to catch specific categories of errors before malformed files propagate through the SwarmForge system.

## Where the Validation Actually Lives

The Bash wrapper at [`swarmforge/scripts/swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarm_handoff.sh) contains no validation logic itself—it simply passes arguments to `swarm_handoff.bb`. All draft validation happens in the Babashka script at `swarmforge/scripts/swarm_handoff.bb`, specifically within functions `parse-draft`, `validate-recipients`, `validate`, and their helper functions.

## Step 1: Parse the Draft File for Syntax Errors

The `parse-draft` function (lines 93–136) reads the hand‑off draft line‑by‑line and extracts field/value pairs. It collects multiple syntax errors rather than failing on the first:

- **Missing separator**: Fields must use `": "` (colon space)
- **Empty fields**: Neither field names nor values can be blank
- **Duplicate headers**: Each header may appear only once
- **Reserved fields**: Fields like `from`, `timestamp`, and `sequence` are reserved for system use
- **Unknown fields**: Any field not in the allowed set is rejected

This greedy error collection lets users fix multiple issues in one pass.

```bash

# Example syntax error caught by parse-draft

$ ./swarm_handoff.sh malformed.handoff
HANDOFF INVALID: malformed.handoff

Errors:
- Line 3: Missing ': ' separator.
- Duplicate header 'priority'.
- Unknown field 'urgency'.

```

## Step 2: Validate Recipients in the `to` Header

The `validate-recipients` function (lines 138–156) processes the comma‑separated `to` field with four specific checks per recipient:

1. **Empty recipient** – no blank entries between commas
2. **Underscores** – illegal in recipient names
3. **Duplication** – no repeated recipients allowed
4. **Unknown role** – each recipient must exist in `roles.tsv` (checked via `role-known?`)

The `roles.tsv` file at `.swarmforge/roles.tsv` provides the canonical list of valid roles.

## Step 3: Check Required Fields and Base Constraints

In the `validate` function (lines 195–202), the `base-errors` construction ensures:

- `type`, `to`, and `priority` are all present
- `type` matches allowed values: `git_handoff` or `note`
- `priority` conforms to the two‑digit pattern (e.g., `01`, `12`)

## Step 4: Apply Type‑Specific Header Validation

Depending on the `type` value, additional validation runs via `git-errors` (lines 211–222) or `note-errors` (lines 224–229):

**`git_handoff` requirements:**
- `commit` header must be present and resolvable to a valid Git object
- `task` header required, with length restrictions
- Commit SHA canonicalized to short‑hash via `canonical-commit`

**`note` requirements:**
- `message` header required, with length validation

## Step 5: Enforce Field‑Type Compatibility

The `field-errors` comprehension (lines 181–194) ensures headers only appear when appropriate to their `type`. For example, `task` and `commit` are illegal in a `note` hand‑off, while `message` cannot appear in a `git_handoff`.

This prevents semantic confusion where fields meant for one hand‑off type leak into another.

## Step 6: Canonicalize Git Commits

The `canonical-commit` function (lines 157–172) provides Git integrity validation:

1. Runs `git rev-parse --disambiguate` to locate a unique object for the given SHA fragment
2. Verifies the object is actually a commit (not a tree or blob)
3. Returns the canonical short‑hash for consistent identification

This ensures hand‑offs reference real, reachable commits in the repository.

## Step 7: Aggregate and Report All Errors

The final return of `validate` (lines 235–236) combines parsing errors, recipient errors, field‑type errors, and type‑specific errors into a single vector. The `-main` function at the bottom of `swarm_handoff.bb` orchestrates the flow:

1. Parse → `parse-draft`
2. Validate → `validate` (with all helper functions)
3. If errors exist → print `error-report` and exit with status **2**
4. If clean → write hand‑off to `.swarmforge/handoffs/outbox/`

## Valid vs. Invalid Hand‑off Examples

**Valid `git_handoff` draft:**

```text
type: git_handoff
to: reviewer,qa
priority: 05
task: improve-login-flow
commit: a1b2c3d4e5

```

```bash
$ ./swarm_handoff.sh example.handoff
HANDOFF QUEUED: /path/to/.swarmforge/handoffs/outbox/05_20241012T083045Z_000123_from_alice_to_reviewer_qa.handoff

```

**Invalid draft (missing `commit`, duplicate recipient):**

```text
type: git_handoff
to: reviewer,reviewer
priority: 09
task: refactor-auth

```

```bash
$ ./swarm_handoff.sh bad.handoff
HANDOFF INVALID: bad.handoff

Errors:
- Missing required header 'commit' for git_handoff.
- Duplicate recipient 'reviewer'.

```

Exit status **2** prevents queuing malformed hand‑offs.

## Key Source Files for Hand‑off Validation

| File | Purpose |
|------|---------|
| [`swarmforge/scripts/swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/swarm_handoff.sh) | Bash wrapper forwarding to Babashka script |
| `swarmforge/scripts/swarm_handoff.bb` | Core validation pipeline with all functions above |
| `.swarmforge/roles.tsv` | Canonical role list for recipient validation |
| [`swarmforge/handoff-protocol.md`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/handoff-protocol.md) | Protocol specification for hand‑off format |

## Summary

The [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) validation logic—implemented in `swarm_handoff.bb`—provides comprehensive draft checking through seven distinct steps:

- **Syntax validation** via `parse-draft` catches formatting errors
- **Recipient validation** via `validate-recipients` enforces role existence and naming rules
- **Base field requirements** ensure mandatory headers are present and well‑formed
- **Type‑specific rules** apply appropriate constraints for `git_handoff` vs. `note`
- **Field‑type compatibility** prevents inappropriate header mixing
- **Git integrity checks** via `canonical-commit` validate commit references
- **Aggregated error reporting** gives complete feedback before any exit

This pipeline guarantees that only syntactically correct, semantically valid, and type‑appropriate hand‑offs reach the SwarmForge outbox.

## Frequently Asked Questions

### What exit status does [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) return for invalid drafts?

Exit status **2**. The script explicitly exits with code 2 when validation errors are found, after printing the `error-report`. Successful validation results in normal exit status 0 and the hand‑off being queued to the outbox.

### Can I use underscores in recipient names according to [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) validation?

No. The `validate-recipients` function explicitly rejects any recipient containing underscores. This is one of the four checks applied to each comma‑separated entry in the `to` header, alongside emptiness, duplication, and role existence in `roles.tsv`.

### Why does [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) delegate to a Babashka script rather than pure Bash?

The validation requirements—structured parsing, error aggregation, Git integration, and type‑dependent logic—are complex enough to benefit from Clojure's data structures and libraries. `swarm_handoff.bb` handles this elegantly while [`swarm_handoff.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarm_handoff.sh) provides a familiar CLI entry point and handles environment setup.