Submodules are worktrees of their own repositories
Since ADR 0016, each submodule follows a branch of its own rather than the task branch.
A task's worktree checks out each submodule its repository records as a worktree of that
submodule's own bare clone, nested at the submodule's path and on the task branch, starting at the
commit the repository pins. Bare clones are shared per repository as before, whether a project or a
submodule needs them (services/workspace/workspace.ts). A submodule URL is resolved against the
superproject's when relative, and an SSH URL is moved onto the forge's HTTPS address, keeping its
path: Mission Control holds the forge's token, not SSH keys. Only submodules on the task's forge get
the token, and only they can be pushed.
Pushing the task branch first pushes the same branch of every submodule pinned to a commit that is not on that submodule's default branch, and opening the pull request opens one per such submodule, listed in the task's own. The task's pull request merges only once the latest of those in each submodule is merged, and they merge with a style that keeps their commits (a merge commit, else a fast-forward), so the commits the superproject points at reach the submodules' default branches.
Considered Options
git submodule update --initin each worktree: rejected; every task would clone every submodule again into the superproject'smodulesdirectory, outside the shared bare clones, and SSH URLs would need rewriting throughinsteadOfconfig for every command an agent runs.- Leaving submodules alone: what we had. Their directories stayed empty, and an agent could neither read nor change them.
- Squash-merging submodule pull requests the repository's way: rejected; squashing (or rebasing) makes new commits, so the superproject would pin one that is on no branch.
Consequences
- One level deep: submodules of submodules stay empty.
- SSH URLs are moved onto the forge whatever their host, since forges often serve SSH under another host name than HTTPS (ssh-code.example against code.example). A submodule on some other host's SSH is therefore looked for at the same path on the forge: it fails to fetch when there is no such repository, and is the wrong repository in the unlikely case that one exists.
git submodulecommands do not know these checkouts (they are not under the superproject'smodules), so agents are told not to run them;git status,git add <path>and commits in the superproject work as usual.- A submodule that cannot be fetched (a local path, an unreachable host) is listed to the agent as not checked out, with why, and the run goes on. One off the forge can be read but not changed: pushing a commit to it is refused.
- A repository whose forge allows neither merge commits nor fast-forwards cannot have its submodule pull requests merged by Mission Control; the refusal says to allow one of them.