mirror of
https://github.com/Permissionless-Software-Foundation/psf-memo.git
synced 2026-09-21 16:52:01 -07:00
Creating mono-repo from psf-memo-client
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# Plan: Option B — `SWARMFORGE_DIR` (external machinery, per-project config)
|
||||
|
||||
> **Status**: pending implementation — working document to resume later.
|
||||
> **Context**: came out of the trial run with `saas-prototype` (see the fork README).
|
||||
> The goal is that projects do not carry SwarmForge code, only their configuration.
|
||||
|
||||
## 1. Goal
|
||||
|
||||
The project stops carrying SwarmForge **code** (scripts) and **shared rules**
|
||||
(articles, default roles). It only keeps its **own configuration** (`swarmforge.conf` +
|
||||
`project.prompt` + overrides). The fork (or any shared location via `SWARMFORGE_DIR`)
|
||||
is the sole source of the machinery. With `SWARMFORGE_DIR` unset, **everything still works
|
||||
as today** (full backward compatibility).
|
||||
|
||||
## 2. Design: where each thing lives
|
||||
|
||||
```
|
||||
SWARMFORGE_DIR (base, e.g. ~/.local/share/swarmforge = fork clone)
|
||||
├── swarm/ + swarmforge/scripts/ ← launcher, daemon, helpers, adapters
|
||||
├── swarmforge/roles/*.prompt ← default roles
|
||||
└── swarmforge/constitution/articles/ ← engineering, handoffs, workflow (shared)
|
||||
|
||||
PROJECT
|
||||
└── swarmforge/ ← "thin": ONLY per-project material
|
||||
├── swarmforge.conf ← project roles + models
|
||||
└── constitution/articles/project.prompt (+ local-*.prompt, role overrides if any)
|
||||
```
|
||||
|
||||
**Merge rule**: the project wins by name — if the project has
|
||||
`roles/cleaner.prompt`, that takes precedence; otherwise the base one is used.
|
||||
|
||||
## 3. Code changes (file by file)
|
||||
|
||||
| File | Change | Why |
|
||||
|---|---|---|
|
||||
| `swarmforge.bb` → `context` | Add `:base-dir` = `(or (System/getenv "SWARMFORGE_DIR") (fs/path working-dir "swarmforge"))`; keep `:swarm-forge-dir` = `working-dir/swarmforge` (project config) | Shared base vs. per-project config |
|
||||
| `swarmforge.bb` → `parse-config` | `roles-dir` = per-role lookup: `project/roles/<role>.prompt` if it exists, else `base/roles/<role>.prompt` | Role overrides |
|
||||
| `swarmforge.bb` → new `sync-shared-config!` | For each worktree **and for master**: copy from `base` the shared articles and default roles into the destination `swarmforge/` **only if missing** (project ones win); scripts as today | The agent reads `swarmforge/constitution.prompt` relative to its cwd — after sync, the merged view is there |
|
||||
| `swarmforge.bb` → `prepare-workspace!` / setup | When syncing on master (project root), add derived files (shared articles) to `.gitignore` so the project's git stays clean | Shared articles are generated at startup, not committed |
|
||||
| `write-agent-instruction-file!` | **No changes** | Relative paths still work because sync merges the view into each worktree |
|
||||
| `check-helper-scripts!` | **No changes** (validates `script-dir`, which points at the base) | — |
|
||||
| `handoffd.bb`, helpers, adapters | **No changes** | Already resolve the project via git (`roles.tsv`) |
|
||||
| `swarm` wrapper | Small install/docs tweak: when installed globally, it runs the base `swarmforge.sh`; the download block remains for first-time setup | Global install |
|
||||
|
||||
**Estimated total**: ~40-60 new/modified lines in `swarmforge.bb` + docs. Nothing else.
|
||||
|
||||
## 4. User setup (once)
|
||||
|
||||
```bash
|
||||
# 1. Install the machinery once
|
||||
git clone https://github.com/pablo-io/swarm-forge ~/.local/share/swarmforge
|
||||
ln -s ~/.local/share/swarmforge/swarm ~/.local/bin/swarm
|
||||
export SWARMFORGE_DIR=~/.local/share/swarmforge # (in your .bashrc)
|
||||
|
||||
# 2. In any project: create ONLY the config
|
||||
mkdir -p swarmforge/constitution/articles
|
||||
# swarmforge.conf + project.prompt (+ local-* / overrides if applicable)
|
||||
|
||||
# 3. Run
|
||||
cd /my/project && swarm
|
||||
```
|
||||
|
||||
## 5. Test plan
|
||||
|
||||
1. **`bb test`** — the existing suite (24 tests) must pass with no semantic changes.
|
||||
2. **Mode B**: minimal project with only conf + `project.prompt` → launch with `SWARMFORGE_DIR`
|
||||
→ verify: worktrees with the merged view (shared articles + roles + scripts),
|
||||
functional master, clean startup.
|
||||
3. **Handoff smoke**: a real end-to-end handoff (like the `saas-prototype` run)
|
||||
under mode B.
|
||||
4. **Backward compatibility**: `saas-prototype` (with full `swarmforge/`) without the env var →
|
||||
must keep working the same.
|
||||
5. **Sync**: change a script in the fork (e.g. a fix) → it is reflected without copying
|
||||
anything into the project.
|
||||
|
||||
## 6. Optional migration of `saas-prototype` (after validating B)
|
||||
|
||||
- Thin its `swarmforge/`: delete `scripts/`, `roles/`, and shared articles; leave
|
||||
`swarmforge.conf` + `project.prompt` (with the design rules).
|
||||
- Re-copy updated prompts from the fork (`architect.prompt` with the written-report
|
||||
rule) — now via the base, not manual copy.
|
||||
|
||||
## 7. Risks / open decisions
|
||||
|
||||
- **Shared articles synced onto master** will be gitignored (derived) — if someone wants
|
||||
to version them explicitly, they can commit them (sync does not overwrite existing ones).
|
||||
- **`roles.tsv` and state** stay under the project's `.swarmforge/` (gitignored) — unchanged.
|
||||
- Agent instructions remain relative to the worktree — key to the design
|
||||
(zero protocol changes).
|
||||
|
||||
## 8. Suggested implementation order
|
||||
|
||||
1. `context` + `parse-config` (base dir + role lookup)
|
||||
2. `sync-shared-config!` + gitignore for derived files
|
||||
3. `bb test`
|
||||
4. Mode B trial with a minimal project
|
||||
5. Doc in the fork README ("SWARMFORGE_DIR mode" section)
|
||||
@@ -0,0 +1,154 @@
|
||||
# Proposal: Configurable quality level per project
|
||||
|
||||
> **Status**: proposal pending implementation — working document.
|
||||
> **Origin**: evaluation of the `saas-prototype` run (see the project's `REPORT.md`): fixed
|
||||
> gates (CRAP≤6, mutation run, DRY, soft Gherkin) cost tokens that not every project needs.
|
||||
> This proposal adds a configurable **quality axis** per project.
|
||||
> **Note**: the two-pack review (section 5) narrows the proposal — the real value is depth
|
||||
> within a pack, not replacing pack choice.
|
||||
|
||||
## 1. The 3 levels
|
||||
|
||||
| Level | Relative cost | Typical use |
|
||||
|---|---|---|
|
||||
| `minimal` | ~1x | Prototypes, spikes, throwaway code |
|
||||
| `standard` | ~2x | Reasonable default for product features |
|
||||
| `maximum` | ~3-4x | **Current rigor** — critical libraries, security/payments, code consumed by others |
|
||||
|
||||
`maximum` = what the pipeline already does today (nothing beyond that for now).
|
||||
|
||||
## 2. Gates by level
|
||||
|
||||
| Gate | minimal | standard | maximum (current) |
|
||||
|---|---|---|---|
|
||||
| TDD + unit tests | ✓ | ✓ | ✓ |
|
||||
| Acceptance Gherkin | optional | ✓ (without full APS pipeline) | ✓ full APS pipeline |
|
||||
| CRAP | — | improve what is reasonable, no hard gate | **≤6** |
|
||||
| DRY | — | reduce reasonable duplication | tooling, strict |
|
||||
| Mutation scan + split >100 sites | — | ✓ (count only — cheap) | ✓ |
|
||||
| Full mutation run | — | — | ✓ differential, kill non-equivalents |
|
||||
| Soft Gherkin mutation | — | — | ✓ |
|
||||
| Property tests | — | — | support |
|
||||
| Written report | — | optional | ✓ |
|
||||
|
||||
## 3. Role responsibility by level (four-pack)
|
||||
|
||||
| Role | minimal | standard | maximum |
|
||||
|---|---|---|---|
|
||||
| **specifier** | Scoping + human gate only (no mandatory Gherkin) | Gherkin ✓ | Gherkin + QA suite |
|
||||
| **coder** | Implements with TDD — required | ✓ | ✓ |
|
||||
| **refactorer** | No gates → **no real work** | Reasonable CRAP + DRY + mutation scan | full gates |
|
||||
| **architect** | No gates → **no real work** | light structural review only | full gates |
|
||||
|
||||
**Conclusion**: level and workflow are correlated. At `minimal`, refactorer/architect have no
|
||||
gates to apply — configuring 4 roles would waste tokens with no benefit.
|
||||
|
||||
## 4. Mechanism (prompts/articles only, zero code)
|
||||
|
||||
```
|
||||
1. PROJECT (project.prompt): "## Quality Level → Quality level: standard"
|
||||
2. SHARED (quality.prompt): the ON/OFF gate table by level
|
||||
3. EACH ROLE PROMPT (one line): "Apply only the gates that are ON for
|
||||
the project's level (see quality article)"
|
||||
```
|
||||
|
||||
The agent reads the level in `project.prompt`, the table in `quality.prompt`, and its role
|
||||
prompt tells it to apply only the ON gates → consistent interpretation across roles.
|
||||
|
||||
The `quality.prompt` article also includes the **role mapping by level**: "at minimal,
|
||||
configure only specifier+coder (2 windows in `swarmforge.conf`); at standard, add
|
||||
refactorer; at maximum, all 4".
|
||||
|
||||
**Honest nuance**: the adjustment is *prompt-soft* — agents follow the level by instruction.
|
||||
Hard enforcement would need a `tools/quality-check` (validate level artifacts), an optional
|
||||
later step.
|
||||
|
||||
## 5. Review: does two-pack already solve part of this?
|
||||
|
||||
**Result of reviewing two-pack's real scope (original project):**
|
||||
|
||||
- **coder (two-pack)**: TDD + unit tests ONLY — explicitly excludes acceptance, Gherkin, IR,
|
||||
Gherkin mutation, property tests, CRAP, DRY, and language mutation.
|
||||
- **cleaner (two-pack, batch)**: coverage, **CRAP≤6**, **DRY**, structure/encapsulation/dependencies
|
||||
and **mutation run on uncovered behavior** + tests to kill mutants.
|
||||
|
||||
**two-pack quality profile**: unit tests ✓ · CRAP≤6 ✓ · DRY ✓ · mutation run ✓ ·
|
||||
structure ✓ (inside cleaner) · acceptance/Gherkin ✗ · property ✗ · separate QA ✗.
|
||||
|
||||
### Conclusion
|
||||
|
||||
1. **two-pack is NOT `minimal`**: it keeps the hard hardening gates (CRAP≤6, DRY, mutation
|
||||
run). It is "full hardening without specification" — not the cheap option on the depth
|
||||
axis.
|
||||
2. **Packs already encode a quality axis**: which gates EXIST (two-pack: no spec;
|
||||
four-pack: spec + architecture; six-pack: + hardender + QA).
|
||||
3. **The level axis adds what packs do NOT cover**: the DEPTH of each active gate
|
||||
(CRAP≤6 vs "improve reasonably"; mutation run vs scan-only; soft Gherkin on/off).
|
||||
4. **Practical implication**: for "cheap", choosing two-pack already drops the expensive
|
||||
layers (spec/architecture) — the main cost lever is the pack. Level is for scaling depth
|
||||
WITHIN a pack (e.g. two-pack without mutation run, four-pack without Gherkin mutation).
|
||||
The run confirmed it: the main waste was four-pack for a login form, not gate depth.
|
||||
|
||||
**Verdict**: the level proposal remains valid but is **narrower** than it first seemed: its
|
||||
real value is depth within a pack, not replacing pack choice. Possible simplification: start
|
||||
with only two levels (standard = current, light = no mutation run or Gherkin mutation) and
|
||||
let pack choice do the rest.
|
||||
|
||||
## 6. Analysis: spec vs hardening priority (the critique of two-pack)
|
||||
|
||||
**two-pack's logic**: TDD already specifies behavior at the unit-test level; Gherkin is a
|
||||
second layer (reviewable contract + end-to-end acceptance) that is expensive (APS pipeline);
|
||||
hardening gates (CRAP≤6, DRY, mutation run) are the code quality floor.
|
||||
|
||||
**The critique (valid)**: for a small task the priority is inverted — mutation run is
|
||||
expensive and protects code that may be thrown away in a prototype; cheap spec ensures the
|
||||
RIGHT thing is built. A well-hardened but wrong feature is still wrong. Logical order:
|
||||
first WHAT (spec), then HOW (gates).
|
||||
|
||||
| | two-pack | spec-first variant (proposal) |
|
||||
|---|---|---|
|
||||
| Base spec | unit tests (TDD) | Light Gherkin (reviewable contract + human approval) + TDD |
|
||||
| Code protection | CRAP≤6 + DRY + **mutation run** | Reasonable CRAP/DRY, **no mutation run** |
|
||||
| Cost | ~2-3x | ~1.5-2x |
|
||||
| Risk covered | dirty/unchangeable code | **building the wrong thing** |
|
||||
|
||||
**The gap it reveals**: two-pack assumes Gherkin comes with the full APS pipeline cost
|
||||
(parser + entrypoint generator + runtime + step handlers). It offers no "spec-lite" variant:
|
||||
write Gherkin as a reviewable contract without building the pipeline or running mutation.
|
||||
|
||||
**Refinement of the `light` level**:
|
||||
|
||||
> `light` = Gherkin written as contract + human approval + TDD + reasonable CRAP/DRY —
|
||||
> **no APS pipeline, no mutation run, no Gherkin mutation, no property tests**.
|
||||
|
||||
This variant covers the most important risk (is it the right thing? does a human approve?)
|
||||
at lower cost than "mutation run without spec".
|
||||
|
||||
## 7. Spec-light: Gherkin or other alternatives?
|
||||
|
||||
**Comparison of options for a cheap, reviewable contract:**
|
||||
|
||||
| Option | Cost | Human pre-code contract | Real enforcement | Risk | Upgrade to spec-full |
|
||||
|---|---|---|---|---|---|
|
||||
| TDD tests as spec (two-pack) | ~1x | ❌ | ✅ | user sees behavior at the end | — |
|
||||
| Prose criteria (markdown) | ~1x | ✅ imprecise | ❌ | ambiguity | rewrite |
|
||||
| **Gherkin written-only (light)** | ~1.5x | ✅ precise | ❌ | **spec drift** | ✅ zero rewrite |
|
||||
| Given/When/Then scenarios in markdown | ~1x | ✅ | ❌ | no standard format | medium rewrite |
|
||||
|
||||
**Key point**: in light, real enforcement comes from the coder's TDD — it turns each approved
|
||||
scenario into unit tests. Gherkin remains a human contract + guide, not verification.
|
||||
**Spec drift** risk is mitigated by a light workflow rule: *"the coder maps each approved
|
||||
scenario to unit tests; the handoff/report confirms the scenario→tests mapping"*.
|
||||
|
||||
**Recommendation**: in four-pack, light = natural degradation — the specifier already writes
|
||||
Gherkin and asks for approval; the back half of the pipeline is cut (the coder does not build
|
||||
entrypoint generator/runtime/step handlers, implements with TDD mapping scenarios). If you
|
||||
later scale to spec-full, the `.feature` files are already there — you only build the pipeline
|
||||
around them.
|
||||
|
||||
**Recommended cheap add-on**: in light, the coder runs `gherkin-parser` ONLY to validate that
|
||||
the spec parses (seconds, no pipeline build) — prevents broken Gherkin syntax from passing
|
||||
as a contract.
|
||||
|
||||
**When to choose each**: never going to scale → prose or TDD-only; may scale → Gherkin
|
||||
written-only (the format IS the upgrade path); human must approve before coding → Gherkin.
|
||||
@@ -0,0 +1,271 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,308 @@
|
||||
# SwarmForge — Description, protocol, and migration requirements
|
||||
|
||||
> Working document. Project source: https://github.com/unclebob/swarm-forge
|
||||
> Goal: understand SwarmForge's architecture and evaluate adapting it to **pi** as the agent, on **Linux**, with **DeepSeek / GLM / Qwen** models.
|
||||
|
||||
---
|
||||
|
||||
## 1. Project description
|
||||
|
||||
**SwarmForge** is a **tmux**-based agent orchestration platform that turns a swarm of AI agents into a coordinated software engineering team. It was created by Robert C. Martin and applies his own engineering discipline (TDD, Gherkin/acceptance testing, mutation testing, CRAP/DRY analysis) to the problem of coordinating agents.
|
||||
|
||||
Core idea: **each agent lives in its own git worktree and its own tmux session**, and agents communicate via a **file-based handoff protocol** delivered by a daemon. There are no direct messages between agents and no direct access to the tmux socket by them.
|
||||
|
||||
### Branch structure
|
||||
|
||||
| Branch | Description | Roles |
|
||||
|---|---|---|
|
||||
| `main` | **Documentary**: shared operational scripts + default constitution articles | — |
|
||||
| `two-pack` | Fast backend workflow (TDD + hardening, no Gherkin) | `coder` → `cleaner` → `coder` |
|
||||
| `four-pack` | Compact workflow with Gherkin specification | `specifier` → `coder` → `refactorer` → `architect` → `specifier` |
|
||||
| `six-pack` | Full workflow with all quality gates separated | `specifier` → `coder` → `cleaner` → `architect` → `hardender` → `QA` → end |
|
||||
|
||||
Each executable branch contains the project config: `swarmforge.conf` (topology), `roles/<role>.prompt` (per-role prompts) and `constitution.prompt` + articles (shared rules). On startup, the `./swarm` wrapper downloads the shared operational scripts from `main` (first time only) and launches the orchestrator.
|
||||
|
||||
### How it works at a high level
|
||||
|
||||
1. **Declarative configuration**: `swarmforge.conf` defines the swarm window by window:
|
||||
```
|
||||
window <role> <agent> <worktree> [task|batch] [extra-args...]
|
||||
```
|
||||
2. **Launcher** (`swarmforge.bb`, Babashka): validates the config, initializes the git repo if needed, creates a **worktree per role** under `.worktrees/`, creates a **tmux session per role** on a project-owned socket and launches each agent with its initial prompt.
|
||||
3. **Agents**: each runs as an interactive TUI in its tmux pane, inside its worktree, with the handoff scripts on its `PATH`.
|
||||
4. **Daemon** (`handoffd.bb`): owner of the tmux socket. Watches agent outboxes, delivers handoffs to recipient inboxes and wakes agents with a message typed into their pane.
|
||||
5. **Handoff protocol**: agents create validated drafts, receive them as tasks or batches (`task`/`batch`), and report completion with `done_with_current.sh`.
|
||||
6. **Optional viewer**: terminal adapters (`terminal-adapters/*.sh`) open one window per role for real-time observation, with a watchdog that reopens closed windows without losing agent state.
|
||||
|
||||
### Key features
|
||||
|
||||
- **Config-driven topology**: swarm shape comes from `swarmforge.conf`, not from code.
|
||||
- **Per-project roles**: `swarmforge/roles/<role>.prompt` per branch/backlog.
|
||||
- **Layered constitution**: `constitution.prompt` directs agents to read articles under `swarmforge/constitution/articles/` (shared engineering, handoff and workflow rules + local per-branch rules).
|
||||
- **Per-role backends**: each role can use a different agent CLI (`claude`, `codex`, `copilot`, `grok`).
|
||||
- **Observable**: one terminal window per role, or headless in tmux.
|
||||
- **Self-hosted and light**: only needs tmux, git, zsh and Babashka; all state lives in `.swarmforge/` inside the project.
|
||||
- **Operational robustness**: host sleep prevention (`caffeinate`/`systemd-inhibit`), task resumption after restart, file-based audit (`new` → `in_process` → `completed`).
|
||||
|
||||
---
|
||||
|
||||
## 2. Handoff protocol (summary)
|
||||
|
||||
The protocol separates **state** (files on the filesystem, durable and auditable) from **control** (tmux, only for notification and liveness).
|
||||
|
||||
### Messages
|
||||
|
||||
Only two types, both strictly validated:
|
||||
|
||||
```
|
||||
type: git_handoff type: note
|
||||
to: <role>[,<role>...] to: <role>[,<role>...]
|
||||
priority: NN (00-99) priority: NN (00-99)
|
||||
task: <stable-name> message: <1 line, max 80 chars>
|
||||
commit: <10 hex>
|
||||
```
|
||||
|
||||
- `git_handoff`: the sender has committed work; the receiver does `merge_and_process <role> <commit>`.
|
||||
- `note`: short message; only when the constitution or role explicitly authorizes it.
|
||||
|
||||
### Flow
|
||||
|
||||
1. The agent commits and writes a **draft** with headers only.
|
||||
2. `swarm_handoff.sh` is the **validation gate**: rejects reserved fields, unknown roles, invalid priorities, ambiguous commits (canonicalizes the hash with `git rev-parse --disambiguate`) and bodies that are not generated.
|
||||
3. The helper generates the payload (`id`, `from`, `role`, `task`, `created_at`, body) and installs it atomically in `outbox/`.
|
||||
4. The **daemon** polls (1s), copies the handoff to each recipient's `inbox/new/` (adding `recipient` and `enqueued_at`) and wakes the receiver.
|
||||
5. The receiver runs `ready_for_next.sh` → moves to `inbox/in_process/` (adds `dequeued_at`) and prints `TASK:`/`BATCH:` with the payload.
|
||||
6. On completion, `done_with_current.sh` moves to `inbox/completed/` (adds `completed_at`) and picks up the next task if one exists.
|
||||
7. The daemon moves the sender's original to `sent/` or `failed/`.
|
||||
|
||||
### Wake-up (control plane)
|
||||
|
||||
The daemon "wakes" an agent by typing into its tmux pane:
|
||||
|
||||
```
|
||||
tmux send-keys -t <session> -l "You have new handoff mail. If idle, run ready_for_next.sh."
|
||||
tmux send-keys -t <session> C-m # Enter
|
||||
tmux send-keys -t <session> C-j # robustness LF
|
||||
```
|
||||
|
||||
The agent receives it as a user message. Protocol rules: if it is already working, **ignore the wake-up**; `done_with_current.sh` picks up the next task when finished. In practice, an agent with a message queue (like pi) enqueues the wake-up and delivers it when the turn ends.
|
||||
|
||||
### Chain rules
|
||||
|
||||
- Intermediate roles **always forward** a `git_handoff` to the next role in the chain, no matter what (even if the change is non-functional).
|
||||
- The final handoff of the chain (broadcast) is **merge-only**: recipients merge and do not forward.
|
||||
- Task names (`task:`) are preserved along the chain.
|
||||
|
||||
---
|
||||
|
||||
## 3. Migration requirements
|
||||
|
||||
### 3.1 Agent contract (necessary condition)
|
||||
|
||||
SwarmForge requires the agent to be a **long-lived interactive process in a tmux pane** that satisfies:
|
||||
|
||||
1. **Interactive CLI (TUI/REPL)** that keeps running — wake-ups arrive as typed text + Enter; a one-shot CLI cannot receive work.
|
||||
2. **Initial prompt via command line** (or injectable via `tmux send-keys` after startup).
|
||||
3. **Work in the worktree directory** (`cd <worktree> && <agent> ...`).
|
||||
4. **Ability to run commands** (the helpers `swarm_handoff.sh`, `ready_for_next.sh`, `done_with_current.sh` are shell/bb on `PATH` — they are model-agnostic).
|
||||
|
||||
Everything else in the protocol (handoffs, worktrees, daemon, wake-ups, watchdog) **does not know about the model**: the only integration point is the launch arm in `swarmforge.bb` and the validated backend list in `parse-config`.
|
||||
|
||||
### 3.2 Validation: pi as agent
|
||||
|
||||
**Fits out of the box.** Verified in docs and installed binary:
|
||||
|
||||
- `pi "<prompt>"` starts the TUI, **sends the initial message and stays interactive** (confirmed in `dist/modes/interactive/interactive-mode.js`).
|
||||
- **tmux officially supported** (`docs/tmux.md`). Recommendation: tmux ≥ 3.5 with `extended-keys-format csi-u` for modified keys; the basic protocol (Enter) works with any version.
|
||||
- **Compatible wake-up**: in pi `Enter` = send, `Ctrl+J` = new line (the daemon's `C-j` is harmless).
|
||||
- **Message queue**: a message typed while pi is working is **enqueued and delivered when the turn ends** — ideal for the protocol's wake-up semantics.
|
||||
- **Sessions**: `pi -c` (continue), `--session`, `--name "SwarmForge <Role>"` (session name).
|
||||
|
||||
Operational requirements with pi:
|
||||
|
||||
| Requirement | Detail |
|
||||
|---|---|
|
||||
| Trust prompt | pi asks on startup in a new project and **would block the agent**. Use `-a/--approve` in the launch arm or pre-seed `~/.pi/agent/trust.json`. |
|
||||
| Fixed model | Use `--model <provider>/<model>` so each role does not start at the login selector. |
|
||||
| Runtime | Node.js ≥ 22 (npm install) or standalone script; Linux supported natively. |
|
||||
|
||||
Proposed launch arm in `swarmforge.bb`:
|
||||
|
||||
```clojure
|
||||
"pi" (str "pi -a --name " (sq (str "SwarmForge " display))
|
||||
" --model " (sq model) " "
|
||||
(extra-args-prefix row)
|
||||
"\"$(cat " (sq (str prompt-file)) ")\"")
|
||||
```
|
||||
|
||||
### 3.3 Validation: opencode as agent
|
||||
|
||||
**Fits with a mandatory adaptation.** Verified on the real v1.18.15 binary and in source:
|
||||
|
||||
- `opencode` (no args) starts the persistent **interactive TUI**.
|
||||
- ⚠️ The TUI **does not accept an initial message via CLI**: `--prompt` in TUI mode calls a Node `rl.question` (waits for stdin input); only `--mini --prompt` sends it as a message, but with `interactive: false` (runs and exits). `opencode run "<msg>"` is **headless one-shot** — not usable as a swarm agent.
|
||||
- **Solution**: launch the TUI (`opencode --auto`) and **inject the prompt with `tmux send-keys`** after startup — the same mechanism the daemon already uses to wake. About ~10 lines in `launch-role!` (launch → sleep → `send-keys -l "$(cat prompt)"` + Enter).
|
||||
|
||||
```
|
||||
opencode --auto -m <provider>/<model> # in the role's tmux session
|
||||
# after ~2s:
|
||||
tmux send-keys -t <target> -l "<initial prompt>" ; tmux send-keys -t <target> C-m
|
||||
```
|
||||
|
||||
Operational requirements with opencode:
|
||||
|
||||
| Requirement | Detail |
|
||||
|---|---|
|
||||
| Permissions | `--auto` (auto-approve; also the hidden aliases `--yolo` / `--dangerously-skip-permissions`) — equivalent to the swarm's autonomous mode. |
|
||||
| Sessions | `-c/--continue`, `-s/--session` for the restart flow ("on restart, run ready_for_next.sh"). |
|
||||
| Runtime | Static binary (npm `opencode-ai` or GitHub release); Linux supported. |
|
||||
| Future | `opencode serve` + `attach`/SDK/ACP would allow a native message queue without keyboard wake-ups (would require changing the architecture, not adapting it). |
|
||||
|
||||
### 3.4 Models: DeepSeek / GLM / Qwen
|
||||
|
||||
| Model | pi | opencode |
|
||||
|---|---|---|
|
||||
| **DeepSeek** | **Native**: `DEEPSEEK_API_KEY`, provider `deepseek`, `--model deepseek/...` | **Native** in catalog (`models.dev`): `deepseek-*` |
|
||||
| **Qwen** | **Native**: `QWEN_TOKEN_PLAN_API_KEY`, providers `qwen-token-plan` / `-individual` / `-cn` (China) | **Native**: `qwen3.x-*`, `alibaba-*/qwen*` |
|
||||
| **GLM (Zhipu)** | **Not native**: needs a custom provider extension (OpenAI-compatible, `api: "openai-completions"`, `thinkingFormat: "zai"`) or OpenAI-compatible proxy | **Native**: `glm-4.x`/`glm-5.x` (`opencode-go/glm-*`, `alibaba-*/glm-*`) |
|
||||
|
||||
Note: pi already implements the *thinking* formats of all three families (`thinkingFormat: "deepseek" | "zai" | "qwen"` in `docs/custom-provider.md`), which simplifies GLM integration: you only need to register the endpoint and models with that extension.
|
||||
|
||||
### 3.5 Linux (runtime)
|
||||
|
||||
| Requirement | Status | Detail |
|
||||
|---|---|---|
|
||||
| `zsh` | **Hard requirement** | Scripts use `#!/usr/bin/env zsh`. Arch: `pacman -S zsh`. |
|
||||
| `tmux` | Required | Recommended ≥ 3.5 (pi with extended keys). |
|
||||
| `git` | Required | Worktrees and commit protocol. |
|
||||
| Babashka (`bb`) | Required | Launcher and all helpers are Babashka (cross-platform). |
|
||||
| Node.js ≥ 22 | pi only | npm install of pi (or standalone script). |
|
||||
| Terminal | **Headless works** | By default on Linux (no `osascript`/`wt.exe`) the launcher falls back to `none`: attaches the current shell to the first role's session and the rest stay detached (`tmux -S <socket> attach -t swarmforge-<role>`). The swarm runs fully without windows. |
|
||||
| Automatic windows (optional) | To build | Write a `terminal-adapters/wezterm.sh` (or kitty) for Linux: 5-function contract (~40 lines). WezTerm is the most scriptable (`wezterm cli`); Ghostty on Linux has no remote control. |
|
||||
| Shutdown | Plan for | The `close-swarm` script lives on the `main` branch; executable branches do not carry it — copy it into the project or use shutdown by "closing the first window". |
|
||||
| Sleep prevention | Works | `systemd-inhibit` on Linux (systemd running). Disable with `SWARMFORGE_PREVENT_SLEEP=0`. |
|
||||
|
||||
### 3.6 Necessary code changes (minimal)
|
||||
|
||||
In `swarmforge/scripts/swarmforge.bb` (the working branch, e.g. `four-pack`):
|
||||
|
||||
1. **`parse-config`**: add the backend to the validated list, e.g. `#{"claude" "codex" "copilot" "grok" "pi"}`.
|
||||
2. **`launch-command`**: add the new backend's arm (pi: section 3.2; opencode: section 3.3).
|
||||
3. **`check-backend-dependencies!`**: no changes — already checks that the binary exists on `PATH`.
|
||||
|
||||
In the project config:
|
||||
|
||||
- `swarmforge.conf`: `window coder pi master` (or `opencode`), with `[task|batch]` and extra args per role.
|
||||
|
||||
Optional depending on goal:
|
||||
|
||||
- Shared constitution articles in `swarmforge/constitution/articles/` of the branch (the wrapper only *stages* them in `scripts/shared-articles/`; confirm agents read what the branch needs).
|
||||
- Linux terminal adapter (section 3.5).
|
||||
- `close-swarm` in the project.
|
||||
|
||||
---
|
||||
|
||||
## 4. Architecture diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph Config["Configuration (per project/branch)"]
|
||||
CONF["swarmforge.conf<br/>window role agent worktree [task|batch] [args]"]
|
||||
ROLES["swarmforge/roles/<role>.prompt"]
|
||||
CONST["swarmforge/constitution.prompt<br/>+ constitution/articles/"]
|
||||
end
|
||||
|
||||
subgraph Launcher["Launcher — swarmforge.bb (Babashka)"]
|
||||
PARSE["Validate config and prompts"]
|
||||
WT["Git worktrees<br/>.worktrees/<role> (branch per role)"]
|
||||
TMUX["tmux sessions<br/>swarmforge-<role> · project-owned socket"]
|
||||
LAUNCH["send-keys: export SWARMFORGE_ROLE<br/>+ PATH helpers + cd worktree<br/>+ <agent> '$(cat prompt)'"]
|
||||
end
|
||||
|
||||
subgraph Swarm["Swarm (1 agent per role)"]
|
||||
A1["Agent TUI<br/>(tmux pane)"]
|
||||
A2["Agent TUI<br/>(tmux pane)"]
|
||||
A3["Agent TUI<br/>(tmux pane)"]
|
||||
end
|
||||
|
||||
subgraph State["Durable state — filesystem (.swarmforge/handoffs)"]
|
||||
OUT["outbox/ · sent/ · failed/"]
|
||||
IN["inbox/ new · in_process · completed"]
|
||||
end
|
||||
|
||||
subgraph Control["Control — daemon handoffd.bb"]
|
||||
DAEMON["Poll outbox → deliver to inbox<br/>→ wake-up via tmux send-keys"]
|
||||
end
|
||||
|
||||
subgraph Viewer["Viewer (optional)"]
|
||||
ADAPT["terminal-adapters/*.sh"]
|
||||
WATCH["swarm-window-watchdog"]
|
||||
end
|
||||
|
||||
CONF --> PARSE
|
||||
ROLES --> PARSE
|
||||
CONST --> PARSE
|
||||
PARSE --> WT --> LAUNCH
|
||||
PARSE --> TMUX --> LAUNCH
|
||||
LAUNCH --> A1 & A2 & A3
|
||||
A1 & A2 & A3 -->|"helpers on PATH:<br/>swarm_handoff.sh"| OUT
|
||||
OUT --> DAEMON
|
||||
DAEMON -->|"deliver .handoff"| IN
|
||||
DAEMON -->|"wake-up: text + Enter"| A1 & A2 & A3
|
||||
A1 & A2 & A3 -->|"ready_for_next.sh<br/>done_with_current.sh"| IN
|
||||
A1 & A2 & A3 -->|"work (git)"| WT
|
||||
A1 & A2 & A3 -->|"observe: tmux attach"| TMUX
|
||||
TMUX --> ADAPT --> WATCH
|
||||
```
|
||||
|
||||
## 5. Protocol diagram (one handoff cycle)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant S as Sender agent (e.g. coder)
|
||||
participant V as swarm_handoff.sh (gate)
|
||||
participant O as outbox/ (sender)
|
||||
participant D as Daemon handoffd.bb
|
||||
participant I as inbox/ (receiver)
|
||||
participant R as Receiver agent (e.g. cleaner)
|
||||
|
||||
Note over S: git commit (message with role byline)
|
||||
S->>S: Write draft (type/to/priority/task/commit)
|
||||
S->>V: swarm_handoff.sh `<draft>`
|
||||
V->>V: Validate: known roles, priority 00-99,<br/>canonical commit (10 hex, --disambiguate),<br/>reserved fields, body forbidden
|
||||
V->>O: Install generated .handoff (id, from, role,<br/>task, created_at, merge_and_process payload)
|
||||
V-->>S: HANDOFF QUEUED
|
||||
O->>D: Poll (1 s)
|
||||
D->>I: Copy to each recipient inbox/new/<br/>+ recipient, enqueued_at headers
|
||||
D->>R: tmux send-keys -l 'You have new handoff mail...'<br/>+ C-m (Enter) + C-j (robustness)
|
||||
Note over R: If busy → ignore (queue or next<br/>done_with_current will pick it up)
|
||||
R->>I: ready_for_next.sh → move to in_process/<br/>+ dequeued_at header
|
||||
I-->>R: TASK: `<path>` / BATCH: `<items>` + PAYLOAD
|
||||
R->>R: merge_and_process `<sender>` `<commit>`<br/>+ process the task in its worktree
|
||||
R->>I: done_with_current.sh → completed/<br/>+ completed_at header
|
||||
I-->>R: Next task or NO_TASK
|
||||
D->>O: Move original to sent/ (or failed/)
|
||||
```
|
||||
|
||||
### Inbox 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
|
||||
new --> [*]: NO_TASK (empty queue)
|
||||
failed --> [*]: delivery error (sender outbox)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Executive summary
|
||||
|
||||
1. **The architecture is model-agnostic**: the only integration point for a new backend is the launch arm + the validated backend list in `swarmforge.bb`; the handoff protocol, worktrees, daemon and wake-ups do not know about the agent.
|
||||
2. **pi fits directly**: interactive initial message via CLI, tmux supported, message queue aligned with wake-up semantics, native DeepSeek/Qwen and GLM with a small extension.
|
||||
3. **opencode fits with an adaptation**: the TUI does not accept an initial prompt via CLI → inject via `tmux send-keys` after startup (mechanism already in the system). Native DeepSeek/GLM/Qwen.
|
||||
4. **Linux is a first-class citizen by design**: the swarm lives in tmux, not in windows; headless runs fully. Automatic windows are only an optional terminal adapter.
|
||||
5. **Minimum requirements**: zsh + tmux (≥3.5 recommended) + git + Babashka + (Node.js for pi) + ~15 lines of changes in `swarmforge.bb` + provider config.
|
||||
Reference in New Issue
Block a user