Change shape
Read the size and spread of a task's change at a glance, and optionally require it to stay under a limit before a stage advance.
A big, spread-out change is harder to review than a small, focused one — not because the code is worse, but because there’s more of it to hold in your head at once. CodeHerder measures this for you automatically and calls it a task’s change shape: how many files it touches, how many lines it adds and removes, how many separate top-level areas of the repo it reaches, and how many commits it’s made. You can also ask a workflow stage to enforce a limit, so a task that grows too large or too spread out is stopped before it advances any further.
Where to see it
Web app: open the task’s page. Right under the stage breadcrumb you’ll find a badge and a line summarizing the shape, for example:
tangled 34 files · +820/-140 · 5 dirs · 3 commits
If the numbers are behind the task’s latest activity, a (stale) marker appears next to them — see Staleness below.
CLI:
ch task show <taskId>
prints a change shape: row in the same format, for example:
change shape: 34 files · +820/-140 · 5 dirs · 3 commits · tangled — 34 files across 5 top-level directories
Before any session has reported a shape, it reads:
change shape: unknown (no session has reported one yet)
ch task list doesn’t include change shape — check an individual task’s page or ch task show for it.
A task spanning more than one repository
Once a task’s change touches more than one repo — see Tasks that span several repositories — the rollup above is followed by one indented line per repo, in both ch task show and on the task page. A single-repo task’s output doesn’t change at all.
What the numbers mean
Five counters make up a task’s change shape, all measured against the branch the task’s work started from — more precisely, against the merge-base of that branch with the task’s own commits, so the counters describe only the task’s own work even if the base branch has moved on in the meantime.
- Files changed — how many files differ from the starting branch.
- Insertions / deletions — lines added and lines removed, shown as
+N/-N. - Distinct top-level directories — how many separate top-level areas of one repo’s codebase the change touches. A change confined to one part of a repo counts as 1; a change that reaches into five unrelated top-level folders of that repo counts as 5.
- Commits — how many commits the task’s branch has made since it was cut.
On a task spanning more than one repository, files, insertions, deletions, and commits are totalled across every repo — but distinct top-level directories is not: it’s the highest count from any single repo in the set, not a sum. Five repos that each touch one top-level folder still roll up to 1 dir, not 5.
The numbers cover the task’s worktree as it stands — changes that are tracked but not yet committed count too, not just what’s landed in a commit.
A session reports its task’s shape at three points: when it starts, again mid-session whenever the set of touched files changes, and once more when it finishes. It’s best-effort — if a shape can’t be measured, nothing is recorded, and the task shows unknown rather than a shape that reads as “nothing changed.” That same report also tells CodeHerder when two sessions touch the same files; see Working nearby.
Staleness
The (stale) marker means the recorded numbers were measured before the task’s most recent work session ended, so more has probably changed since. It isn’t a problem on its own — the next session’s report catches it up, or you can reload once a session finishes.
What the verdict means
Every recorded change shape gets one of four verdicts:
| Verdict | Meaning |
|---|---|
ok |
Every counter is within its limit. |
large |
Too many files or too many changed lines, without also spreading across too many top-level directories. |
tangled |
The change reaches across too many top-level directories. This wins over large when both trip, since a spread-out change is usually the one worth splitting up. |
unknown |
No session has reported a shape for this task yet. |
When a workflow stage hasn’t set its own limits (see Setting a limit on a stage below), CodeHerder still shows you a verdict using built-in advisory numbers — more than 25 files changed, more than 1,000 changed lines (insertions and deletions combined), or more than 4 distinct top-level directories. This advisory verdict is informational only: it flags a change worth a second look, but it never stops a task from advancing. Turning that into an actual limit means setting one on the stage yourself, which is what the next section covers.
Setting a limit on a stage
A workflow stage can enforce its own change-shape limits instead of relying on the advisory defaults above. This is off everywhere by default — no built-in workflow ships with one — and it’s opt-in per stage, so you only turn it on where it’s useful, typically the stage where an agent is writing code.
You set it the same way you’d set any other schema detail that isn’t in the web editor: read the type’s schema, edit it, and write it back.
ch workspace workflow show --json
Find the type you want to change, then add a change_shape object next to that stage’s other gate settings:
{
"stage_gates": {
"code": {
"change_shape": {
"max_files_changed": 20,
"max_changed_lines": 800,
"max_top_level_dirs": 3
}
}
}
}
Then write it back:
ch workspace workflow edit <type> --from-file <file>
Setting it requires a workspace owner or admin, same as any other workflow edit. edit replaces the type’s whole schema, so keep every other stage, gate, and field in the file — not just the one you’re touching. See Manage workflows from the CLI in Customising workflows for the full round-trip.
You don’t have to set all three counters — include only the ones you care about, and each unset or zero-valued one is simply not checked. At least one has to be non-zero, though; a change_shape clause where every counter is 0 is rejected when you try to save it — leave the whole clause out instead if you don’t want a limit on that stage.
What happens when a task trips the limit
A task whose recorded shape exceeds a limit is refused when it tries to leave that stage. The refusal names the one counter that tripped, its actual value, and the limit it exceeded, for example:
advancing out of stage "code" is blocked by its change-shape gate: max_files_changed is 34, exceeding the threshold of 20
If you hit this, either split the change into smaller tasks so each one fits inside the limit, or raise (or remove) the limit on that stage if it’s too tight for the work at hand.
What a change-shape limit never blocks
A change-shape limit only ever intercepts a forward advance out of the stage that declared it, and only once a shape is actually known. It never blocks:
- a task whose change shape hasn’t been recorded yet (
unknown), - cancelling, blocking, or archiving a task,
- reopening a task out of a terminal stage,
- a counter that’s left at 0 or simply not included in the clause.
The web workflow editor doesn’t know about this setting — and will remove it
The Workflows editor in the web app has no field for change_shape, and it isn’t simply unaware of it: saving a type from the web app rebuilds every stage’s gate from what the editor’s form knows about (required fields and approval only), so opening a type in the web app and clicking Save — even without touching the stage in question — silently removes any change_shape clause you set from the CLI. If you’re using a change-shape limit on a type, make all further edits to that type through the CLI, or be ready to reapply the limit if someone saves the type from the web app.
Related guides
- Reviewing an agent’s work — read an agent’s output at the review checkpoint
- Review debt — how the review queue itself is doing
- Customising workflows — the full stage-gate and schema round-trip
- Why isn’t my task moving? — diagnose a stuck task, including one refused by a change-shape limit
- Working nearby — how CodeHerder uses this same changed-file report to spot two sessions editing the same files
- Tasks that span several repositories — how the per-repo breakdown works
