Approvals & staying in control
Approval gates, the pending-advance state, approving and rejecting, separation of duties, 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 a task moves forward past a stage boundary, 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 this task moves forward past this stage, 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.
A gate holds every forward move out of its stage. That includes CodeHerder’s own automatic advance and an explicit ch task status <taskId> <stage> command. When the move is held, the task stays where it is and a pending request is filed. The person or agent who ran the command is the requester. A different eligible member must approve it.
Some moves never meet a gate: backward moves, operational exits, and exits from a terminal stage.
You can gate any stage that tasks leave in the forward direction. A gate on the entry stage asks for approval before work starts. A gate on a later stage, such as review or merge, holds the hand-off out of that stage until someone approves.
An initiative or epic also works this way at its entry stage, but its ending is worth knowing about. Once its children all finish, it closes itself to done, and that close doesn’t go through the gated-advance path at all. It’s a plain status update, not an automatic advance CodeHerder asks you about first. So a gate on the stage a container waits in, or on its close to done, never fires and never holds the parent open while you look. See Understanding the task hierarchy for how initiatives and epics relate to the work they contain.
The pending-advance state
When a forward move 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 is whoever asked for the move: a person or agent who ran ch task status, or CodeHerder itself for an automatic advance.
To see a pending request, use ch task show:
ch task show 019e4d48-…
ch task show <taskId> displays a banner below the task header, followed by a hint on what you can do about it:
⏸ pending advance → plan — requested (3 minutes ago)
run 'ch task approve 019e4d48-…' or 'ch task reject 019e4d48-…'
The hint line changes depending on whether you’re eligible to act:
- If you can act on the request, you get the hint above: run
ch task approveorch task reject. - If you can’t, you’re told so, in plain English — for example, that the gate names one specific approver and you aren’t that person, or that your member kind doesn’t match what the gate requires.
Either way, you know where you stand before you run anything.
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
An eligible approver can approve a pending advance:
ch task approve <taskId>
On the web, the pending-advance banner shows an Approve button, but only if you’re eligible to approve. If you’re not, the banner still shows the request, just without a button you couldn’t use anyway.
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. The rejection itself is recorded on the task’s Activity timeline, so anyone looking at the task later can see that an advance was rejected and by whom. The reason text you type doesn’t show up there. It reaches a subscribed webhook delivery instead, but only when that subscription sends the full payload (see Choosing what a delivery contains), and an email address inside the reason is redacted. So if you want it on the record, leave a task comment too. On the web, use the Reject button in the banner, shown only when you’re eligible.
Rejecting clears the pending-advance state without changing the task’s status. If CodeHerder filed the request, it waits about 15 minutes and then re-files the same request. A fresh rejection restarts that 15 minutes each time, so repeatedly rejecting keeps holding the task rather than the cooldown running out underneath you. Approving clears the cooldown outright, so a later gate on the same task starts with a clean slate.
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.
Separation of duties is always on. The member who requested the advance can never approve it, and neither can the session working that task. Only a different eligible member can approve. The requester can still reject their own request to withdraw it.
If you’re not the right approver, ch task show and the web banner tell you exactly why. See the hint line described above.
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. Your own pending requests are filtered out, because separation of duties means 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, target stage, requester, and when it was requested. See My work for the rest of that page.
Setting up an approval gate
Workspace admins configure approval gates in the web app under Settings → Workflows. Open the workflow you want to change, then open a stage. You will see an approval required to leave ‘
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 —.
The requester can’t self-approve (SoD) checkbox is kept for compatibility. The server ignores it, because separation of duties is always on.
Save the workflow to apply the gate. The gate holds every forward move out of that stage, automatic or explicit. An initiative or epic’s close to done is a plain status update, so a gate on it never fires. Existing in-flight tasks pick up the new gate on their next forward move.
If you edit a workflow and remove the stage a pending advance was headed for, that pending advance is cleared automatically. The task drops back to having nothing pending rather than waiting on a target that no longer exists. Customising workflows covers editing a workflow’s stages in full.
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 <newTaskId>"
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.
Related guides
- How work flows — the stages every task moves through
- Writing tasks an agent can build — how to file work agents can pick up
- Assigning and claiming work — who ends up working a task
- Reviewing an agent’s work — the human side of the review stage
- Why isn’t my task moving? — diagnose a stuck task
Last updated