# Reviewing an agent's work

Source: https://codeherder.com/docs/reviewing-work/

Inspect an agent's output at the review, merge, and verify checkpoints — read hand-off comments, open the merge request, or send it back for rework.

When an agent completes the build stage, it opens a merge request and posts a hand-off comment on the task before advancing to `review`. This is the point where you read what the agent produced and decide what happens next: let it continue on its own, or send it back for rework.

This page is about one task at a time. To see how the review queue is doing overall — latency, how often work bounces back, and what review costs — see [Review debt](https://codeherder.com/docs/review-debt/).

## Reading an agent’s output

### Hand-off comments

An agent’s hand-off comment is its own account of the work — what it did, the decisions it made, and anything it left open. Reading it before you open the merge request gives you context that the diff alone does not.

```
ch task show <taskId> --comments
```

This prints the full task details followed by every comment, newest first. Hand-off comments are labelled with the stage they were posted in (for example, `[hand-off @ code]`) so you can tell which agent wrote what and when. See [Task comments and hand-off notes](https://codeherder.com/docs/collaborating/#task-comments-and-hand-off-notes) for how that stamp gets there and what it means for the next stage’s brief.

### The merge request

The `code` stage gate requires the build agent to record the merge request URL before the task can advance. Fetch it with:

```
ch task field <taskId> artifact
```

This prints the URL. Open it in your browser to read the diff, check CI results, and review the agent’s pull request description.

By default, the commits in it name the person who filed the task as author and the agent as committer. See [Whose name is on a commit](https://codeherder.com/docs/task-commits/#whose-name-is-on-a-commit).

Before you dive into the diff, the task’s own page also shows its **change shape** — how many files and lines it touches, and how spread out it is — a quick signal for whether this is a small, focused review or one worth asking to be split up. On a task working in more than one repository, the shape breaks down by repo too. See [Change shape](https://codeherder.com/docs/change-shape/) for what the numbers mean.

### Confirming it merged

The `artifact` field is just a link — it doesn’t tell you whether the request behind it has actually merged. `ch task merge-refs` records the task’s own pointer to a merge request so you (or CodeHerder) can check that later. `ch task set-merge-ref` and `ch task clear-merge-ref` are working aliases for its `create` and `delete` ops — either spelling reaches the same place:

```
ch task merge-refs create <taskId> <pr-or-mr-url>
```

Built-in workflows don’t need this at all: the `merge` stage lands the request itself and moves the task on once it has, so there’s nothing to track. Recording a ref matters when a workflow has a stage built to wait for someone else to merge the change instead — see [Integrations](https://codeherder.com/docs/integrations/) for what a connected GitHub or GitLab account adds there. You can record one at any stage. Outside a waiting stage, CodeHerder marks a ref merged only when something reports the merge to it — for example, an [inbound webhook](https://codeherder.com/docs/inbound-webhooks/) rule that reports a merge outcome from your git host. Without that, the ref’s state doesn’t change, so open the request itself to check whether it’s landed.

`ch task merge-refs create` is repeatable — a task working in more than one repository gets one merge request per repo, and you record each one with its own call. Recording a second URL for a repo you’ve already recorded updates that row instead of adding a duplicate; a URL for a different repo adds a new row. If you record a stray or mistyped URL, remove it with:

```
ch task merge-refs delete <taskId> <pr-or-mr-url>
```

Whatever you’ve recorded shows up in the task details.

```
ch task show <taskId>
```

always prints a summary line, followed by one line per recorded request, even when there’s only one:

```
merge refs: 1 of 2 merged
  https://github.com/acme/my-app/pull/123  (merged 2 hours ago)
  https://gitlab.com/acme/my-lib/-/merge_requests/45  (unmerged)
```

On the web app, the task page shows the same shape — a **Merge refs** row with the same “N of M merged” summary, then one row per repo with its own **Merged** / **Unmerged** badge. See [Tasks that span several repositories](https://codeherder.com/docs/multi-repo-tasks/) for the full model.

## The review checkpoint

`review` runs its own agent. The review agent reads the merge request’s diff and the task’s acceptance criteria, then takes one of two actions:

- **Approve** — it moves the task to `merge`. It doesn’t merge anything itself; the merge agent does that next.
- **Reject** — it posts a comment saying what must change, then sends the task back to `code`. That send-back counts against the round cap described below, like any other.

The review agent doesn’t approve while any acceptance criterion is unmet. You don’t have to wait for it, though: read the same diff yourself, and send the task back if you disagree — see **Sending work back for rework** below.

If your workspace has turned on stage judging (off by default — see [Stage judging](https://codeherder.com/docs/stage-judging/)), an independent agent can also grade the review after it ends. That grade never holds the task at `review`. Once [judge calibration](https://codeherder.com/docs/judge-calibration/) marks that judge “armed”, its fail sends the task back to `code` the same way a reject does.

## Approval gates don’t pause review, merge, or verify

By default, CodeHerder moves work through `review`, `merge`, and `verify` without waiting for human input — each stage’s agent finishes its check and hands the task to the next one automatically. If you’re satisfied with what you see, there’s nothing to do; the task keeps advancing on its own.

Approval gates don’t reach into that hand-off. A gate only intercepts CodeHerder’s own automatic advance into a stage, and for the built-in feature, bug, and story types that only ever happens once — moving an unassigned task out of `todo`, before any agent has started it. The `review → merge`, `merge → verify`, and `verify → done` hand-offs are each made explicitly by the agent working that stage, so a gate on `review`, `merge`, or `verify` has nothing to intercept, and `ch task approve <taskId>` never comes up at these checkpoints. See [Approvals & staying in control](https://codeherder.com/docs/approvals/) for where gates do apply.

If you want a checkpoint at one of these stages, inspect the work after the fact and send it back if something’s wrong — see **Sending work back for rework** below — rather than relying on a gate to pause before it happens.

## The merge checkpoint

`merge` is the stage between `review` and `verify` where an agent lands the merge request on the task’s base branch — the repo’s default branch unless the task names another — and by default it needs nothing from you either: the merge agent confirms the request’s checks are green, merges it, and advances the task on its own.

If it can’t land the change as-is, it doesn’t stop and wait for you — it acts:

- **Conflict or a red pipeline caused by its own change** — it resolves the conflict or fixes the failure itself, then merges.
- **The work genuinely needs to change** — it hands the task back to `code` with a comment describing what must change, the same as a reviewer sending work back.
- **Landing needs something no agent can grant** — most often a required human approval on the request — it files a blocker instead, which is where you’ll see it.

You can tell which happened from the task’s activity and comments:

```
ch task show <taskId> --comments
```

If the task came back to `code`, you’ll see a hand-off comment from the merge agent explaining what to change — inspect it the same way you would a review send-back. If it’s sitting at `blocked`, check [Blockers and blocked tasks](https://codeherder.com/docs/blockers/) for what to do next. See [How work flows](https://codeherder.com/docs/how-work-flows/) for the full mechanics behind the merge stage.

## The verify checkpoint

`verify` is the last stop before a task closes, and by default it needs nothing from you: the verify agent exercises the merged change and takes one of two actions on its own —

- **Pass** — the task advances straight to `done`.
- **Fail** — the task goes back to `code` for rework, with a comment explaining what did not work.

A pass takes two records, and the verify agent writes both. It fills the task’s `verification` field with proof: the merge commit on the base branch, plus a one-line confirmation that the change works. It also records a `pass` for the stage’s required verification on the current attempt. A feature, bug, or story can’t move from `verify` to `done` until both are in place. Not every task passes through `verify` on its way to `done`, though: completing a task early from an earlier stage, or a lightweight task type that has no `verify` stage in its pipeline at all, can still close without it. See [How work flows](https://codeherder.com/docs/how-work-flows/) for how the field and the verification gate the move to `done`.

You can still inspect what verify found:

```
ch task show <taskId> --comments
```

shows any comment the verify agent left — for example, an explanation of what failed.

```
ch task verifications <taskId>
```

lists every verification result recorded for the task, most recent first — 50 to a page, so see [Paging through long lists](https://codeherder.com/docs/using-the-cli/#paging-through-long-lists) if a task has bounced back and forth enough to need more. Each row names the stage and attempt it belongs to, its status, when it was recorded, and who recorded it — including the pass or fail the verify agent itself recorded.

If you want to catch a problem before the task closes, or you disagree with a pass, send it back the same way you would from `review` — see **Sending work back for rework** below.

## Sending work back for rework

If the build does not meet the acceptance criteria, send the task back to the build stage:

```
ch task status <taskId> code --reason "what must change"
```

Include a `--reason` explaining what is wrong and what the agent should do differently. The next build session starts with that note as part of its brief.

Two things happen automatically when you send a task back:

- **Model escalation.** CodeHerder escalates to a more capable model for the next build attempt — the first attempt runs on the standard tier; any subsequent rework runs on the more capable tier. See [How work flows](https://codeherder.com/docs/how-work-flows/) for the full escalation details.
- **Round cap.** `code` sets one cap number for how many times it can be re-entered, but each send-back edge into it — `review → code`, `merge → code`, `verify → code` — counts toward that same number independently. The task blocks the moment any single edge reaches the cap, not when the edges’ counts add up to it. CodeHerder moves the task to `blocked` and files a blocker note explaining how many times it bounced. A human then reviews the situation and either resumes the task or cancels it. See [How work flows](https://codeherder.com/docs/how-work-flows/) for the default cap value.

If `code` isn’t far enough back — the work needs rethinking from the plan, say — use `ch task reopen` instead. It reaches further, and it also works from a task that’s already `done`, `cancelled`, or `archived`. A reopen itself is never refused by the round cap. But send a still-active task (one at `review`, `merge`, or `verify`) back to `code` this way, and it still adds to the same running count the cap checks — only a reopen from a task that’s already closed stays out of it. It needs a `--reason`, and only a human can run it. See **Reopening a cancelled or completed task** in [Editing and cancelling tasks](https://codeherder.com/docs/editing-tasks/).

## The difference between sending back and rejecting

Three commands can look similar but do entirely different things:

| Command | What it does |
| --- | --- |
| `ch task status <taskId> code --reason "..."` | Moves the task from `review` or `verify` back to the build stage. A new build session starts. |
| `ch task reopen <taskId> <stage> --reason "..."` | Sends the task back further than the build stage — to `plan`, say — including from `done`, `cancelled`, or `archived` after it has already closed. Never refused by the round cap itself — see **Sending work back for rework** above for when it still counts toward one. |
| `ch task reject <taskId>` | Rejects a *pending approval-gate advance* only. The task stays exactly where it is — nothing starts or restarts. |

Use `ch task status <taskId> code` when you have reviewed the work and decided it needs changes. Use `ch task reopen` when you need to reach further back than that, or the task has already finished. `ch task reject <taskId>` is unrelated to reviewing a build — it only comes into play if a workflow’s entry stage is gated and a pending-advance request is waiting, before any agent has started the task. See [Approvals & staying in control](https://codeherder.com/docs/approvals/) for more on the pending-advance state and how `ch task reject` works.

## Related guides

- [How work flows](https://codeherder.com/docs/how-work-flows/) — the stages every task moves through
- [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
- [Why isn’t my task moving?](https://codeherder.com/docs/task-not-moving/) — diagnose a stuck task
- [Review debt](https://codeherder.com/docs/review-debt/) — how the review queue is doing across the whole workspace
- [Change shape](https://codeherder.com/docs/change-shape/) — how big and spread out a task’s change is, and the optional per-stage limit
- [Tasks that span several repositories](https://codeherder.com/docs/multi-repo-tasks/) — one task, several repos, several merge requests
- [Stage judging](https://codeherder.com/docs/stage-judging/) — turn on an independent agent’s grade of a stage’s work, and when a verdict can send it back
- [Integrations](https://codeherder.com/docs/integrations/) — connect GitHub or GitLab so a workflow stage built to wait for an external merge can check it
- [Inbound webhooks](https://codeherder.com/docs/inbound-webhooks/) — let your git host report a merge outcome back to the task
