> For the index of every page in Orbital's docs, read https://docs.beta.runorbital.dev/llms.txt.

# Worktrees and delivery

> Code workflows work in a worktree, open a pull request and see it merged

Give a workflow a worktree step when it must change files without touching your own checkout. A worktree is an isolated copy of a repository, on a branch of its own. This workflow makes one, changes it, and removes it only when nothing would be lost:

```dot title="worktree.dot"
digraph worktree {
  graph [version="1", entry="checkout"]
  checkout [shape="folder", action="create"]
  implement [prompt="Make the change the goal describes and commit it."]
  cleanup [shape="folder", action="remove"]
  done [shape="Msquare"]
  kept [shape="Msquare", outcome="failed"]
  checkout -> implement
  implement -> cleanup
  cleanup -> done [condition="worktree.clean == true", weight="1"]
  cleanup -> kept
}
```

A worktree is optional. A run gets one only when its workflow has a worktree step. `explain-repo`, the research, follow-up and triage guides, and most workflows that only read and write text have none.

The shipped code workflows create a worktree first. When the work passes review, they open a pull request and repair it until it merges or closes. Then they remove the worktree, unless that would lose work.

## Worktrees

A worktree step with `action="create"` makes one worktree for each repository folder of the [project](/projects/). All of them share one branch name, `orbital/<id>`. They live under `~/.orbital/worktrees/`. Plain folders, which are not Git repositories, are used where they are. See [plain folders and Git](/projects/#plain-folders-and-git).

From then on, each agent step starts in the primary folder's worktree and reaches the other worktrees as additional folders. Before a worktree exists, a step works in your own folders. That is why `explain-repo`, which makes no worktree, only reads. Its prompt tells it not to change files.

The dock's **Changes** tab shows what the run changed in its worktree, and **Files** shows the worktree's files. **Open in** opens the worktree in your editor.

## Removing a worktree

A worktree step with `action="remove"` removes the run's worktrees only when nothing in them would be lost. It keeps a worktree that holds uncommitted changes or commits nobody has pushed, and records why in `worktree.status`. The shipped workflows then end on a terminal that says the worktree was kept, so you can look.

Never delete a kept worktree to make a run look finished. Commit and push what you need first.

Archiving a run also removes its worktrees.

## Delivery

The shipped delivery workflows share one delivery sub workflow, `pr-delivery`. It runs these stages:

| Stage | What happens |
| --- | --- |
| Publish | An agent step pushes the branch and opens the pull request, following the repository's conventions. When the step ends, Orbital asks GitHub to squash-merge the pull request once its checks and reviews allow it. A pull request that can merge already merges at once. If the repository does not allow auto-merge, a rule blocks it, or the token lacks permission, the run's history records that and delivery carries on. |
| Watch | A probe reads the pull request's state from GitHub. |
| Wait at a gate | While the pull request waits for review or merge, the run waits at a human gate, "The pull request is waiting for review or merge". It shows a clock and "Waiting: The pull request is waiting for review or merge", and takes no run slot. Orbital looks at the pull request once a minute and carries on by itself when something changes. |
| Repair | Conflicts, failing CI and requested changes each have a repair step. A repair that changes the branch is reviewed against its reason alone, then published again. |
| End | A merged pull request ends the run Succeeded. A pull request closed without merging ends it Failed. |

Choose **Skip** at the gate, or **Resume**, to make the run look at the pull request at once. [Add gates and waits](/workflows/gates-and-waits/) explains gates.

![A gate step reads The pull request is waiting for review or merge, with the waiting time, the next check and a Skip button.](/screenshots/gate-wait.webp?v=fdf72f63d4)

*Skip makes the run check the pull request now.*

:::caution[Pull requests can merge with no review]
Delivery never approves a pull request. It asks GitHub to merge it once your branch rules allow. On a repository that requires no review and no checks, the pull request merges as soon as it opens. Your branch rules decide, so keep required reviews and checks on the branches you care about.
:::

### Turn auto-merge off

No setting turns auto-merge off. It comes from `auto_merge="squash"` on the publish step of `pr-delivery`. That is a sub workflow, not a workflow you can start or open by name. Each delivery workflow imports its own file, `subgraphs/pr-delivery.dot`, from beside it. A separate `pr-delivery` of your own is never used. Instead, take your own copy of each delivery workflow you start, and delete `auto_merge="squash"` from that copy's `pr-delivery`.

Every shipped workflow except `explain-repo` delivers through `pr-delivery`. `implement-ticket` and `implement-epic` reach it through `deliver-ticket`.

To do it in the app:

1. **Open the workflow.**

   Open **Workflows** and open the shipped workflow you use, for example `pursue-goal`.

2. **Find the delivery file.**

   Open the **Prompt docs** tab and select `subgraphs/pr-delivery.dot`. It shows "Imported by delivery", or "Imported by a nested workflow" for `implement-ticket` and `implement-epic`.

3. **Remove the attribute.**

   Delete `auto_merge="squash"` from the `publish` line, then save.

Saving stores your own copy, with its imported files, under the same name. The list shows it as Custom with "Hides Shipped". **Revert** brings the shipped one back.

To do it with files, copy the workflow's `.dot` file, with its `subgraphs/` and `prompts/` folders, into `~/.orbital/workflows/` or the project's `.orbital/` folder. Delete `auto_merge="squash"` from the copy's `subgraphs/pr-delivery.dot`. A workflow there wins over a shipped workflow of the same name. The [Ticket to merge](/examples/workflows/ticket/) example holds the same delivery files.

[Auto-merge](/reference/attributes/#auto-merge) describes the attribute.

### When GitHub never runs a check

Sometimes GitHub does not run a required check at all, for example because of billing or no runner. The run is then Paused on the probe step, with GitHub's reason and the check names. See [standings](/runs/#standings). Fix the cause on GitHub, then choose **Resume**.

### Several repositories

A project with several repository folders gets one pull request per repository that changed. The run treats the set as one delivery. It merges only when every pull request merged, and it repairs the worst state first. The dock's **Pull requests** section lists them all.

![The dock's Pull requests list shows open and merged pull requests, each with its branch.](/screenshots/pull-requests-panel.webp?v=612ac483b1)

*Every pull request of the run, with its state.*

## The GitHub token

Delivery reads pull requests, checks and review threads through a GitHub token. Orbital tries these in order and uses the first it finds:

1. the token saved in **Settings > GitHub**
2. `GH_TOKEN`
3. `GITHUB_TOKEN`
4. an existing `gh auth login`

The page shows which one is in use. See [Settings](/reference/settings/#github).

![Settings, GitHub page shows a saved token and the token sources in order: saved token, GH_TOKEN, GITHUB_TOKEN and gh auth login.](/screenshots/settings-github.webp?v=f898ede9d3)

*The page names the token source in use.*

## Related

- [Add gates and waits](/workflows/gates-and-waits/): how the run waits for a pull request
- [Implement a feature](/examples/feature-pull-request/): a delivery workflow from goal to merge
- [Ticket to merge](/examples/workflows/ticket/): the delivery sub workflow file by file
- [Troubleshooting](/troubleshooting/#a-worktree-remains-after-cleanup): what to do when a worktree remains
