Skip to content
rev0Docs

Worktrees and branches

The runner works on up to Max concurrent agents tickets at once (Settings > Workspace > Board). Every setup profile sets it to 8; REV0_MAX_PARALLEL only seeds the value on a board that never ran setup. Isolation is automatic:

  • Different working directories run concurrently with no extra machinery.
  • Same git repo: each ticket gets its own branch agent/ticket-<id>, checked out in a dedicated worktree (a sibling <repo>.agent_worktrees/ directory by default, or REV0_WORKTREE_ROOT), so parallel agents in one repo never clobber each other. The agent always creates the working branch; you may pick the base branch it branches off (per ticket on the create or edit form, or --base-branch). Blank auto-detects origin/HEAD, then main or master, then the current branch; REV0_BASE_BRANCH overrides that.
  • Same non-git working directory: tickets run one at a time, since they cannot be isolated.

The worktree persists across resumes and continuations and is committed after every run. A ticket with no repo at all works in a scratch directory instead (below). To reclaim disk, a merged ticket’s Git integration section has Clean workspace, which deletes its worktree.

It is resolved in one order everywhere: the ticket’s own working directory, else the nearest parent’s, else its board’s Default working directory (Settings > Workspace > Board; a board can override it in its Board settings, so each board names its own repo), else no repo at all. Before the first run, the Git integration section shows the answer as Will run in, with where it came from.

That last case is a first-class posture, not a failure: the ticket runs in its own scratch directory (~/.rev0/scratch/ticket-<id>, or REV0_SCRATCH_ROOT) with nothing to merge and no code quality gate, and delivers on the board itself (artifacts, comments, a plan). The directory is tracked in a throwaway git repo of its own, purely so you can review its changes in the diff and commits. That is the right shape for a question, a report, a document or a one-off script. If such a ticket turns out to need version control, the agent can create a repo and switch into it, but only with your approval: the update_ticket MCP tool asks before changing a ticket’s working directory or base branch, naming the target, and an agent that was not asked to create a repo has to justify why a scratch directory is not enough.

While an agent works, its board card shows a ticket-<id> branch chip, and the ticket detail has a Git integration section with the exact branch, base branch and worktree path, so you can tell at a glance that each agent is on its own branch and worktree. The chip then flips to PR open, merged or merge conflict as the ticket integrates. A ticket in a non-git directory reads Runs in place, and a scratch ticket reads Scratch workspace.

Rename the branch or move the ticket to another repo

Section titled “Rename the branch or move the ticket to another repo”
  • Rename the branch: in the Git integration section, click the branch name and type a new one. With a live worktree the branch is renamed in place, so its commits and the later merge follow it. An agent can do the same with its set_branch tool, which asks you first.
  • Move a ticket to another repo, or re-cut its branch off another base: ask the agent. Its set_repo_binding tool checks the change with a dry run, then parks the ticket on a permission request that lists every field it would rewrite (repository, worktree, branch, base) and what it checked. Nothing changes until you approve. A re-cut keeps the old tip on a rescue branch.
  • The base branch is fixed once the worktree exists. Pick it before the first run; afterwards changing it takes a re-cut.

If you change a ticket’s Working directory after it ran, the Git integration section warns Next run will move it, and whatever is on the old branch stays in the old repo.

Integrating a ticket’s branch is an explicit choice, separate from reaching Done, so you can test a change before committing to it:

  • Merge while In Review. An In Review ticket with a git branch shows a Ready to merge banner with a Merge button. It merges the branch into the base branch locally (or, if the ticket opted into “Open a GitHub PR”, opens a PR on GitHub) but keeps the ticket In Review, so you can exercise the change and only then decide. The worktree is kept, so you can re-run or iterate on the ticket and merge again later.
  • Approving to Done asks first. Moving a ticket to Done (the Approve button, the status dropdown, or dragging it into the column) asks: Merge & mark Done, Mark Done without merging or Cancel. You can finish a ticket without ever merging.

When a merge is requested, the runner integrates the branch. By default it integrates locally and never opens a GitHub PR; opening a PR is opt-in, so the runner has no external side effect you did not ask for:

  • Local integration (default, all repos): merges the branch into the base branch locally, and pr_url stays empty. On a real content conflict an agent first tries to resolve it on the branch (REV0_MAX_CONFLICT_ATTEMPTS); if it cannot, the ticket goes to Pending Action with the conflicted files. Move it back to To Do to have the agent resolve and finish again.
  • GitHub PR (opt-in): when the ticket’s Open a GitHub PR toggle is on (create form, --create-pr, or the REV0_GITHUB_CREATE_PR default) and the repo is on GitHub (origin on github.com and gh authenticated), the runner instead pushes the branch, opens a pull request, and posts the PR link on the ticket. By default it then waits for you to review and merge. If the ticket’s auto-merge toggle is also on (create form, --auto-merge, or the REV0_GITHUB_AUTO_MERGE default), the agent merges the PR without a review (REV0_PR_MERGE_METHOD: merge, squash or rebase). Auto-merge only takes effect when “Open a GitHub PR” is on: there is no PR to merge otherwise.

Two failures are kept distinct. A content conflict (above) needs the branch resolved. A dirty base branch (the base’s own working tree has uncommitted changes, so it cannot be merged into safely) is transient: it shows as Waiting to merge, keeps the ticket where it is, notifies once, and retries by itself once the base is clean. The runner never stashes or overwrites uncommitted work in the base tree.

Every tunable named here is in Configuration.