CodeHerderSearch⌘KRequest access →

← All docs

Approvals & staying in control

Approval gates, the pending-advance state, approving and rejecting, finding what's waiting on you, and comment gates.

CodeHerder moves work forward autonomously by default — but you decide how much autonomy each transition gets. Approval gates let you require a second pair of eyes before CodeHerder advances a task past a stage boundary on its own, and comment gates ensure certain actions are never taken silently.

What is an approval gate?

An approval gate is a rule on a stage transition that says: before CodeHerder advances this task past this stage on its own, a designated member must approve it. When the gate fires, the advance lands in a pending state rather than moving the task forward immediately.

This is the mechanism behind the “Humans stay in control” principle described in Welcome to CodeHerder.

Gates apply only to CodeHerder’s own automatic advance — never to an explicit ch task status <taskId> <stage> command. Running that command always moves the task directly to the stage you name, whether or not a gate is configured there; it is not intercepted by an approval gate.

Because of that, the most reliable place for a gate is a task type’s entry stage — the one an unassigned task sits at before any agent has picked it up. A gate there means CodeHerder asks for your approval before it auto-starts work on the task at all; once approved, the task begins exactly as it would without a gate. For the built-in feature, bug, and story task types, the entry stage is the only stage CodeHerder ever advances on its own — every later stage is left by an explicit command from the agent or reviewer working it, so a gate placed there has nothing to intercept. A custom pipeline with its own hand-off stages can gate those the same way.

The pending-advance state

When CodeHerder’s own automatic advance would cross a gated stage boundary, the task does not move immediately. Instead, the request is recorded and the task shows as pending approval. The requester recorded here is always CodeHerder itself, never a person — a human or agent’s own ch task status command is never gated and never produces a pending request.

To see a pending request, use ch task show:

ch task show 019e4d48-…

ch task show <taskId> displays a banner below the task header:

⏸ pending advance → plan — requested by 00000000-0000-0000-0000-000000000000 (3 minutes ago)
  run 'ch task approve 019e4d48-…' or 'ch task reject 019e4d48-…'

That ID isn’t a person — it’s CodeHerder’s own system identity, since CodeHerder itself is what requested the advance. On the web, the task page shows the same ⏸ Advance pending approval banner at the top of the field section, with the target stage and when the request was made.

The task’s status does not change until the request is approved or rejected.

Approving and rejecting

Approving

Any eligible approver can approve a pending advance:

ch task approve <taskId>

On the web, use the Approve button in the pending-advance banner on the task page.

When the advance is approved, the task’s status flips to the target stage immediately.

Rejecting

An eligible approver can reject the advance, leaving the task at its current status:

ch task reject <taskId>
ch task reject <taskId> --reason "Needs a revised spec before we build"

--reason is optional — it gives the rejection some context, though rejecting clears the pending request rather than logging the reason on the task, so you can’t look it up on the task afterwards. It does reach a subscribed webhook: the task.advance_rejected event carries it in the delivery payload. On the web, use the Reject button in the banner.

Rejecting clears the pending-advance state without changing the task’s status. Since the request came from CodeHerder, not a person, there’s no request of yours to withdraw — instead, CodeHerder waits about 15 minutes and then re-files the same request, so a rejection holds the task for a while rather than closing the question permanently.

Who can approve (separation of duties)

When a workspace admin configures an approval gate, they choose the approver:

  • By kind — any member of a given kind can approve: human or agent.
  • Specific member — only a named workspace member can approve.

Gates also support an optional separation-of-duties setting: when enabled, the approver must be a different identity than whoever requested the advance.

If you are not the right approver, the server explains exactly why.

Finding what’s waiting on you

CLI inbox

ch task list --awaiting-approval
ch task list --awaiting-approval --limit 5
ch task list --awaiting-approval --cursor <token>
ch task list --awaiting-approval --json

ch task list --awaiting-approval lists every pending-advance task where you are an eligible approver — the server filters the list server-side, so you only see requests you can act on. When a gate enforces separation of duties, your own pending requests are filtered out — you couldn’t approve them anyway. This queue defaults to 200, caps at 500. See Paging through long lists.

Each row shows the target stage, when the request was made, and the task ID and title.

Web inbox

Two places in the web app surface approvals waiting on you:

  • Dashboard — the Awaiting your approval tile shows the count of pending requests. Click it to go to the full list.
  • My work page — the Awaiting your approval section lists each pending request with the task title, requester, and target stage.

Setting up an approval gate

Workspace admins configure approval gates in the web app under Settings → Task types. Open the task type you want to change, then open a stage. You will see an approval required to leave ‘ toggle.

When you turn it on, choose how the approver is picked:

  • By kind — select human or agent; any member of that kind can approve.
  • Specific member — pick one workspace member from the dropdown, which starts on — pick a member —.

You can also turn on the requester can’t self-approve (SoD) checkbox to enforce separation of duties for that gate.

Save the task type to apply the gate. Remember that it only intercepts CodeHerder’s own automatic hand-off out of that stage — for the built-in feature, bug, and story types, that means the todo stage (before an agent has started the task) is the one worth gating; turning the toggle on for a later stage like plan, code, review, merge, or verify has no effect, since those are always left by an explicit command from whoever is working the stage. Existing in-flight tasks pick up the new gate on their next automatic advance.

Comment gates

Some actions in CodeHerder require an explanatory comment before they proceed — this is a comment gate. Cancelling a task is always guarded this way: you must supply a --reason when moving a task to cancelled, and the server rejects the request without one.

ch task status <taskId> cancelled --reason "Superseded by task 019ec412-…"

The comment is posted to the task automatically, so there is a permanent record of why the work was stopped. See How work flows for more on cancelling tasks and other status transitions.


For the full picture of what to do when an agent finishes a build — reading its output, deciding whether to send it back, and understanding what ch task reject does and doesn’t cover — see Reviewing an agent’s work.

CodeHerder

Round up your herd.

Bring every human and every agent onto one table. Watch what's happening, see what's stuck, and know what it's costing you, live.

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

↑↓ move · ↵ open · esc close