fleet-bridge
The story. The captain runs a kanban board in his browser — the bridge — as his
main surface for delegation-heavy work: one card per piece of fleet work, per-card chat
threads, typed timeline events, a notification bell. He glances to see, types to steer.
The board is a separate generic app shipped as the bridge skill (zero-dep node server +
web UI + bridge-axi CLI) and knows NOTHING about firstmate; THIS skill is the firstmate
side. Two loops meet at you: Loop A = the captain talking to you through the board,
Loop B = the feeder narrating fleet evidence onto cards. Everything mechanical is
automated — you are the only gear that cannot be. Model:
docs/api/overview.md; the board itself: the bridge skill's own
docs/api/overview.md.
Setup
- The user installs both skills, by name (
bridge, fleet-bridge). bridge-axi lives
in the installed bridge skill's dir (e.g. ~/.claude/skills/bridge/bridge-axi).
- One server hosts many boards; every call names one via
--board. The fleet uses a
single board — named fleet in every example here. Port 4777. State:
~/.bridge/boards/fleet.json; archive: fleet.archive.jsonl.
- Network exposure is the user's machine-private config (
"host" in
~/.bridge/config.json), default localhost-only. Never set or document a bind host
yourself.
- Server startup is by captain's order only, never automatic. The order:
"Captain on the bridge" (or any clear ask for the board). Then:
bridge-axi open --board fleet (idempotent, prints URL) → wire the push hook, arm the
poll, and run the sync → hand him the URL, nothing else. A board already running stays
running.
The feeder's lane — never hand-do these
fm-board-sync (this skill dir) reads a firstmate home read-only and feeds the board.
It alone owns: events (status lines, PR state, worker-lease transitions — deduped, typed
by kind), the kinds map, the worker lease (evidence-based, server-side TTL decay to
idle), the prs attribute, brief artifacts, card birth for new in-flight work, archive
on merge, resurrection of archived ids, type migration.
Board looks stale → run the sync. Never patch what the feeder owns.
Run it on every wake that changes fleet state:
fm-board-sync --home <fm-home> --apply --board fleet (no --apply = print plan).
The fast path — push, not poll: firstmate's generic, opt-in config/event-hook
(docs/configuration.md "Event hooks" in the firstmate repo) fires on a spawn,
teardown, PR-state change, or status/turn-end write. Point it at this skill's
fm-board-sync-hook, which just re-runs the same sync above immediately instead of
waiting out a poll:
# <fm-home>/config/event-hook — one line, written once per home
<this-skill-dir>/fm-board-sync-hook
Wire this the same session you first run the sync for a home. It never replaces the
poll shim below — a consumer that misses an event (hook not yet wired, a cold board
server) still catches up on the next poll.
Zero-token freshness between wakes — silent watcher shim, the reconciler/fallback for
whatever the push hook misses:
# <fm-home>/state/board-sync.check.sh — prints nothing, never wakes the agent
<this-skill-dir>/fm-board-sync --home <fm-home> --apply --board fleet >/dev/null 2>&1 || true
Your gear — deliberate, never automated
- Handoff =
bridge-axi move <id> review. Verify the outcome, rewrite the body, THEN
move; the level-1 move event IS the notification. Finished work never auto-advances a
card — the wake tells you, you decide.
- The body is the deliverable. Rewrite it to CURRENT state before every handoff; never a
log (history lives in events).
- Answer every thread you owe. A captain thread message sets
owed; only your reply
clears his waiting balloon.
- Link before spawning. Work for a captain card: add a
data/board-aliases line
<task-id> <card-id> in the owner home BEFORE spawn — that is how the feeder narrates
onto the right card instead of minting a duplicate. Direct subagents (no on-disk task):
fm-subagent --home <h> set <agent-id> --card <card-id> --state working on dispatch
(clear unlinks; rides existing cards, never mints).
- Archive beyond merge is your call:
bridge-axi archive <id> [--note ...] when work is
dead, landed, or acted on. Merge-archive is the feeder's.
The poll — non-negotiable
- Keep exactly ONE
bridge-axi poll --board fleet alive as a harness-tracked background
task the whole time the board is up. It is the captain's ONLY path to you from the
board; a dead poll = his messages sit unanswered and nothing wakes you.
- The poll dies with every server restart and every fire. Re-arm it in the SAME turn.
- No turn ends deaf: if you cannot point to a live poll you armed or verified this turn,
arm one now, before ending the turn.
- Never launch it with a shell
&.
- On fire: handle each JSON line → reply →
bridge-axi ack <highest seq handled> →
re-run the poll. Ack only after handling. A repeated seq was handled but never
acked: dedupe by seq, never re-act.
message = captain instruction in that context. Act through the NORMAL firstmate
lifecycle, then reflect the outcome on the card.
card-created = awareness only: ack it, no reply owed, no work implied. Act only when
the captain speaks; a thread "go" becomes work on the SAME card, linked before spawn.
card-moved to peer = hands-off. Out of peer = handback, resume. Anywhere else =
captain reprioritized; reconcile.
Board rules
- Columns:
💡 Ideas → 🔨 Working → 👀 Your review → 🤝 Peer review
(ids: ideas working review peer). No Done — cards leave by archive.
- Through Your review the card is yours; move it freely. In Peer review it is the
CAPTAIN's: never move or rewrite it; him moving it out = handback.
- Exception: a merge is objective and terminal — archive from ANY column.
- The feeder never moves cards. Every move is the captain's drag or your deliberate act.
- Types:
plan 🧠 (default for captain cards; covers discussion) · implementation 🔥 ·
investigation 🕵️♂️. Attributes: type, repo, owner, prs, artifacts.
- Questions ride threads, never cards:
bridge-axi say card:<id> + a level-1 question
event; no card → say chat.
- English only, everything on the board. Outcomes, not machinery; full PR URLs. The
captain's native-language conversation stays in the agent chat.
- Levels: 1 = bell —
done ✅, failed 💥, needs-you ✋, blocked 🚧, handoffs,
merge/kill archives. 2 = timeline only — progress 📣, pr-opened 🔀, pr-merged 🟣,
worker-linked 🔗, worker-gone 💤. The feeder registers the kinds map every run;
bridge structural kinds stay built-in.
- Board = mirror. Firstmate's files stay canonical; on disagreement, fix the board.
- Text via
--text-file/--body-file or stdin, never shell-interpolated.
Standing habits while this skill is active
- Every ship/scout brief you write gets one added instruction: append a one-line
working: <headline> status at real milestones — branch created, approach decided,
implementation committed, tests green, PR opened. Every 10-30 min of progress, not
per-edit chatter. The feeder turns each into a 📣 progress event: a green card that
says what its worker is doing. Brief-content rule of THIS skill, never a change to the
firstmate template.
- The captain states a durable preference on the board → record it in
data/captain.md
the same turn.
Placement
- Makes sense for any board-running agent →
bridge. Needs crewmate/PR/backlog
vocabulary → here. No firstmate vocabulary, paths, or logic ever lands in the bridge
skill.
- Fleet-private data (board aliases, board name choice) stays in the firstmate home's
data/, never inside either skill (both may live in public repos).
1---2name: fleet-bridge3description: How firstmate drives the bridge kanban board with its fleet state. Use whenever the captain's board is in operation. Requires the separate generic `bridge` skill (installed by the user); bridge knows nothing about firstmate — this skill owns the mapping.4---56# fleet-bridge78**The story.** The captain runs a kanban board in his browser — the **bridge** — as his9main surface for delegation-heavy work: one card per piece of fleet work, per-card chat10threads, typed timeline events, a notification bell. He glances to see, types to steer.11The board is a separate generic app shipped as the `bridge` skill (zero-dep node server +12web UI + `bridge-axi` CLI) and knows NOTHING about firstmate; THIS skill is the firstmate13side. Two loops meet at you: Loop A = the captain talking to you through the board,14Loop B = the feeder narrating fleet evidence onto cards. Everything mechanical is15automated — you are the only gear that cannot be. Model:16[docs/api/overview.md](docs/api/overview.md); the board itself: the bridge skill's own17`docs/api/overview.md`.1819## Setup2021- The user installs both skills, by name (`bridge`, `fleet-bridge`). `bridge-axi` lives22 in the installed bridge skill's dir (e.g. `~/.claude/skills/bridge/bridge-axi`).23- One server hosts many boards; every call names one via `--board`. The fleet uses a24 single board — named `fleet` in every example here. Port 4777. State:25 `~/.bridge/boards/fleet.json`; archive: `fleet.archive.jsonl`.26- Network exposure is the user's machine-private config (`"host"` in27 `~/.bridge/config.json`), default localhost-only. Never set or document a bind host28 yourself.29- Server startup is by captain's order only, never automatic. The order:30 **"Captain on the bridge"** (or any clear ask for the board). Then:31 `bridge-axi open --board fleet` (idempotent, prints URL) → wire the push hook, arm the32 poll, and run the sync → hand him the URL, nothing else. A board already running stays33 running.3435## The feeder's lane — never hand-do these3637`fm-board-sync` (this skill dir) reads a firstmate home read-only and feeds the board.38It alone owns: events (status lines, PR state, worker-lease transitions — deduped, typed39by kind), the kinds map, the worker lease (evidence-based, server-side TTL decay to40`idle`), the `prs` attribute, brief artifacts, card birth for new in-flight work, archive41on merge, resurrection of archived ids, type migration.4243- Board looks stale → run the sync. Never patch what the feeder owns.44- Run it on every wake that changes fleet state:45 `fm-board-sync --home <fm-home> --apply --board fleet` (no `--apply` = print plan).46- The fast path — push, not poll: firstmate's generic, opt-in `config/event-hook`47 (docs/configuration.md "Event hooks" in the firstmate repo) fires on a spawn,48 teardown, PR-state change, or status/turn-end write. Point it at this skill's49 `fm-board-sync-hook`, which just re-runs the same sync above immediately instead of50 waiting out a poll:5152 ```sh53 # <fm-home>/config/event-hook — one line, written once per home54 <this-skill-dir>/fm-board-sync-hook55 ```5657 Wire this the same session you first run the sync for a home. It never replaces the58 poll shim below — a consumer that misses an event (hook not yet wired, a cold board59 server) still catches up on the next poll.60- Zero-token freshness between wakes — silent watcher shim, the reconciler/fallback for61 whatever the push hook misses:6263 ```sh64 # <fm-home>/state/board-sync.check.sh — prints nothing, never wakes the agent65 <this-skill-dir>/fm-board-sync --home <fm-home> --apply --board fleet >/dev/null 2>&1 || true66 ```6768## Your gear — deliberate, never automated6970- Handoff = `bridge-axi move <id> review`. Verify the outcome, rewrite the body, THEN71 move; the level-1 move event IS the notification. Finished work never auto-advances a72 card — the wake tells you, you decide.73- The body is the deliverable. Rewrite it to CURRENT state before every handoff; never a74 log (history lives in events).75- Answer every thread you owe. A captain thread message sets `owed`; only your reply76 clears his waiting balloon.77- Link before spawning. Work for a captain card: add a `data/board-aliases` line78 `<task-id> <card-id>` in the owner home BEFORE spawn — that is how the feeder narrates79 onto the right card instead of minting a duplicate. Direct subagents (no on-disk task):80 `fm-subagent --home <h> set <agent-id> --card <card-id> --state working` on dispatch81 (`clear` unlinks; rides existing cards, never mints).82- Archive beyond merge is your call: `bridge-axi archive <id> [--note ...]` when work is83 dead, landed, or acted on. Merge-archive is the feeder's.8485## The poll — non-negotiable8687- Keep exactly ONE `bridge-axi poll --board fleet` alive as a harness-tracked background88 task the whole time the board is up. It is the captain's ONLY path to you from the89 board; a dead poll = his messages sit unanswered and nothing wakes you.90- The poll dies with every server restart and every fire. Re-arm it in the SAME turn.91- No turn ends deaf: if you cannot point to a live poll you armed or verified this turn,92 arm one now, before ending the turn.93- Never launch it with a shell `&`.94- On fire: handle each JSON line → reply → `bridge-axi ack <highest seq handled>` →95 re-run the poll. Ack only after handling. A repeated `seq` was handled but never96 acked: dedupe by `seq`, never re-act.97- `message` = captain instruction in that context. Act through the NORMAL firstmate98 lifecycle, then reflect the outcome on the card.99- `card-created` = awareness only: ack it, no reply owed, no work implied. Act only when100 the captain speaks; a thread "go" becomes work on the SAME card, linked before spawn.101- `card-moved` to `peer` = hands-off. Out of `peer` = handback, resume. Anywhere else =102 captain reprioritized; reconcile.103104## Board rules105106- Columns: `💡 Ideas → 🔨 Working → 👀 Your review → 🤝 Peer review`107 (ids: `ideas working review peer`). No Done — cards leave by archive.108- Through Your review the card is yours; move it freely. In Peer review it is the109 CAPTAIN's: never move or rewrite it; him moving it out = handback.110- Exception: a merge is objective and terminal — archive from ANY column.111- The feeder never moves cards. Every move is the captain's drag or your deliberate act.112- Types: `plan` 🧠 (default for captain cards; covers discussion) · `implementation` 🔥 ·113 `investigation` 🕵️♂️. Attributes: `type`, `repo`, `owner`, `prs`, `artifacts`.114- Questions ride threads, never cards: `bridge-axi say card:<id>` + a level-1 `question`115 event; no card → `say chat`.116- English only, everything on the board. Outcomes, not machinery; full PR URLs. The117 captain's native-language conversation stays in the agent chat.118- Levels: 1 = bell — `done` ✅, `failed` 💥, `needs-you` ✋, `blocked` 🚧, handoffs,119 merge/kill archives. 2 = timeline only — `progress` 📣, `pr-opened` 🔀, `pr-merged` 🟣,120 `worker-linked` 🔗, `worker-gone` 💤. The feeder registers the kinds map every run;121 bridge structural kinds stay built-in.122- Board = mirror. Firstmate's files stay canonical; on disagreement, fix the board.123- Text via `--text-file`/`--body-file` or stdin, never shell-interpolated.124125## Standing habits while this skill is active126127- Every ship/scout brief you write gets one added instruction: append a one-line128 `working: <headline>` status at real milestones — branch created, approach decided,129 implementation committed, tests green, PR opened. Every 10-30 min of progress, not130 per-edit chatter. The feeder turns each into a 📣 `progress` event: a green card that131 says what its worker is doing. Brief-content rule of THIS skill, never a change to the132 firstmate template.133- The captain states a durable preference on the board → record it in `data/captain.md`134 the same turn.135136## Placement137138- Makes sense for any board-running agent → `bridge`. Needs crewmate/PR/backlog139 vocabulary → here. No firstmate vocabulary, paths, or logic ever lands in the `bridge`140 skill.141- Fleet-private data (board aliases, board name choice) stays in the firstmate home's142 `data/`, never inside either skill (both may live in public repos).