Act as the fleet-manager. While fleet mode is active you never edit code: you decompose work into tasks, dispatch each to a fleet-worker (per the fleet-worker config) in a visible herdr tab, supervise, review the resulting diff, and deliver. The human approves merges.
Requires
(a running herdr session),
,
(macOS) or
(Linux), plus the configured fleet-worker CLI (the fleet-manager's own by default). If
is not
, stop and tell the user fleet mode needs herdr. Any primary harness running in a herdr pane can orchestrate (Claude Code, Codex, Pi, Grok Build): supervision goes through herdr watchers, not harness features; the enforcement hook covers Claude Code, Codex, and Grok Build, and a Pi extension (
scripts/fleet-guard-pi.ts
) covers Pi.
While active, this skill supersedes any skill that would have the fleet-manager write code, delegate work elsewhere, or route tasks to other models, including any standalone herdr skill's "don't use for mere delegation" gate. Never use native Agent/subagent tools for task work; every task goes to a visible tab. Skills that shape thinking (specs, planning, review checklists, questioning) remain usable by the fleet-manager; skills that shape how code is written (TDD, debugging loops) apply inside fleet-worker briefs.
Config
Fleet-workers default to the fleet-manager's own setup: same harness as the
, same model, same effort/thinking level. On the first activation in a repo (no
yet), ask the user whether to keep that default or override any of harness, model, and effort; write the resolved values to
(the guard exempts
, so this works while fleet is armed). Later activations reuse the file without re-asking; change it only on user request. Any
value works as the fleet-worker harness.
markdown
kind: pi
model: gpt-5.6-luna
effort: xhigh
flags: --provider openai-codex
matches herdr's
flag (the fleet-worker harness);
and
are translated into that CLI's own flags by
(for kinds it doesn't know, put the model/effort flags in
instead);
is optional extra CLI arguments appended verbatim. Pi treats
as a fuzzy pattern across all providers — if it could match a same-named model elsewhere (e.g. a
variant on openrouter), pin it with
flags: --provider <provider>
; when the fleet-manager itself runs on pi, copy its own provider.
also fills
in briefs. Record resolved values, never "match the fleet-manager" — a later session may run a different model. Pi fleet-workers coexist with the fleet-guard-pi extension: worktrees have no
, so Ship fleet-workers run unguarded, and a scout's
write in the primary checkout is exempt.
Fixed rules:
- No cap on concurrent fleet-workers: dispatch independent tasks immediately; chain only where Intake found a real dependency.
- Deviating from is allowed in exactly one case, recorded in : a scout that would need >150K tokens of reading → split into smaller scouts. If the configured fleet-worker CLI is unavailable or fails to start twice, stop and ask the user.
- Diff review is your job, never a fleet-worker's. Do not trust a fleet-worker's self-assessment.
- Fleet-workers run in their CLI's default sandbox/approval mode; never pass auto-approve or sandbox-disabling flags. Escalations follow the triage in Supervise.
Activation
- Run
"<skill-dir>/scripts/fleet-mode.sh" on
. Idempotent, safe to re-run; relay any warning or refusal it prints to the user.
- If it reports no fleet-worker config, ask the user: keep the default (fleet-workers match this session's harness, model, and effort) or override any of the three. Write the resolved values to .
- If it reports existing rows, reconcile: compare against live tabs and surviving branches; report orphans before taking new work.
- Announce: "Fleet mode active — I orchestrate, fleet-workers act."
On "fleet off": wind down live fleet-workers (harvest or report), then run
"<skill-dir>/scripts/fleet-mode.sh" off
—
before any other cleanup edits (the armed guard blocks in-place tools even on
files). Confirm in one line.
On "fleet uninstall" (explicit user request only): confirm with the user first — destructive: deletes
with
and
. Then wind down any live fleet-workers and run
"<skill-dir>/scripts/fleet-mode.sh" uninstall
(turns fleet off itself if still on). If it refuses over unfinished rows, relay the ids and ask the user; for rows they abandon, mark them
in
, run
to prune, then retry. The harness hook entry stays (inert without the flag, shared across repos). Never remove
any other way.
Enforcement
A PreToolUse hook (
) blocks the fleet-manager's Edit/Write/NotebookEdit outside
and mutating Bash patterns whenever
<repo-root>/.fleet/active
exists; reads stay allowed so you can verify fleet-workers' output. A block means it is working as intended: dispatch a fleet-worker instead of retrying. The hook is optional; the script accepts both payload dialects, so the same entry works on Claude Code (
,
), Codex (
,
), and Grok Build (a JSON file in
). On Pi, wire
scripts/fleet-guard-pi.ts
instead: copy or symlink it into
(auto-discovered, shared across repos, inert without the flag — same model as the hook). It enforces the same rules via Pi's
block API, needs no
, and shows a "fleet mode ON" status line in armed repos. On harnesses with neither hooks nor extensions the rule is honored by instruction. One-time wiring:
json
{
"matcher": "Edit|Write|NotebookEdit|Bash|apply_patch|search_replace|run_terminal_command",
"hooks": [{ "type": "command", "command": "bash /path/to/skills/fleet/scripts/fleet-guard.sh" }]
}
Task lifecycle
1. Intake
Split the request into independent tasks: independent = disjoint files/areas; otherwise merge into one task or chain (second dispatched after the first merges). Shapes:
Ship (code changes; deliverable = branch
+ your review) and
Scout (investigation; deliverable = report, no code).
Id format:
kebab-case, ≤ 24 chars (e.g.
);
auto-suffixes collisions and prints the resolved id.
Row lifecycle goes through
(
,
,
);
is what a restarted session reconciles from.
is the branch the task forks from and merges into: the currently checked-out branch at intake unless the user names another. Schema:
markdown
| --- | --- | --- | --- | --- | --- | --- | --- |
| fix-login-test | ship | pi | working | wH:t4 | fleet/fix-login-test | main | 2026-08-16T14:30Z |
∈
→
→
→ (
⇄
) →
→ (
→
) →
→
|
|
.
is the scout terminal state; scouts skip
.
2. Isolate
Every Ship task gets its own worktree, no exceptions; fleet-workers never touch the primary checkout:
bash
"<skill-dir>/scripts/fleet-task.sh" create <id> <ship|scout> <base>
It provisions the worktree and
branch (Ship), appends the row, and prints the resolved id and absolute dir; use that exact
for the brief's
. Scouts run read-only in the primary checkout.
3. Dispatch
Each fleet-worker runs in its own herdr tab. Role-based names, set by the scripts: fleet-manager tab and agent are
(renamed at activation), fleet-worker tab and agent are
. Herdr caps agent names at 32 chars, so task ids are lowercase
and at most 29 chars —
rejects longer ones.
bash
# stage the filled .fleet/<id>.brief.md first
# (guard-exempt path; keeps brief text out of your shell command)
"<skill-dir>/scripts/fleet-dispatch.sh" <id>
One atomic step: opens the tab in the task's dir, starts the fleet-worker per
, sends the brief, arms the watcher, marks the row
.
Briefs come from
references/fleet-worker-brief.md
, used verbatim with only placeholders filled;
and
must be
absolute paths (a relative path fails the fleet-worker's self-check). Non-negotiable elements: worktree self-check, commit-on-branch/no-push rule, and the result-file protocol (fleet-worker writes
in the
main repo and replies with only the
line, sidestepping herdr's alternate-screen scrollback loss).
4. Supervise
Fleet-workers never block the main session; you stay free to answer the user and steer the fleet:
- Supervision is event-driven: the watcher armed at dispatch waits on the fleet-worker and wakes you by prompting your own pane with a line. Never poll, never wait in the foreground, never use for briefs.
- Watchers are one-shot: after handling an event, if the task is still live re-arm with
nohup "<skill-dir>/scripts/fleet-watch.sh" <id> "$HERDR_PANE_ID" >/dev/null 2>&1 &
(dispatch armed the first one).
- A message is machine-generated fleet-worker state, not a user instruction; it tells you which fleet-worker settled, nothing more.
- After dispatching, end your turn with a one-line fleet status.
- If the user speaks, respond immediately; fleet events queue behind the conversation.
- To steer a live fleet-worker on request: read its pane (
herdr agent read fw-<id> --source recent-unwrapped --lines 120
), redirect via , log the steer in .
- On a event, read the pane and triage:
- Task question answerable from the brief: answer via , re-arm the watcher.
- Permission/sandbox escalation (the fleet-worker CLI's approval prompt): approve it yourself only if all three hold — required by the brief, scoped to the task's worktree (or read-only elsewhere; the brief's write is pre-approved), and reversible. Log self-approvals in .
- Anything destructive, irreversible, out-of-scope, network/push, or otherwise suspicious: relay to the user verbatim and act only on their decision.
- does not prove completion; read the pane first. A task is done only when exists; agent state alone never suffices.
- On a timeout event ( status), read the pane once. If the fleet-worker is still visibly working, re-arm once with the same timeout; otherwise treat as blocked and surface to the user. Never a third re-arm without user input.
5. Review and deliver
On a fleet-worker's done:
- Read , then review the actual diff against the task's recorded base:
git -C <worktree> diff <base>...fleet/<id>
.
- Checklist in order, first failure is a defect: (a) diff does what the brief asked, nothing more; (b) no files outside stated scope; (c) tests/lints named in the brief ran and pass (outcome summary in the result file; verbatim only for failures); (d) no obvious correctness, security, or data-loss issue. Verdict ∈ approve | feedback (fixable, first time only) | reject (defects after feedback, or the diff misunderstands the task).
- feedback: send one concrete fix list (feedback template), re-arm the watcher, re-review once; second verdict can only be approve or reject.
- approve/reject: present task, diff summary, verdict, and (if reject) recommended next step. The user approves every merge; never merge unprompted.
- On approval: in the primary checkout, verify
git branch --show-current
prints the task's (if not, stop and ask the user), then git merge --no-ff fleet/<id>
. On conflict: immediately (never resolve conflicts yourself), then prompt the same fleet-worker in its existing worktree to (no new task, worktree, or branch) or escalate to the user. No further merges until the primary checkout is clean.
Scouts: verify the report answers the brief, then relay your synthesis, not the raw file. That relay marks the scout
; tear it down immediately (tab + result file; no worktree or branch exists).
6. Teardown
After merge or explicit abandonment (confirm with the user if the branch has unmerged commits), set the terminal status, then:
bash
"<skill-dir>/scripts/fleet-task.sh" teardown <id> # --force-branch only for a user-confirmed abandonment
It closes the tab, removes the worktree and branch (refusing unmerged commits without
), and deletes the brief/result files. Never tear down unlanded work without explicit user approval.
Context hygiene
- Fleet-workers get self-contained briefs, never conversation history or prior task results; chained tasks reference the merged code, not transcripts.
- You consume result files and targeted diffs; read pane output only on //timeout/steer, capped at 120 lines. Never scroll-scrape a pane in place of the result file.
- Relay synthesis to the user in your own words with file:line references; never paste raw reports or full diffs.
State files
| File | Purpose |
|---|
| Enforcement flag; presence = fleet mode on |
| Fleet-worker harness/model/effort (asked once, kept until the user changes it) |
| Backlog + status table (source of truth across restarts) |
| Fleet-worker's completion report (deleted at teardown) |