13 KiB
SwarmForge — Handoff protocol and deterministic pipeline
Working document complementary to
swarmforge.md. Example based on the fullsix-packworkflow (specifier → coder → cleaner → architect → hardender → QA).
1. Handoff semantics (full example)
Task: "Implement a shopping cart with tax calculation" → stable task name: cart-tax. That name travels the entire chain unchanged.
1.1 The message contract
Only two message types exist, and only the headers the agent may write:
type: git_handoff → "I committed work; merge and process it"
to: coder
priority: 50 → 00 = urgent · 50 = normal · 99 = low
task: cart-tax → stable name that travels the chain
commit: 3f9a2c1d7e → canonical 10-hex hash (the gate validates and canonicalizes it)
type: note → short message (only if the constitution/role authorizes it)
to: architect
priority: 70
message: <1 line, max 80 chars>
Agents never write the payload or reserved headers (id, from, role, recipient, created_at, enqueued_at…): the tool generates all of that.
1.2 The specifier opens the chain
The specifier talks with you, writes features/cart.feature (Gherkin) + the end-to-end QA suite, and asks for your explicit approval. Only after your OK does it commit and write its draft:
type: git_handoff
to: coder
priority: 50
task: cart-tax
commit: 3f9a2c1d7e
It runs swarm_handoff.sh draft → the validation gate does 4 checks: coder is a known role, 50 is a valid priority, the commit resolves to exactly one object and is a commit (via git rev-parse --disambiguate), and there are no reserved fields or agent-written body. It generates the payload and installs it atomically in the outbox:
50_20260710T120000Z_000042_from_specifier_to_coder.handoff
The daemon (1 s polling) copies the file to the coder's inbox/new/ adding delivery headers, and wakes the coder by typing into its tmux pane: "You have new handoff mail. If idle, run ready_for_next.sh." + Enter.
The delivered file (this is what the coder sees):
id: 20260710T120000Z_000042_from_specifier
from: specifier
to: coder
recipient: coder ← added by the daemon (per-recipient copy)
priority: 50
type: git_handoff
role: specifier
task: cart-tax
commit: 3f9a2c1d7e
created_at: 2026-07-10T12:00:00Z
enqueued_at: 2026-07-10T12:00:01Z ← added by the daemon
Re-read your role and constitution.
merge_and_process specifier 3f9a2c1d7e
1.3 The coder consumes the task
The coder runs ready_for_next.sh → the helper moves the file from inbox/new/ to inbox/in_process/, adds dequeued_at, and prints:
TASK: .swarmforge/handoffs/inbox/in_process/50_..._from_specifier_to_coder.handoff
FROM: specifier
TYPE: git_handoff
PRIORITY: 50
TASK_NAME: cart-tax
PAYLOAD:
Re-read your role and constitution.
merge_and_process specifier 3f9a2c1d7e
The coder does merge_and_process specifier 3f9a2c1d7e (merge of the specification commit), applies TDD (unit tests first, then implementation), runs the acceptance tests generated from the Gherkin, commits with byline ("Implement cart tax" — By coder.) and forwards along the chain with the same task: cart-tax and its new commit. Key rule: an intermediate role ALWAYS forwards, no matter what (even if the change is format-only).
1.4 The cleaner in batch mode
The cleaner is configured in swarmforge.conf with batch. If 3 handoffs arrive from the coder at the same priority, ready_for_next_batch.sh groups them:
BATCH: .swarmforge/handoffs/inbox/in_process/batch_20260710T130000Z_000051
COUNT: 3
PRIORITY: 50
BATCH_ITEM: 1 → TASK_NAME: cart-tax ...
BATCH_ITEM: 2 → TASK_NAME: user-auth ...
BATCH_ITEM: 3 → TASK_NAME: cart-coupon ...
It processes all 3 as one cleanup pass: coverage, CRAP ≤ 6, DRY, mutation site scan (split files with >100 sites), acceptance + unit tests, commits, and forwards once to the architect.
1.5 The chain continues (architect → hardender → QA)
Each with the same mechanics: ready_for_next.sh (task or batch) → process its gate → verify → commit with byline → forward along the chain with task: cart-tax preserved.
1.6 QA closes: the "terminal broadcast"
When QA verifies everything (e2e UI suite, commit/manifest consistency, final CRAP/DRY), it commits and sends a single handoff to multiple recipients with priority: 00:
type: git_handoff
to: specifier,coder,cleaner,architect,hardender
priority: 00
task: cart-tax
commit: b4d8e2f1a0
This is the exception to the forwarding rule: each recipient does merge_and_process QA b4d8e2f1a0, runs its tests, and does NOT forward. The specifier, on receiving the broadcast, merges and asks you for the next feature. Chain closed.
1.7 Each task's state machine
inbox/new/ ──ready_for_next──► inbox/in_process/ ──done_with_current──► inbox/completed/
(daemon delivery) (+dequeued_at) (+completed_at)
done_with_current.shpicks up the next task or batch automatically if there is a queue → agents do not sit idle.- If a wake-up arrives while the agent is working → it is ignored; the queue is not lost because state lives in files.
- Swarm restart → agents re-run
ready_for_next.shand resume fromin_process.
2. Rules for a deterministic pipeline
Determinism does not come from one place: it comes from four layers of rules that reinforce each other.
2.1 Layer 1 — Shared rules (constitution)
workflow.prompt (work discipline):
- Each role works only in its assigned worktree/branch; forbidden to diff/merge foreign branches except via explicit handoff.
- Every commit carries a byline:
By <role>. - Temporary files under
./tmp/of the worktree, not/tmp. - If the expected git layout does not exist → stop and report, do not improvise.
handoffs.prompt (protocol):
- Only
git_handoffandnote; notes require explicit authorization. - On ambiguity/contradiction → stop and ask, do not send notes.
- Mandatory chain forwarding: each intermediate role forwards to the next stage after completing, even if the change is non-functional (format, manifests, metadata).
- Terminal broadcast = merge-only: recipients of the final handoff do not forward.
task:is preserved when forwarding; invent a stable name only for new work.- Forbidden to edit/add/commit handoff runtime state.
engineering.prompt (technical rules):
- TDD: unit tests first, then minimal production to pass.
- Quality tools (mutation/CRAP/DRY/coverage) run only on testable modules; "environmentally unsuitable" modules remain as excluded adapters.
- Acceptance via
gherkin-parser(APS) — forbidden to reimplement the parser. - Local verification before each handoff; verification commands never concurrent with each other.
- Guardrails: do not edit mutation manifests by hand; do not commit unrelated artifacts.
2.2 Layer 2 — Per-role rules (six-pack)
| Role | Owns | Does Not Own (boundary) | Verification before handoff | Handoff obligation |
|---|---|---|---|---|
| specifier | Gherkin + acceptance criteria + e2e QA suite | Does not run mutation or quality tools | Tests if needed; nothing more | Does not commit or forward without your approval. After your OK: commit + handoff to coder with invented task: |
| coder | Implementation of approved slices with TDD | QA suite, mutation, CRAP/DRY, Gherkin mutation | Unit tests + acceptance tests | Commit + handoff to cleaner |
| cleaner (batch) | Cleanup preserving behavior: names, duplication, boundaries, coverage | Mutation tests, Gherkin mutation, new behavior | CRAP ≤ 6, DRY, mutation site scan, acceptance + unit | Commit + handoff to architect before taking another task/batch |
| architect (batch) | Structure, boundaries, dependency direction, mutation hardening, DRY, property tests | — (inherits the chain) | Per-file mutation (differential), DRY, property tests, Gherkin soft | Commit + handoff to hardender |
| hardender (batch) | Mutation hardening (kill survivors), Gherkin mutation, final CRAP/DRY | Specifier's e2e QA suite | Mutation → Gherkin soft → CRAP → DRY | Commit + handoff to QA |
| QA (batch) | Independent final verification, turn QA suite into executable scripts, e2e via UI | Mutation and Gherkin mutation | e2e UI suite, handoff/manifest consistency, CRAP/DRY | Commit + broadcast priority 00 to all (merge-only) |
2.3 Layer 3 — Transport rules (the gate)
swarm_handoff.shrejects drafts with: reserved fields, unknown roles, non-numeric priority (00–99), ambiguous or non-commit commits,task> 80 chars, agent-written bodies. The agent repairs and retries; nothing malformed enters the queue.- Priorities: 50 = normal chain progress, 00 = terminal broadcast / urgent follow-up work. The queue orders by
priority_timestamp_sequence, so order is deterministic even if they arrive in the same second. batchroles consume all equal-priority handoffs as one unit → cleaner/reviewer does not interrupt its pass for each delivery.- Agents do not talk to tmux: the daemon is the only one with socket access; agents only write files to their outbox. Control channel and state channel are separated.
2.4 Layer 4 — State rules (the queue as a state machine)
new → in_process → completedwith audit timestamps (enqueued_at,dequeued_at,completed_at).- Resumption: state lives in files, not memory — you restart the swarm and
ready_for_next.shresumes fromin_process. done_with_current.shchains the next task automatically → the pipeline advances without human intervention between gates.
2.5 Where determinism comes from (summary)
- Closed message types (2) and strict validation gate → nothing ambiguous enters the system.
- Mandatory chain forwarding + merge-only broadcast → processing order is always the same, with no skips or loops.
- Ownership boundaries ("Does Not Own") → each agent only touches its own work; nobody steps on another's (coder does not do mutation; cleaner does not introduce behavior).
- Mandatory verification before each handoff → a handoff only exists if its gate passed.
- Worktree isolation → each role sees only its branch; merge happens explicitly via
merge_and_processat handoff time. - Stable task name + priority + sequence → full traceability: you can follow
cart-taxcommit by commit through the whole chain.
3. Diagrams
3.1 Full pipeline (six roles, six-pack)
sequenceDiagram
autonumber
participant U as User
participant S as Specifier
participant C as Coder
participant CL as Cleaner (batch)
participant A as Architect (batch)
participant H as Hardender (batch)
participant Q as QA (batch)
U->>S: Implement cart with tax
S->>U: Gherkin + e2e QA suite (asks approval)
U-->>S: Approved
S->>S: commit spec + draft (type/to/priority/task/commit)
S->>S: swarm_handoff.sh → outbox (gate: canonical commit)
Note over S,C: daemon delivers to coder inbox/new + tmux wake-up
C->>C: ready_for_next.sh → in_process + TASK cart-tax
C->>C: merge_and_process specifier `<commit>` + TDD + acceptance
C->>C: commit + byline + forward (same task)
Note over C,CL: daemon delivers (several equal-priority handoffs)
CL->>CL: ready_for_next.sh → BATCH (N items)
CL->>CL: CRAP ≤ 6 + DRY + mutation scan + tests
CL->>CL: commit + forward to architect
A->>A: structure + dependencies + differential mutation + DRY
A->>A: commit + forward to hardender
H->>H: mutation hardening + Gherkin soft + CRAP/DRY
H->>H: commit + forward to QA
Q->>Q: e2e UI suite + handoff consistency
Q->>Q: commit + broadcast priority 00 (merge-only)
Q-->>S: merge_and_process QA `<commit>` — no forward
S->>U: Next feature?
3.2 Handoff chain and priorities
flowchart LR
U[User] -->|"intent"| S[Specifier]
S -->|"git_handoff p50 · stable task"| C[Coder]
C -->|"git_handoff p50"| CL[Cleaner · batch]
CL -->|"git_handoff p50"| A[Architect · batch]
A -->|"git_handoff p50"| H[Hardender · batch]
H -->|"git_handoff p50"| Q[QA · batch]
Q -->|"git_handoff p00 · broadcast merge-only"| S
S -.->|"human approval"| U
3.3 Task lifecycle
stateDiagram-v2
[*] --> new: daemon delivers .handoff
new --> in_process: ready_for_next.sh (dequeued_at)
in_process --> completed: done_with_current.sh (completed_at)
in_process --> in_process: next queued task or batch
new --> [*]: NO_TASK (empty queue)
4. Mermaid syntax notes (validated with v11.13.0)
- In
sequenceDiagrammessages do not use</>entities — use backticks:`<commit>`. - In
flowchartlabels do not use escaped double quotes (\") — use inner single quotes or plain text. <br/>does work inside sequence messages and labels.