Skip to main content

Agents work on any branch

An agent could push only its task's own branch, mc/<task id>, and open pull requests only from it, and an agent on a task without a project could only read detached checkouts without their submodules. People want agents to work on pull requests that already exist, too: switch to their branch, commit, push, and open pull requests of their own. We decided that an agent may check out and push any branch, the default branch included, and that the task branch is simply whatever the task's checkout has checked out.

  • Switching goes through checkout_branch, given a branch or a pull request's number. It fetches with the forge's token, which agents do not hold (ADR 0004), fast-forwards the local branch to the forge's, and moves the submodules to the commits the new branch pins. Given from, it creates the branch there.
  • Pushing pushes what is checked out, never forced. Mission Control refuses no branch: what guards the default branch is the forge's branch protection.
  • Submodules each follow their own branch (replacing ADR 0007's single task branch). Switching the superproject puts each submodule on a branch of the same name, created at the pinned commit when it has none; checkout_branch given a submodule's path switches only that one, to any branch or pull request of its own repository. Pushing pushes each submodule's own branch, and its pull request is opened from it.
  • A pull request an agent checks out is recorded on its task, like one it opened, so the task shows it, its agent reads its reviews and may merge it under the usual rules. One pull request can be on several tasks. open_pull_request changes the title and description only of pull requests Mission Control opened; anyone else's is left as its author wrote it.
  • Every task gets real checkouts, whether or not it has a project: clone_project checks any project out with its submodules, on a branch, and keeps it between calls. A task's project is where it is filed and is checked out when a run starts; it does not limit the projects the task may change, such as the deployment of the app it is about. The tools above name the project to act on, and its pull requests are recorded on the task.
  • Conversations stay read-only: their checkouts have submodules but are not pushed. To change a branch or a pull request, the agent creates a task for it (ADR 0012), whose agent does the work.

Considered Options​

  • Refusing the default branch, or every protected one: rejected. Which branches take direct pushes is the forge's setting, where it holds for people and agents alike; a second list here would only disagree with it.
  • Recording the task branch on the task: rejected; it would have to be kept in step with the worktree, and a git switch in the agent's shell would quietly make them disagree. Reading what is checked out cannot be out of date.
  • A fetch tool, with switching left to git in the agent's shell: rejected; submodules are worktrees of their own clones that git submodule does not know (ADR 0007), so only Mission Control can move them with the switch, and agents would need several steps to get right.
  • Submodules always on the superproject's branch name: rejected; a pull request in a submodule whose branch has another name could not be worked on.
  • A pull request on one task only: rejected; a second task working on it would neither show it nor be able to merge it.
  • Full git in conversations: rejected; their checkouts would hold branches for as long as the conversation lasts, and pull requests are shown on tasks, which conversations are not.

Consequences​

  • Anything pushed straight to a branch the forge leaves unprotected skips review, including the default branch. The approval rule still holds for what an agent merges.
  • A branch is checked out in one place at a time: git refuses a branch another worktree has checked out, so a second task asking for it is refused until the first's worktree is removed. The checkouts in a task's directory are therefore removed when the task is done, although the directory is kept.
  • Local branches are shared by every task on the repository, as they live in its bare clone. A local branch with commits the forge's lacks, and missing some it has, is refused rather than merged or overwritten.
  • Uncommitted changes block a switch, as they block a push.
  • mc/<task id> is only where a task starts; agents choose other branch names with checkout_branch. A subtask's pull request goes into the branch its parent's checkout of the same project has checked out, or mc/<parent id> once that is gone, when the forge has it, and else into the default branch. (A subtask no longer starts from its parent's branch.)
  • An agent that switches with git in its shell is followed, but its submodules stay where they were; agents are told to switch with checkout_branch.