Features
The long version.
Four mechanisms, named rather than described: where an agent works, who tells it what to do, how its output reaches you, and what the app does around all three.
01. Isolation
A worktree is a real second checkout, not a sandbox.
Every session gets one: the same repository, a different directory, its own branch, and full access to your machine from inside it. That is what makes the agents in a squad genuinely independent rather than politely taking turns.
Started from origin, never from your local base
origin/<base>. Creating a worktree from the base branch as it sits on your disk is how an agent ends up working against a week old main. It fetches and never pulls, because fetching only writes remote tracking refs, and moving your own checkout to serve a background session is the merge trap wearing a different hat. Offline, or with no remote at all, it falls back to the local branch rather than refusing to start.A deterministic block of ports
Run scripts you can actually kill
A pty per session
Setup failure is not session failure
Archiving is reversible, deleting is not
| branch | worktree | ports |
|---|---|---|
| fleet/rail | acme.fleet/rail | 3140-3149 |
| fleet/review | acme.fleet/review | 3210-3219 |
| fleet/store | acme.fleet/store | 3380-3389 |
blocks are 10 ports wide, inside 3100-3999, and derived from the session id
Two things Auto never answers for you
ExitPlanMode. Approving a plan is a decision about what happens next, by definition, so it stops and waits even in Auto. Approving moves both halves at once: the mode the SDK is in and the mode Fleet shows you.AskUserQuestion. The answer is not something a policy can supply. It is collected from you and handed back as the tool’s input, which is also why a client that does not implement it does not look bad, it hangs.
Bypass is the exception, and the exception is the point of it: under bypassPermissionsthe SDK never invokes the permission hook at all, so nothing checks anything, these two included. An orchestrator’s own ask_user still reaches you in every mode, because that tool blocks on your answer rather than on a permission.
02. The orchestrator
One round is one feature, one branch, one pull request.
A squad is one orchestrator session driving several worker sessions, and each worker is a full Fleet session in its own worktree. Nothing about it is simulated. The unit of work is the round, not the squad, because a squad outlives the feature it was created for.
- 01
plan modeNothing runs until you approve the plan
An orchestrator starts in plan mode. Its ExitPlanMode call arrives as an ordinary permission, so the plan gate is a card you answer, not a separate mechanism. Approving moves both halves at once: the mode Fleet shows you and the mode the SDK is in.
- 02
spawn_agentEach agent gets a brief and a checkout of its own
The orchestrator writes the brief, names the files that agent owns, and spawns it. A new worktree, a new branch, a full Fleet session. Nothing about it is simulated.
- 03
request_reviewAn agent commits, hands back, and stops
A branch is the only way an agent’s work reaches anyone, so uncommitted changes do not exist. Having requested review it stays idle until it is answered, which is what keeps a squad cheap between rounds.
- 04
review_agentThe orchestrator answers: notes, or approval
Notes go back and the agent picks up where it left off, with its context intact. There is no second boss: you type to the orchestrator, and an agent’s view has no composer at all.
- 05
merge_agentWork lands in the orchestrator’s worktree, never on your base branch
This is the whole difference. Merges happen into the integration branch the orchestrator is holding. Your main is untouched until you decide otherwise, and the orchestrator is the one resolving conflicts between its agents.
- 06
open_pull_requestOne pull request, and the round is sealed
Opening it seals the round. While it is sealed, spawn_agent, merge_agent and open_pull_request all refuse, because anything they did would land on a branch you have already been asked to review. The next prompt starts a new round or continues this one, and that is the orchestrator’s call because only it can tell a new feature from an amendment.
- running
- blocked on you
- done
- failed or refused
- idle
The round is sealed. These three refuse until the orchestrator says whether your next prompt is a new feature or an amendment to the one just delivered:
- refusesspawn_agent
- refusesmerge_agent
- refusesopen_pull_request
Anything they did would land on a branch you have already been asked to review. start_roundbegins a new feature on a new branch and retires this round’s agents. continue_round keeps the branch and the pull request, and only clears the seal.
The next prompt is a decision first
A new round moves the branch, and retires the old agents
An agent has no composer
03. Review
Nothing merges that you have not read.
The review panel is beside every session, not at the end of one. Staged and unstaged, file by file, with notes you write on lines and send as a single instruction.
export function mergeAgent(squad: SquadRecord, agent: SessionRecord) {- return git.merge(agent.branch)+ if (squad.sealed) throw new FleetFailure(SEALED)+ return git.merge(agent.branch, { into: squad.branch })}
note on line 3
Refuse before the merge, not after. A sealed round should not touch the branch at all.
- $ git -C acme merge --no-ff fleet/rail
- $ git push --set-upstream origin fleet/rail
- $ gh pr create --base main --head fleet/rail
Every action shows the exact commands before it runs them. Merges run from the main worktree under a per-repo lock, and refuse rather than stashing anything on your behalf.
Notes re-anchor, or say they could not
A note on a worker goes to its orchestrator
Checkpoints, on a ref of Fleet’s own
refs/fleet/checkpoints. It never becomes a commit on your branch, so it cannot appear in your history or in your diff against base. Restoring puts tracked content back and does not delete a file the turn created, because that would mean a clean, and a clean cannot tell a new source file from a .env.local a setup script planted.A plan reads in a column
“Refuse before the merge, not after.”
The diff has not moved. The note is on the line it was written on.
“Refuse before the merge, not after.”
The agent added sixteen lines above it. The same text was found lower down, so the note went with it and says it moved.
no longer in the file
if (sealed) throw SEALED
“Refuse before the merge, not after.”
The line is gone. Fleet says so, in the panel and in the prompt, rather than pointing the note at whatever now occupies line 58.
main
your base branch
fleet/checkout-flow
the session’s branch
reads here
- git log
- git branch
- your diff against base
<session id>
one ref per session, up to 50 deep
a1f4c0e
before “add the retry”
9c2b871
before “fix the test”
4de0aa3
before “rename it”
reads here
Fleet, and nothing else
Restoring is two independent choices. Putting the files back returns tracked content and deliberately does not delete a file the turn created, because that would mean a clean, and a clean cannot tell a new source file from a .env.local a setup script planted.
Rewinding the conversationstarts a fresh Claude session. The model’s context is held server side and there is no way to truncate it, so the agent keeps the repository and loses what it worked out along the way. That is a real limitation and Fleet says so rather than implying an undo.
04. Everything else
The rest of it, without the adjectives.
Checkpoints
Undo a turn without touching your history
MCP
Servers carry their provenance
New project
30 templates across 9 toolchains
Usage
A ledger per turn, not a running total
Command palette
Every action from the keyboard
Themes
Twelve, from two attributes
Notifications
You find out when an agent needs you
Editors
Open any worktree where you actually work
One brief in. One pull request out.
Apple silicon. Intel Macs are not supported.