mirror of
https://github.com/Permissionless-Software-Foundation/psf-memo-client.git
synced 2026-09-21 16:52:02 -07:00
272 lines
13 KiB
Markdown
272 lines
13 KiB
Markdown
# SwarmForge — Handoff protocol and deterministic pipeline
|
||
|
||
> Working document complementary to `swarmforge.md`.
|
||
> Example based on the full `six-pack` workflow (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:
|
||
|
||
```text
|
||
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)
|
||
```
|
||
|
||
```text
|
||
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:
|
||
|
||
```text
|
||
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:
|
||
|
||
```text
|
||
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):
|
||
|
||
```text
|
||
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:
|
||
|
||
```text
|
||
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:
|
||
|
||
```text
|
||
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`:
|
||
|
||
```text
|
||
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
|
||
|
||
```text
|
||
inbox/new/ ──ready_for_next──► inbox/in_process/ ──done_with_current──► inbox/completed/
|
||
(daemon delivery) (+dequeued_at) (+completed_at)
|
||
```
|
||
|
||
- `done_with_current.sh` **picks 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.sh` and resume from `in_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_handoff` and `note`; 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.sh` **rejects** 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**.
|
||
- `batch` roles 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 → completed` with audit timestamps (`enqueued_at`, `dequeued_at`, `completed_at`).
|
||
- **Resumption**: state lives in files, not memory — you restart the swarm and `ready_for_next.sh` resumes from `in_process`.
|
||
- `done_with_current.sh` **chains the next task** automatically → the pipeline advances without human intervention between gates.
|
||
|
||
### 2.5 Where determinism comes from (summary)
|
||
|
||
1. **Closed message types** (2) and **strict validation gate** → nothing ambiguous enters the system.
|
||
2. **Mandatory chain forwarding** + **merge-only broadcast** → processing order is always the same, with no skips or loops.
|
||
3. **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).
|
||
4. **Mandatory verification before each handoff** → a handoff only exists if its gate passed.
|
||
5. **Worktree isolation** → each role sees only its branch; merge happens explicitly via `merge_and_process` at handoff time.
|
||
6. **Stable task name + priority + sequence** → full traceability: you can follow `cart-tax` commit by commit through the whole chain.
|
||
|
||
---
|
||
|
||
## 3. Diagrams
|
||
|
||
### 3.1 Full pipeline (six roles, `six-pack`)
|
||
|
||
```mermaid
|
||
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
|
||
|
||
```mermaid
|
||
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
|
||
|
||
```mermaid
|
||
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 `sequenceDiagram` messages do not use `<`/`>` entities — use backticks: `` `<commit>` ``.
|
||
- In `flowchart` labels do not use escaped double quotes (`\"`) — use inner single quotes or plain text.
|
||
- `<br/>` does work inside sequence messages and labels.
|