The land order
With many sessions running, the slow part stops being writing code and becomes getting it merged. Each landing takes the same steps: sync from the base, hand any conflict back to the agent, run the checks on the merged tree, merge. The land order runs those steps for you, one session at a time per base branch, and always shows why the head isn’t moving when it isn’t.
Land a session
Section titled “Land a session”Any of these puts a session in its project’s land order:
- Land on the status strip at the top of the session;
- l on a selected session in the Fleet;
- Land on a lane in Compare;
- Add to land order in the Merge dialog;
- Land this session (queue it) in the ⌘K palette.
Land is offered once the session has something its base doesn’t: commits ahead, or uncommitted changes. After you press it, the button becomes the session’s live place in the order, such as Queued #2, Syncing…, Conflict → agent, Checking… or Landing…. Click it to open the order, or press × to take the session out. When it lands you see landed ✓. If it’s ejected you see Ejected, with a Retry button.
Open the order itself from the Land order row under the project in the sidebar. The row shows how many sessions are pending, and a red dot if any were ejected. You can also use Land order · project in the ⌘K palette.
A merge queue with no staging branch
Section titled “A merge queue with no staging branch”A typical merge queue stacks queued work on a shared staging branch. Then one contested file blocks everyone behind it, and the conflict belongs to nobody. The land order keeps the useful parts of a merge queue (one landing at a time, automation, visibility) and drops the staging branch:
- Every entry syncs from the real base, into its own worktree, and lands straight onto the real base. No session’s work reaches another session’s branch except by landing on the base first.
- Entries are grouped into lanes, one per base branch in the project’s repo. At most one entry per lane is between sync and landed at a time, so the check tests exactly the tree that will land. Lanes don’t wait on each other.
States
Section titled “States” ┌──────── base moved, or you have the files open ───────┐ ▼ │Queued ──ready──▶ Syncing ──clean──▶ Verifying ──ok──▶ Landing ──────┴──▶ Landed ▲ │ │ │ │ conflict checks failed merge failed │ ▼ │ │ └──resolved──── Conflict ▼ ▼ └──still stuck after 2 hand-offs──▶ Ejected (with a reason)| State | What’s happening |
|---|---|
| Queued | Waiting its turn, or waiting on something it names. |
| Syncing | Merging the base into the session’s branch, in its worktree. |
| Conflict | The sync stopped on conflicts. Its own agent has them. |
| Verifying | Running the session-integrate checks on the merged tree. |
| Landing | Merging into the base. |
| Landed | Done. The history records the landed commit. |
| Ejected | Taken out of the order, with a reason, for a person to look at. |
The transitions:
-
Queued → Syncing when the entry is ready. An entry isn’t ready when:
- its agent is mid-turn, or waiting for an answer;
- a hook is running;
- an earlier sync still has unresolved conflicts;
- landing would overwrite files you’re editing in the base checkout;
- there’s another blocker, such as the base not being checked out anywhere.
The entry shows what it’s waiting on.
-
Syncing → Verifying when the base comes in cleanly, or when rerere replays an earlier resolution.
-
Syncing → Conflict when the sync stops. The conflict is handed to the session’s own agent, in its own worktree, with the note described in Sync from base, plus one line saying the land order will sync again and land once the merge is committed.
-
Conflict → Queued once the merge is no longer open, whether the agent committed it or someone aborted it. The entry syncs again, since the base may have moved meanwhile.
-
Conflict, still stuck: if the agent finishes a turn and the merge is still open, the conflict is handed over once more. After two hand-offs the entry is ejected: its agent couldn’t resolve the conflicts in …. The conflict stays in the worktree for you.
-
Verifying → Landing when the checks pass, or when there are no checks to run. Failing checks eject the entry: npm-test.sh failed on the merged tree.
-
Landing → Queued when the base moved after the sync. What was checked isn’t what would land, so the entry goes round again. This is normal and doesn’t count as a failure. After more than three in a row it says the base keeps moving.
-
Landing → Queued as well when merging would overwrite your uncommitted edits in the base checkout. It waits for you instead of failing.
-
Landing → Landed when the merge succeeds. Uncommitted work in the session is committed onto its branch first.
If someone merges a queued session by hand, the order notices there’s nothing left to land and records it as landed (merged by hand).
A blocked entry never holds up the rest
Section titled “A blocked entry never holds up the rest”The runner doesn’t take the first entry in a lane. It takes the first ready one.
Consider three sessions queued for main:
auth-fixhas a conflict its agent is working through;docs-passchanges a file you have open in your project folder;retry-wrapperis ready.
retry-wrapper lands. The other two keep their places and say why they’re waiting.
Each lane’s header answers “why isn’t it moving?” in one line:
into main "auth-fix" is resolving conflicts in src/api/client.ts with its agent. 1 behind it can land meanwhile.Working the order
Section titled “Working the order”Select an entry by clicking it, or with j / k.
| Key | Button | What it does |
|---|---|---|
| j / ↓, k / ↑ | Select the next or previous entry. | |
| K / J | ↑ / ↓ | Move the selected entry sooner or later. |
| h | ⚡ Hotfix | Toggle hotfix. A hotfix goes before every normal entry. |
| p | ⏸ Pause / ▶ Resume | Pause the whole order. Nothing new starts until you resume. A conflict that gets resolved still rejoins the queue. |
| x / Delete | Remove | Take the entry out of the order. This is refused while a step is running on it. |
| ↵ / o | Open | Open the session. |
Finished entries move to Recently, which keeps the last 50:
- Retry puts an ejected entry back at the end of the order, starting from the beginning, with its hand-off and attempt counts reset.
- × dismisses a line from the history.
While a step (sync, check or land) is running on a session, a manual merge, sync or sync abort on it is refused: pause the order or wait a moment.
Deleting a session takes it out of the order.
Options
Section titled “Options”Options on an entry sets three things for that entry:
| Option | Default | Effect |
|---|---|---|
handoff |
submit |
Hand a conflict to the agent and start its turn. Untick it (paste) to leave the note in the composer for you to send. |
requireVerify |
off | Require session-integrate checks. With it on, an entry with no session-integrate scripts is ejected instead of landing on “it merges”. |
deleteAfter |
off | Delete the session once it has landed, worktree and branch included. |
The land order submits hand-offs by default. Everywhere else, spwn pastes into the composer and leaves Enter to you. Putting a session in the land order is how you opt in to automation.
session-integrate checks run with prompts declined, since nobody is standing by to
answer them. See Verify the merged result
for how to write one.
Under the hood
Section titled “Under the hood”- The order is saved in
land-queue.jsonin spwn’s app data folder. After a restart, an entry that was mid-step goes back to Queued, because every step is safe to redo. A conflict stays with its agent. - The runner looks at the order every 5 seconds, and immediately when a turn ends, a session’s status changes or a hook finishes.
- Every transition is pushed to the page as it happens, so the order and the Land button are never staler than the websocket.
- The steps are the same sync, verify and merge the Merge dialog uses. The land order does nothing you couldn’t do by hand. It does them in order, without being asked each time.
The state Awaiting approval appears in the progress steps but is never entered yet. Landing approvals will come with team roles.
The design is in design/013-land-order.md.