CodeHerderSearch⌘KRequest access →

Tasks that span several repositories

How a single task works across a set of repositories 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 many 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. Leave it off and CodeHerder picks the repo for you — see When you don’t attach any, below. 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, it means the same automatic choice.

If a task needs no checkout at all, such as a scan that only reads fields and wiki pages, pass --no-repo:

ch task create --title "Summarise open questions" --no-repo

The task starts in an empty sandbox even when the workspace has a default repo or several repos. You can’t combine --no-repo with --repo, and the web app’s New task form has no matching option, so use the CLI for this.

Once the task exists, you can only change its repo set from the CLI or the API:

ch task repos create <taskId> <repo>
ch task repos delete <taskId> <repo>

attach and detach still work as older spellings of create and delete. Adding a repo that’s already in the set is a no-op, and removing one that isn’t attached is a no-op too — both are safe to run more than once.

ch task repos create only records the repo on the task. The change applies the next time the task gets a sandbox. To check a repo out into a sandbox that is already running, use ch sandbox repos create. In a read-only stage, that checkout is read-only: it sits at the base commit with no task branch.

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. An archived repo in a task’s set blocks the task the next time CodeHerder starts a session for it. The blocker note names the repo — see Limits and what goes wrong, below.

ch task repos also has a SOURCE column that shows how each repo got into the set: attached by you, chosen automatically (inherited), or discovered.

When you don’t attach any

If you don’t name a repo, CodeHerder picks one for you. It works through this order:

  1. The workspace’s default repo, if one is set.
  2. The one active repo across the workspace and its parent groups, if there is exactly one.
  3. If neither resolves anything, see Limits and what goes wrong, below.

CodeHerder makes this choice when the task is created and attaches the result to the task, where it shows up in ch task repos. From then on the set is fixed: changing the workspace default later doesn’t move tasks that already exist. To change an existing task, use ch task repos create and ch task repos delete. Tasks that a schedule spawns work the same way, except CodeHerder makes the choice when the task starts.

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 merge-requests create. An agent that writes its pull request or merge request link into the task’s artifact field gets that request recorded for the repo automatically, but a multi-repo agent still records each one itself. merge-refs and set-merge-ref still work as older names:

ch task merge-requests create <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: once at least one request is recorded, it prints a merge refs: N of M merged summary followed by one line per request, even when there’s only one. The web app’s task page lists them in its Merge requests panel, with a Merged or Unmerged state on each and an Awaited badge on the ones the task waits on. 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

There is no fixed cap on how many repos a task or its sandbox can hold. A large set takes longer to check out, but CodeHerder provisions every repo you declare. Discovery has no cap either: it attaches every matching repo, with no truncation.

To work across many repos from one terminal, start a session from a parent folder. It lists every reachable repo and checks out a missing one on demand. See Start from a parent folder of repos.

A few repo-shaped problems land the task in the same blocked state, each with its own explanation attached:

  • Nothing to resolve — more than one active repo is in play across the workspace and its parent groups, and there’s no workspace default. A repo inherited from a parent group counts as a candidate, so a workspace with one repo of its own and one from its group is ambiguous. The blocker names every candidate it found and tells you to either attach a set on the task or set a workspace default.
  • An archived repo — a repo in the task’s set, or the workspace default, has been archived. CodeHerder refuses to start a session for the task and files a blocker naming the repo. Restore it with ch repo restore <name>, remove it from the task with ch task repos delete <taskId> <repo>, or change the 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 and no repos in the workspace or its parent groups — isn’t a failure case. The same goes for a task you created with --no-repo. 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.

Last updated

CodeHerder

Round up your herd.

Bring every human and every agent onto one table. Watch the work move. Costs update as it happens.

Try "pricing", "connect a device", or "who reviews the code"

↑↓ move · ↵ open · esc close