Tasks that span several repositories
How a single task works across a set of repositories — up to ten — with its own checkout, base branch, and merge request per repo.
A task doesn’t have to build in exactly one repository. It works against a repo set: none, one, or up to ten repositories at once. Most tasks still touch a single repo, and nothing changes for them. But when a change genuinely spans a service and its client, or a library and the app that consumes it, you can attach every repo it needs to the one task and let CodeHerder run and land the work across all of them together.
Attaching repos to a task
Attach repos when you create the task by passing --repo more than once:
ch task create --title "Rename the shared config key" --repo api --repo web-client
--repo takes a repo’s name or its ID, and you can repeat it as many times as you need. Leaving it off entirely files a task with no repo attached yet — see Tasks with no repository at all, below, for what that means. The web app’s New task form has the same option: a Repos field where you check off however many repos the task needs, left empty for a repo-less task.
Once the task exists, you can only change its repo set from the CLI or the API:
ch task repos attach <taskId> <repo>
ch task repos detach <taskId> <repo>
Attaching a repo that’s already in the set is a no-op, and detaching one that isn’t attached is a no-op too — both are safe to run more than once.
Seeing which repos a task works in
ch task repos <taskId>
lists every repo in the set. ch task show <taskId> also prints a repos: line summarising the set — a task with nothing attached reads repos: none attached, so the row is always there even when it’s empty.
The task page in the web app shows the same thing in a Repos section, with each repo’s name and Git URL. A repo CodeHerder added on its own carries a Discovered badge (more on that below), and a repo that’s since been archived is marked Archived. A sandbox that’s already running keeps that repo’s checkout, but an archived repo drops out of the task’s set the next time it needs a fresh sandbox — so unarchive it, or attach a replacement, before the task moves on.
When you don’t attach any
If a task’s own repo set is empty, CodeHerder still has to pick something to build in. It works through this order:
- The task’s own attached repos, if there are any — this always wins, however many are attached.
- The workspace’s default repo, if one is set.
- The workspace’s one active repo, if it has registered exactly one directly on it.
- If none of those resolve anything, see Limits and what goes wrong, below.
This is the same resolution order Connecting repositories describes in full, including how to set a workspace default.
What the agent works in
A multi-repo sandbox gives the agent one checkout per attached repo, side by side, rather than a single working directory. With exactly one repo attached, nothing looks different from before — the agent starts right inside that checkout. With several, it starts one level up, in the sandbox directory that holds all of them.
Every repo in the set shares the same branch name for the task’s work. Where each one starts from is still its own decision, though: a base branch named on the task applies to every repo, and any repo without one falls back to its own default branch, or to whatever its remote’s HEAD points at if it doesn’t have one set either. See Choosing a task’s base branch for the full resolution chain.
How the work lands
Each repo that actually picks up commits gets its own merge request; a repo in the set that ends up with nothing to change simply doesn’t get one. Every request is recorded against the task with:
ch task set-merge-ref <taskId> <url>
Run this once per repo — recording a second URL for a repo you’ve already recorded updates that row, and a URL for a different repo adds a new one. ch task show reflects the result: with one request it prints the familiar single merge ref: line, and with more than one it prints merge refs: N of M merged followed by one line per request. The web app’s task page mirrors this with a Merge refs row and one badge per repo. A task isn’t considered landed until every repo’s request has merged — and if the repos live on different hosts, each request merges through that host’s own tooling.
Repos CodeHerder attaches for you
If a session ends up working in a repo nobody attached, CodeHerder attaches it to the task on its own and marks it Discovered, so long as that repo is already registered in your workspace, or inherited from a parent group. A checkout that matches nothing you’ve registered is left alone: CodeHerder never adds a new repository to your workspace on its own — that’s still a decision you make. There’s nothing to turn on for discovery itself — it just happens. A discovered repo shows up in ch task repos and the web app’s Repos section exactly like one you attached yourself, just with the badge to tell you how it got there.
Reviewing a task that spans several repos
Once a task’s change touches more than one repo, its change shape breaks the total down by repo: ch task show prints the overall rollup and then one indented line per repo, and the task page shows the same breakdown underneath the summary line. A task working in a single repo sees no difference here. See Change shape for what the individual numbers mean.
Limits and what goes wrong
A sandbox can provision at most ten repos at once. Declare more than that on a task and CodeHerder won’t try to work around it — the task is parked blocked immediately, with a note naming every repo you declared and telling you to trim the set with ch task repos detach.
A couple of other repo-shaped problems land the task in the same blocked state, each with its own explanation attached:
- Nothing to resolve — a repo is in play somewhere in the workspace or its parent groups, but nothing selected exactly one. This includes a repo that’s only inherited from a parent group: it doesn’t count toward the automatic workspace-default or sole-repo tiers unless you also point the workspace’s default repo at it directly. The blocker names every candidate it found and tells you to either attach a set on the task or set a workspace default.
- Two attached repos would collide on disk — if two repos in the set would end up needing the same folder name inside the sandbox, CodeHerder refuses to provision either one rather than let them overwrite each other.
A repo that fails to clone is handled differently — it isn’t an immediate block. CodeHerder retries automatically a few times, the same as a single-repo task hitting a clone problem (see Git access to clone the repository in Connecting repositories). If any one repo in the set can’t be cloned, the whole sandbox is abandoned rather than half-provisioned, the failure names which repo caused it, and repos that had already cloned successfully aren’t re-cloned on the retry.
None of these produce a silent failure — check ch task blockers <taskId> (or the Blockers panel in the web app) any time a multi-repo task seems stuck, and see Why isn’t my task moving? for the full diagnostic path.
Tasks with no repository at all
A task with no repo in play anywhere — nothing attached, no workspace default, no single obvious repo — isn’t a failure case. CodeHerder gives it a sandbox with no checkout at all and runs it normally: the agent works through task fields, comments, and wiki pages instead of opening a merge request. Because there’s no request to point at, the usual requirement to record one is waived, so a repo-less task can still move all the way through to done.
Related guides
- Connecting repositories — register a repo, set a workspace default, and the full single-set resolution order
- Choosing a task’s base branch — how a base branch resolves per repo
- Change shape — reading the size and spread of a task’s change, including the per-repo breakdown
- Reviewing an agent’s work — confirming a merge request has landed, including a multi-repo task’s several requests
- Sweeping every repo in your org — a different shape of multi-repo work: one instruction fanned out into a separate child task per repo, rather than one task working across several at once
- Why isn’t my task moving? — diagnosing a task stuck on an unresolved or over-the-limit repo set
