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. Givenfrom, 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_branchgiven a submodule'spathswitches 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_requestchanges 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_projectchecks 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 switchin 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
gitin the agent's shell: rejected; submodules are worktrees of their own clones thatgit submoduledoes 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 withcheckout_branch. A subtask's pull request goes into the branch its parent's checkout of the same project has checked out, ormc/<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
gitin its shell is followed, but its submodules stay where they were; agents are told to switch withcheckout_branch.