How work flows
Task types, workflow stages, stage gates, the merge agent stage, and how to move a task yourself from the web app or the CLI.
Every task carries a type, and the type names a workflow: an ordered list of stages — the pipeline — the task must move through from creation to completion. Understanding pipelines is the key to understanding how CodeHerder keeps work predictable.
Stages
A stage is a named checkpoint in a task’s lifecycle. The stages in the built-in workflows are:
| Stage | Meaning |
|---|---|
todo |
Claimable — waiting to be picked up or auto-staffed |
planning |
Container planning: proposal, design, and spec authoring plus child-task decomposition (initiative and epic only) |
in_progress |
Container holding state: child tasks are being worked; no agent runs on the container itself (initiative and epic) |
plan |
Read-only stage: the agent reads the spec, explores the codebase, and confirms the approach (feature, story). On a bug, this stage runs as triage |
code |
Build stage: the agent implements the work and opens a merge request |
review |
Code review: a reviewer approves or sends back to code with a comment explaining what must change |
merge |
Autonomous merge stage: the agent lands the merge request on the task’s base branch — the repo’s default branch unless the task names another (see Choosing a task’s base branch) — then advances the task forward |
auto |
Read-only stage for an auto task: the agent picks the rest of the workflow from the shared stage library |
verify |
Confirmation: the agent exercises the change in the running app; passes to done or returns to code for rework |
done |
Terminal — work complete |
blocked |
Cross-cutting: paused waiting on an external dependency (any type, any active stage) |
cancelled |
Cross-cutting terminal — abandoned; requires an explanatory comment |
Not every type uses every stage. See Core concepts for each type’s pipeline. The lightweight task type runs todo → code → merge → done, and an auto task runs todo → auto → done. Every workspace has these built-in workflows without any setup. If a workspace defines its own version of a type, that version replaces the built-in one. blocked and cancelled are cross-cutting statuses that can be applied at any point; they are not linear stages in any pipeline.
Read-only stages and stage gates
Read-only stages (planning, plan including bug triage, review, verify, and auto) are stages where the agent cannot commit. Agents read the codebase and research freely but do not commit code or open merge requests. Only code and merge can commit. In the planning-type stages, the agent’s job is to fill in structured fields on the task.
Before a task can advance out of any stage, it must pass the stage gate: a check that all required fields for that stage have been filled. The gate is enforced server-side — attempting to advance a task with missing fields returns an error listing what is still needed. It’s one of several checks a task must clear before it moves on — see Before a task moves on below for the full set.
The ch task validate <taskId> command dry-runs a related but distinct check: the rigor gate. It previews whether the task’s required fields (proposal, design, spec, acceptance criteria, or reproduction steps, depending on type) are filled and long enough, that the acceptance criteria hold at least one - [ ] checklist item, and that a container has enough child tasks. It lists what’s missing without transitioning anything. The output reads <id> (<type>): ready, or NOT READY — N violation(s) followed by one line per field. The command exits with code 2 when the task is not ready. It does not preview the plan, artifact, or verification fields — those are filled by an agent as it works (plan while the task sits at plan, artifact when opening the merge request at code, verification when confirming the change at verify), and the stage gate enforces them at transition time rather than as part of this upfront readiness check.
| Stage | Gate — required before advancing |
|---|---|
Container planning (initiative, epic) |
proposal, design, spec, and at least one child task |
Feature/story plan (built-in workflow) |
acceptance (acceptance criteria) and plan (implementation plan) |
Bug plan (triage) |
repro (reproduction steps) |
code (every type with a code stage) |
artifact (the merge request or commit link). A task with no repository does not need it |
verify (feature, bug, story) |
verification (proof the merged change works on the default branch) |
On the built-in story and feature workflow, the planning agent also writes an implementation plan into the plan field before the task can leave plan — the task description stays the specification you filed, and plan is where the agent records how it intends to build it. A workspace that has customised its story or feature workflow may not carry this field; run ch task fields <taskId> on any single task to see what it actually requires.
The artifact field is the proof-of-work that the subsequent review, merge, and verify stages act on. It is set by the code agent when the merge request is opened, and the stage gate prevents the task from leaving code until it is present.
Before a task moves on
The stage gate above is one of several checks CodeHerder runs before it lets a task move to its next stage. Every check below runs on the forward move, and a task with even one unmet moves nowhere — there’s no partial advance.
- Required fields for the current stage are filled — the stage gate, above.
- Every acceptance-criteria checklist item is ticked, before the task can advance to
mergeor the workflow’s completion stage — the checklist gate, a second, separate check from the stage gate: the stage gate only checks that theacceptancefield is present, the checklist gate additionally requires every item in it to be ticked. See Write acceptance criteria for how ticking works. - Every required verification for the current stage has a
passrecorded, for this attempt at the stage. See Stage verifications below. - Every task this task depends on has finished. See Collaborating for how dependencies work.
- Every child task has finished, before a container can close. See Understanding the task hierarchy for how containers and their children relate.
- The change stays within any size or spread limit the stage has set. See Change shape for what’s measured and how a limit is set.
CodeHerder tells you the verdict before you attempt the move, so you don’t have to guess and get refused. Both surfaces show the same answer, because both read the same check:
On the CLI, ch task show <taskId> prints a row naming the next stage and whether it’s reachable:
advance to review: blocked — artifact: required field is empty
or, once nothing is outstanding:
advance to merge: not blocked
A third row, advance to <stage>: pending — <message>, appears when the next stage has an entry condition that is not met yet. For example, the task may still wait for a merge to be recorded. A blocked row reports the first check that blocks the move. A field-based check names every field it found, one reason each; any other check — a missing verification or an unfinished dependency, say — reports as a single sentence. Either way, the row isn’t a full list of everything outstanding: clear what it names, and a later check can surface in its place.
In the web app, the task page shows the same verdict below the stage breadcrumb. When every check passes, it shows an Advance to <stage> button. Otherwise it shows a line naming each field that blocks the move and why, such as an empty field or “3 of 5 checklist items outstanding”. For any other check, it shows the sentence the server gives. When the next stage has an entry condition that is not met, the line reads “Not yet ready to advance to <stage>: …”.
Neither surface shows a row or button at all when the task has no next stage to move to — a terminal task, or one whose pipeline ends here. Read that absence as “nothing to advance to”, never as “nothing is stopping it”.
These checks are the requirements a task must meet. They aren’t the only reason a task sits still — an approval, a device, or a stalled assignee can hold ready work too. See Why isn’t my task moving? for the full diagnostic.
To change the stages, gates, or fields on a workflow — or to create an entirely new type — see Customising workflows.
The merge stage
merge is an autonomous agent stage: when a task reaches merge, the engine spawns an agent that lands the open merge request on the task’s base branch — the repo’s default branch unless the task names another — confirms the commit is present, and then advances the task forward — to verify for feature, bug, and story tasks, or straight to done for the lightweight task type. This stage requires no human action by default. If the merge can’t land as-is, the agent doesn’t stop and wait for someone to sort it out — it acts:
- A conflict: resolve it by rebasing the change on the default branch, fixing the conflict, and merging.
- A red CI pipeline caused by its own change: fix it and merge.
- Work that genuinely needs to change, or a conflict it can’t safely resolve itself: hand the task back to
codewith a comment explaining what must change — the same send-back edge covered below. - Landing needs something no agent can grant, most often a required human approval on the request: file a blocker, which takes the task out of the staffing queue for a person to resolve. See Blockers and blocked tasks for what that looks like.
The verify stage
verify is a read-only agent stage. The agent exercises the merged change in the running application to confirm it behaves as expected. Then it takes one of two actions:
- Pass: advance the task to
done— the change works correctly. - Fail: advance back to
code— post a comment describing what failed and what must be fixed, then return the task for rework.
Passing writes proof into the verification field (like artifact at code, filled automatically by the agent, never by a human), and the verify stage gate holds the task there until that field is set — a feature, bug, or story cannot reach done without it. This is a required field, distinct from the verification results described in Stage verifications below.
A workflow sets one round cap, on its code stage. CodeHerder checks that cap against each send-back route into code — review → code, verify → code, and merge → code — and counts each route separately. A task blocks the moment any single route reaches the cap, not when the counts add up to it. You can’t give two routes different caps. The built-in feature, bug, story, and task types all default to a cap of 3 rounds. Leaving the cap at 0 doesn’t remove the limit — it falls back to CodeHerder’s own safety cap of 5. When a task hits the cap, CodeHerder automatically moves it to blocked and files a blocker note. The note names the stage, gives the count against the cap, and tells a human to read the latest hand-off comment and then fix the blocker or cancel the task. To change the cap for a workflow, see Customising workflows.
Stage verifications
The stage gate described above checks that required fields are filled before a task can leave a stage. Verifications are a separate, complementary mechanism. Most of them are not automated checks that CodeHerder runs for you — a verification is a result the stage’s own agent records once it has done the work, and CodeHerder’s only job is to hold the gate shut until a pass shows up. The one exception is a stage judging verification: CodeHerder runs that one itself, in the background, and it never gates — see Stage judging for what it checks and how to turn it on.
A stage can declare one or more verifications, each with an id and marked required or not. Someone working the stage records a result with:
ch task verify <taskId> <verificationId> <pass|fail|error|skipped> [--reason "..."]
For a test, lint, or typecheck check, you can let ch run the check and record the real outcome instead:
ch task verify <taskId> <verificationId> --execute
--execute runs the command that the task’s current stage declares for that check, in your current directory. It records pass when the command exits with 0, fail when it exits with a non-zero code, and error when it can’t start. You give it no status. If the stage declares no command for the check, or the check isn’t declared on the task’s current stage, ch stops with an error and records nothing. Tip: commit your changes first, so the result reflects what you push.
Any workspace member can record a result. An agent session can record a result only on its own task. You can record one yourself if a session dies partway through its checks. Some checks keep the checker apart from the author. See Who can record a pass, below.
The built-in feature, bug, and story workflows declare these verifications:
| Stage | Verification | Required? | On fail |
|---|---|---|---|
plan (feature and story only), code, review |
A judged check of the stage’s work | No. It runs in the background and never gates | Nothing moves, unless the judge is armed. See Stage judging |
code (not the task type) |
test, lint, and typecheck |
No. None declares a command out of the box | Stays at code |
verify |
The goal gate: confirmation the merged change works as intended | Yes | Sends the task back to code |
The bug type’s triage stage declares no verifications. The task type’s code stage has the judged check only. So out of the box, the only check that holds a task is the verify goal gate. A workspace can turn a check on or off, or make it required, for one workflow. A judged check never holds the forward move, even when a workspace marks it required. A recorded fail on it then sends the task back. See the last paragraph of this section.
What the gate enforces: before a task can move forward out of a stage, every required verification on that stage needs a pass recorded for the current attempt — how many times the task has entered that stage. A pass from an earlier attempt doesn’t carry forward: send a task back to code and it re-enters review on a fresh attempt, needing a fresh pass. A missing result, or one recorded as fail, error, or skipped, all leave the gate shut; only pass opens it.
The gate only governs the forward advance. It doesn’t apply when a task moves to blocked or cancelled, when it’s archived, when a reviewer or the verify stage sends the task backward for rework, or when a terminal task is reopened.
Recording fail on a required verification moves the task itself. CodeHerder routes it back to the stage that check sends failures to — code on the built-in workflows. Nothing moves if the task is already there. This is the same reject loop as a human send-back, counting against the same round cap and triggering the same model escalation (see below). Recording error doesn’t move anything; it just leaves the forward gate shut until something records a pass.
Seeing results: run ch task verifications <taskId> to list every result recorded for a task — stage, verification, status, and which attempt it ran on — most recent first. Each recorded result also shows up in the task’s activity feed. For a walkthrough of reading this output when a task keeps bouncing back to code, see Verification keeps failing in Why isn’t my task moving?.
Who can record a pass
CodeHerder keeps the checker apart from the author for two kinds of check. An author is anyone who ran a session on a stage that can commit code, or who is pinned to one. On the built-in workflows those stages are code and merge.
- The
verifygoal gate. A requiredpassis refused from an author. Someone who did not build the change must record it. The exception: a member who worked theverifystage itself may record thepass, even if they also did rework earlier. - A judged check. CodeHerder refuses a result from anyone who worked that stage, on any attempt, and from anyone who already recorded a result on this attempt. This holds for every status, and even when the check isn’t required. See Stage judging.
A test, lint, or typecheck check has no such rule. Any member can record any status for it.
For the goal gate, CodeHerder never refuses a fail, error, or skipped. If CodeHerder refuses a result, hand the task to someone who did not build the change. The refusal message may suggest --execute. That helps only for a check whose stage declares a command, and the built-in goal gate declares none. See A result is refused in Why isn’t my task moving?.
The same idea applies to the Assignee pin. See Assigning work.
There’s no view for verification results in the web app. The Settings → Workflows editor shows each verification’s enabled and required state, read-only. It lets you pick which stage a failed verification routes to. It doesn’t let you add a new verification or change what it grades — that content comes from the workflow’s schema (a hand-written type) or the stage library (a composed type). A composed type can still turn an existing verification on or off, or change whether it’s required, for that one workflow alone — but that’s a CLI-only move today, a small overlay on the instance rather than an editor control; see Changing a stage’s verifications for one workflow. See Customising workflows or Customising a task’s workflow for the rest of what each editing path controls.
Automatic model escalation on rework
When a task is sent back into code — by a reviewer, by verify returning it for rework, or because merge couldn’t land the change and handed it back — CodeHerder automatically escalates to a more capable model for the next build attempt. This gives rework the extra capability it often needs without paying the higher rate on the first attempt.
How escalation works for feature, bug, story, and task:
- Built-in read-only stages (
planning,plan,review,verify) run on the more capable tier from the start. Escalation affects the build stage only. - The first build attempt at
coderuns on the standard model tier (model:sonnet). - After the first rejection back into
code— fromreview,verify, ormerge, added together — the rework and all subsequent build attempts run on the more capable tier (model:opus). Themergestage never escalates.
This is the shipped default for every type with a send-back edge into code. It happens automatically whenever a task is sent back for rework; there is nothing to configure.
The lightweight task type has one send-back edge, merge → code — it has no review or verify stage of its own, but a merge that can’t land still sends it back, so it can be reworked and escalates the same as any other type.
The reject-loop cap described above counts differently from escalation: escalation sums every send-back into code together, while the cap is checked per route — a task blocks when any single route reaches the cap, regardless of how the others are doing.
What this means for costs: A task that required rework will typically show spend across two model tiers. ch task show <taskId> doesn’t split spend by model. See Understanding costs to see the per-model breakdown.
Moving a task forward
Most of the time you don’t move a task yourself — the engine advances it automatically as each stage’s gate clears, and an agent or reviewer sends it forward or back. This section is for the times you do it by hand: resuming something stuck, cancelling it, or jumping to a stage that isn’t the pipeline’s immediate next step.
CodeHerder works out which stages a given task can move to right now, using the same rule the server checks when the request actually comes in, and shows you exactly that list on the task itself. You never have to read the workflow schema to guess — the options you’re offered are the options that will work.
From the web app
The status dropdown shows the task’s current status plus one → Stage option for every stage it can reach right now, and nothing else: → Archived, → Code, or → In progress, depending on the task. It appears on the task’s own page, on each row of the Tasks list, and on each card on the board view, and all three stay in sync. A task with no reachable stage — a cancelled one, for instance — shows a disabled dropdown instead. Hover over it to see why. See Reopening a cancelled or completed task in Editing and cancelling tasks for the separate way back into the pipeline from a finished or cancelled task.
Picking a target that needs a reason — cancelling, by default — opens a Reason required dialog that reads “Moving <title> to <status> requires a reason.” Click Change status with an empty box and the dialog shows “Reason is required.” under the box. Type a reason to continue.
If the task’s type declares a pipeline, a breadcrumb above the task body shows every stage left to right with the current one highlighted. Below it, the page shows the advance verdict described in Before a task moves on. That button only ever offers the pipeline’s immediate next stage — for anything else, use the dropdown.
From the CLI
Check what’s reachable before you act:
ch task show <taskId>
The output includes a next stages row listing every stage the task can move to right now, comma-separated. Then name the one you want:
ch task status <taskId> <stage>
The server enforces valid transitions and stage gates against that same list, so naming a stage that isn’t reachable is rejected with a message naming the ones that were — for example, a task at review can’t jump straight to verify (it has to pass through merge first), and the rejection reads transition from review to verify is not allowed (reachable stages: code, merge, done, blocked, cancelled). A wrong guess tells you exactly what to try next. For a task at blocked, that list also includes the stage it was blocked at.
To see the full pipeline a task’s type declares — rather than just what this one task can do right now — use ch workspace workflow show --type <type> for one type. ch workflow show <taskId> prints one task’s effective workflow, including any override on that task.
What’s always available, beyond the pipeline
A handful of moves work the same regardless of where a task’s pipeline says it should go next:
- Any active stage can move straight to
done, for work that’s already finished or no longer needs the rest of its workflow — see Completing a task early in Editing and cancelling tasks for the walkthrough and how the usual close checks still apply. - Any active stage can move to
cancelled(with a reason, below) or toblocked— see Blockers and blocked tasks for the blocked lifecycle and how a task returns from it. - A
done,cancelled, orarchivedtask can be reopened to an earlier stage in its pipeline withch task reopen; adonetask can also be archived instead withch task status <taskId> archived. Reopening needs a reason and a human caller:ch task reopen <taskId> <stage> --reason "...". See Reopening a cancelled or completed task and Archiving a finished task in Editing and cancelling tasks.
Cancelling a task
Moving any task to cancelled requires an explanatory comment — no silent cancels. Supply the reason with --reason:
ch task status <taskId> cancelled --reason "Superseded by task <newTaskId>"
Attempting to cancel without --reason is rejected server-side. The reason is posted to the task as a comment automatically.
Approval gates
Some stage transitions require a designated approver before the task moves forward. See Approvals & staying in control for the full details: the pending-advance state, how to approve or reject, and how to find what’s waiting on you.
Containers: epics and initiatives
Epics and initiatives follow the container pipeline (todo → planning → in_progress → done). Their planning stage is where the planning agent fills the proposal, design, and spec fields and creates the child tasks. The container cannot leave planning until all three fields are filled and at least one child task exists.
Once all required fields are present, the container advances to in_progress — a non-executable holding state meaning “child tasks are being worked”. No agent is spawned for this stage. The container closes once every child is finished: done, cancelled, or archived. CodeHerder refuses every earlier close attempt.
For a step-by-step walkthrough of the human side of the review process — reading hand-off comments, opening the merge request, approving work, or sending it back for rework — see Reviewing an agent’s work.
Related guides
- Writing tasks an agent can build — how to file work agents can pick up
- Assigning and claiming work — who ends up working a task
- Approvals & staying in control — gate advances behind a designated approver
- Reviewing an agent’s work — the human side of the review stage
- Why isn’t my task moving? — diagnose a stuck task
Last updated