Skip to content

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

Fleet fetches and starts the branch from 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

Ten consecutive ports per session, inside 3100-3999, derived from the session id, so two dev servers do not both take 3000 and the same session gets the same block every time you come back to it.

Run scripts you can actually kill

A run child is spawned into its own process group and stopped by signalling the group. Signalling the process alone reaches npm and leaves the vite it forked holding the port, and the next start collides with something nothing in the UI can point at.

A pty per session

A real shell in the worktree, with escape sequences intact so it renders. The run log strips them, because it is a list of strings and would otherwise show the escapes as noise. Every pty is killed before a worktree is removed, since a shell holds its directory open.

Setup failure is not session failure

A worktree whose setup script exited nonzero is still a worktree an agent can read code in. Fleet marks the setup failed, keeps the log, and keeps the session, rather than discarding the prompt you already sent.

Archiving is reversible, deleting is not

Archiving reclaims the checkout and keeps the record, the transcript and the branch, so restoring rebuilds the worktree at the same branch. Deleting is the only unrecoverable one, and it is the only one that removes the transcript.
Example sessions, each with its own branch, its own worktree and its own block of ports.
branchworktreeports
fleet/railacme.fleet/rail3140-3149
fleet/reviewacme.fleet/review3210-3219
fleet/storeacme.fleet/store3380-3389

blocks are 10 ports wide, inside 3100-3999, and derived from the session id

what happensPlanplanAskdefaultAutoacceptEditsBypassbypassPermissions
Edit a file inside the worktreenot allowedasks youallowsallows
Run a command inside the worktreenot allowedasks youallowsallows
Reach outside the worktreenot allowedasks youasks youallows

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.

Auto is not about which tools are safe. It allows tool use and keeps the boundary at the worktree, which is why the third row is the only one where auto and bypass disagree.

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.

  1. 01plan mode

    Nothing 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.

  2. 02spawn_agent

    Each 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.

  3. 03request_review

    An 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.

  4. 04review_agent

    The 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.

  5. 05merge_agent

    Work 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.

  6. 06open_pull_request

    One 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
After open_pull_request

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.

Work reaches the orchestrator through merge_agent, into its own worktree. Your base branch is untouched until the pull request.

The next prompt is a decision first

While a round is sealed, spawning, merging and opening a pull request all refuse. The orchestrator has to say whether your next prompt is a new feature or an amendment to the one just delivered, because only it can tell the difference.

A new round moves the branch, and retires the old agents

Starting one moves the orchestrator’s worktree onto a fresh branch, from origin once the last round has merged and from the sealed branch while its pull request is still open, so the next feature is not written against code the agents cannot see. The round’s agents are archived: their checkouts come back, their transcripts and branches stay.

An agent has no composer

You type to the orchestrator. An agent’s view has a stop button and permission cards, because interrupting is not an instruction and only you can answer a permission, but the field is absent rather than disabled. Two bosses on one branch leaves the orchestrator planning around a brief its agent has already been talked out of.

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.

src/main/agent/orchestration/squadOps.ts+2 -1
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.

Ship
  • $ 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

The file may have changed between writing a note and sending it. Each note is re-anchored against the current diff, and one whose line is gone is marked outdated in the panel and again in the composed prompt. Deciding a note no longer matters is your call, so nothing is dropped quietly.

A note on a worker goes to its orchestrator

Instructions flow one way, so review notes are routed the same way a brief is. The agent hears it from its boss, which is the only voice it has.

Checkpoints, on a ref of Fleet’s own

Every turn is captured as a tree on 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

A proposed plan is a document, so it opens as a full height pane beside the conversation rather than as a card in the feed that scrolls away. It is amber only while it still owes an answer. An approved plan is a reference, and reopens from the session bar.
squadOps.tsanchored
41export function mergeAgent(
42 if (sealed) throw SEALED
43 return git.merge(branch)

“Refuse before the merge, not after.”

The diff has not moved. The note is on the line it was written on.

squadOps.tsshifted
57 await assertUnsealed(squad)
58 if (sealed) throw SEALED
59 return git.merge(branch)

“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.

squadOps.tsorphaned
57 await assertUnsealed(squad)
58 return git.merge(branch)
59}

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.

A note carries its text, its context and a hash of the diff it was written against, so re-anchoring is a search rather than a bet on a line number.
refs/heads/
  • main

    your base branch

  • fleet/checkout-flow

    the session’s branch

reads here

  • git log
  • git branch
  • your diff against base
refs/fleet/checkpoints/

<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.

A checkpoint is a real commit holding a real tree. It is simply not on a branch, so nothing you or your team runs will ever list it.

04. Everything else

The rest of it, without the adjectives.

Checkpoints

Undo a turn without touching your history

Every turn is captured as a tree on refs/fleet/checkpoints, a Fleet-only ref. It is never a commit on your branch, so it cannot appear in your log or in your diff against base. Restoring puts tracked content back and deliberately does not delete a file the turn created.

MCP

Servers carry their provenance

A server you type into Settings is trusted the way a shell profile is. One declared in a repository arrives disabled, and its own enabled flag is ignored, because the repository is not the party being trusted. Approval is keyed to a fingerprint of the definition, so editing the command does not inherit the approval.

New project

30 templates across 9 toolchains

Node, uv, cargo, go, composer, Flutter, Swift, XcodeGen and dotnet. Fleet scaffolds, commits, publishes and registers the repository, then hands the first session a brief that already knows what the project is for. Integrations install the dependency and write blank env keys. They do not generate the wiring.

Usage

A ledger per turn, not a running total

Each row is one turn and carries that turn alone, attributed solo, orchestrator or worker. A row that copied the session total would bill every turn for all of its predecessors, and every chart would double count.

Command palette

Every action from the keyboard

New session, new squad, new project, move between sessions, toggle the review panel, open a worktree in an editor, settings, usage, diagnostics. It is the fastest way around a rail with a dozen sessions in it.

Themes

Twelve, from two attributes

Four schemes and three accents on the root element. Schemes own surfaces and text, accents own only the accent family. Every accent stays in the blue to violet band on purpose: cyan, amber, green and red are already spoken for by status.

Notifications

You find out when an agent needs you

A native notification when a session is blocked on you, when one fails, and when one finishes its turn, so a squad can run while you are in another window without becoming something you have to poll. A worker finishing is not one of them: that is its orchestrator’s turn, not yours.

Editors

Open any worktree where you actually work

VS Code, Cursor, Windsurf and Zed, jumping to the line, plus Finder and Terminal. Fleet probes your login shell PATH for each, so the menu lists what you have and nothing you do not.

One brief in. One pull request out.

Download for macOSv0.9.0 · 135 MB

Apple silicon. Intel Macs are not supported.