# How work flows

Source: https://codeherder.com/docs/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](https://codeherder.com/docs/base-branch/)) — then advances the task forward |
| `auto` | Read-only stage for an [auto task](https://codeherder.com/docs/auto-tasks/): 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](https://codeherder.com/docs/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 `merge` or the workflow’s completion stage — the **checklist gate**, a second, separate check from the stage gate: the stage gate only checks that the `acceptance` field is present, the checklist gate additionally requires every item in it to be ticked. See [Write acceptance criteria](https://codeherder.com/docs/writing-tasks/#write-acceptance-criteria) for how ticking works.
- **Every required verification for the current stage has a `pass` recorded**, for this attempt at the stage. See **Stage verifications** below.
- **Every task this task depends on has finished.** See [Collaborating](https://codeherder.com/docs/collaborating/) for how dependencies work.
- **Every child task has finished**, before a container can close. See [Understanding the task hierarchy](https://codeherder.com/docs/hierarchy/) for how containers and their children relate.
- **The change stays within any size or spread limit the stage has set.** See [Change shape](https://codeherder.com/docs/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?](https://codeherder.com/docs/task-not-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](https://codeherder.com/docs/task-types/).

## 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 `code` with 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](https://codeherder.com/docs/blockers/) 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](https://codeherder.com/docs/task-types/).

## 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](https://codeherder.com/docs/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](https://codeherder.com/docs/stage-judging/#when-a-verdict-can-send-work-back) |
| `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?](https://codeherder.com/docs/task-not-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 `verify` goal gate.** A required `pass` is refused from an author. Someone who did not build the change must record it. The exception: a member who worked the `verify` stage itself may record the `pass`, 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](https://codeherder.com/docs/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?](https://codeherder.com/docs/task-not-moving/).

The same idea applies to the **Assignee** pin. See [Assigning work](https://codeherder.com/docs/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](https://codeherder.com/docs/stage-library/#changing-a-stages-verifications-for-one-workflow). See [Customising workflows](https://codeherder.com/docs/task-types/) or [Customising a task’s workflow](https://codeherder.com/docs/workflow-overrides/) 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 `code` runs on the standard model tier (`model:sonnet`).
- After the first rejection back into `code` — from `review`, `verify`, or `merge`, added together — the rework and all subsequent build attempts run on the more capable tier (`model:opus`). The `merge` stage 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](https://codeherder.com/docs/costs/#model-escalation-and-rework-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](https://codeherder.com/docs/editing-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](https://codeherder.com/docs/editing-tasks/) for the walkthrough and how the usual close checks still apply.
- Any active stage can move to `cancelled` (with a reason, below) or to `blocked` — see [Blockers and blocked tasks](https://codeherder.com/docs/blockers/) for the blocked lifecycle and how a task returns from it.
- A `done`, `cancelled`, or `archived` task can be reopened to an earlier stage in its pipeline with `ch task reopen`; a `done` task can also be archived instead with `ch 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](https://codeherder.com/docs/editing-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](https://codeherder.com/docs/approvals/) 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](https://codeherder.com/docs/reviewing-work/).

## Related guides

- [Writing tasks an agent can build](https://codeherder.com/docs/writing-tasks/) — how to file work agents can pick up
- [Assigning and claiming work](https://codeherder.com/docs/assigning-work/) — who ends up working a task
- [Approvals & staying in control](https://codeherder.com/docs/approvals/) — gate advances behind a designated approver
- [Reviewing an agent’s work](https://codeherder.com/docs/reviewing-work/) — the human side of the review stage
- [Why isn’t my task moving?](https://codeherder.com/docs/task-not-moving/) — diagnose a stuck task
