suspenders

Your agent fleet,
with suspenders.

Five parallel Claude Code sessions. A usage cliff freezes three mid-edit. The survivors keep editing the same files; nobody notices for six hours. Suspenders is the control plane that notices — and the board where you decide what happens next.

GitHub Features $ ./install.sh --wire

The board — monitoring and task completion in one surface

suspenders fleet board: an open decision with LLM recommendation, lanes, task columns, event stream

Open decisions surface as red cards. Get recommendation has an LLM analyze the decision in control-plane context; you edit or send. Lanes, completion stats, and the event stream stay live at 1s.

How a decision resolves

01
A lane needs a call made mid-flight — it emits NEED_DECISION instead of blocking silently
02
The board surfaces it — red card, asker, age; the question becomes visible to you, not just the lane
03
You press Get recommendation — the advise worker gathers claims, recent events, work-graph shape and asks your configured LLM
04
Recommendation lands inline — recommendation, rationale, risk, model; one click fills your answer box
05
You edit, then send — the answer goes back to the lane as ANSWER. Recommendations are suggestions; only you send.

What it enforces

secrets-gate before every Bash call  ·  file leases on edits  ·  capability-aware dispatch  ·  plan-gated fan-out  ·  zombie detection that never mistakes a failed lookup for death  ·  no auto-reclaim — reclaiming a lane is your call

Messaging, consults, and the human in the loop

sequenceDiagram
    autonumber
    participant L as Lane (worker session)
    participant C as Coordinator session
    participant G as governor.db (bus + graph)
    participant B as Fleet board (browser)
    participant A as advise.ts (LLM worker)
    participant H as Human operator

    Note over L,G: work and checkpoints are structured events in the database
    L->>G: work take W7 (CAS claim, capability-checked)
    L->>G: emit checkpoint --sha (milestone)
    L->>G: emit NEED_DECISION (decision raised)

    G->>B: 1s poll — decision card (red, asker, age)
    B->>H: "2 decisions need you"

    alt human wants machine advice
        H->>B: click "Get recommendation"
        B->>A: POST /api/advise (spawn, detached)
        A->>G: read claims, recent events, graph shape
        A->>A: LLM call (OpenAI-compatible, autodiscovered)
        A->>G: fact advice.#id + ADVICE event
        G->>B: recommendation inline (rec / rationale / risk / model)
        B->>H: "use" fills the answer field
    end

    H->>B: edit decision, send
    B->>G: emit ANSWER --to lane (auto-acks the decision)
    G->>L: lane inbox sees ANSWER on next poll

    Note over L,C: consults — one agent asks another a question
    L->>G: coord who-knows "retry contracts?"
    G->>C: consult C## in expert inbox
    C->>G: coord consult-reply C## answer
    G->>L: answer lands in lane inbox

    Note over G,H: zombie detection — two stale signals alert; reclaim is human
    G->>B: zombie chip (lane frozen by usage cliff)
    H->>G: work reclaim W4 — re-dispatch at frozen transcript

Rendered with mermaid — the same diagram lives in the README. The database is the transport, consults only carry answers between agents, and only you answer decisions or reclaim lanes.

Architecture

bun CLIs + hook gates
  │ SessionStart → bootstrap session, caps, transcript_path
  │ PreToolUse   → secrets / edit-enforce / governor lease
  ▼
governor.db (SQLite/WAL) — sessions · claims · work graph · events · facts
  ▲ 1s poll (WAL = concurrent readers)
fleet board — decisions · lanes · tasks · stream   ← you, deciding

Why "suspenders"

Belt and suspenders: the graph is the record, and a lane is only flagged dead after two independent signals — heartbeat and transcript — both go stale. Task state lives in the database, so any session — restarted, compacted, or brand new — sees the same queue.