What is a human approval gate for an AI agent?
A point where the agent's work stops until a person approves, enforced by something outside the agent. The agent can ask for approval, but text in the conversation is not the approval: the yes has to arrive through the mechanism the gate checks, and the record shows whose account sent it.
Three properties separate a real gate from a polite request: the check runs in code the agent does not control, the work actually waits (minutes or days), and the record shows who moved it on.
Gates are one form of human-in-the-loop control. The other common form is review after the fact: the agent acts, a person checks the result. Both are useful. Gates belong where undoing the action is expensive or impossible, review belongs everywhere else.
- Enforced outside the model: a permission layer, a hook, a platform or an engine.
- The work waits, and resumes from the same place after the yes.
- The approval leaves a record you can read later.
Why is "ask me before you deploy" in a prompt not a gate?
Because the model decides whether the instruction applies. Anthropic's Claude Code docs say instruction files are treated "as context, not enforced configuration", and recommend a hook to block an action regardless of what Claude decides.
A prompt rule usually works. It fails in the cases that matter: long sessions, conflicting instructions, a task that does not look like a deploy, or text from a file or web page that tells the agent something else. A gate does not read the situation; it checks a condition.
This matters most for agents that read untrusted input: issues, emails, web pages, documents from customers. Text in that input can tell the agent the change was already approved, or that this case is an exception. A gate checked by code does not read that text at all.
sourcesClaude Code docs: memoryClaude Code docs: hooks
in the prompt read by the AI
Shipped. Nobody said yes.
as a gate checked by the engine
Waiting for you.
What kinds of approval gates are there?
Gates differ in where they live and how long they can wait. Pick the one whose scope matches the risk. Dedicated approval services are another option; see how ConvOps compares with HumanLayer.
| Gate | Where it lives | Can it wait for days? | Example |
|---|---|---|---|
| Prompt instruction | The model's context | No, and it is not enforced | "Ask me before pushing" |
| Permission ask rule | Claude Code settings | Only while the session is open | ask: Bash(git push *) |
| Hook decision | Your script, before the tool runs | Only while the session is open | PreToolUse returns ask or deny |
| Platform approval | CI or deploy platform | Yes | GitHub environment required reviewers |
| Workflow engine gate | A process engine outside the agent | Yes | ConvOps requires_approval on a step |
| Structural condition | A process engine | Yes | Wait until child tasks are complete |
How do I add approval gates in Claude Code today?
Use permission rules for command patterns and PreToolUse hooks for anything that needs logic. Both run outside the model. Rules evaluate deny first, then ask, then allow, and a hook's decision cannot override a deny or ask rule.
Commit .claude/settings.json to the repo so every teammate and every session gets the same ask and deny rules. Personal overrides go in .claude/settings.local.json, which stays out of git.
- 1
Ask before risky commands
Add ask rules to .claude/settings.json in the repo so the whole team gets them. Deny what must never happen.
{ "permissions": { "ask": ["Bash(git push *)", "Bash(npm publish *)"], "deny": ["Bash(git push --force *)", "Read(./.env)"] } } - 2
Ask on a condition
A PreToolUse hook can inspect the call and return an ask decision with a reason, for example when a migration touches a production schema.
{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "ask", "permissionDecisionReason": "Migration touches production tables. Confirm." } } - 3
Know what happens unattended
In claude -p with nobody attached, an ask cannot be answered, so it is denied. That fails safe, but the work stops instead of waiting. Claude Code Routines run without stopping for approval, and Anthropic notes the fired prompt cannot act as approval or consent. Dynamic workflows have no mid-run user input.
- 4
Gate the deploy on the platform
For deploys, a CI platform gate also works: GitHub environments can require a reviewer and prevent self-review. As of October 2026, GitHub documents required reviewers on private repositories only for Enterprise plans.
sourcesClaude Code docs: permissionsClaude Code docs: hooksClaude Code docs: headless modeClaude Code docs: RoutinesClaude Code docs: Dynamic workflowsGitHub docs: deployment environments
Where should approval gates go in an agent's process?
Where a wrong move is expensive or hard to undo, and nowhere else. Too many gates train people to click yes without reading, which is worse than no gate.
Give the approver what they need to decide in one screen: the diff or the draft, the test results, and what happens if they say yes. A gate that asks "Ship it?" next to a clear summary gets read. A generic "Continue?" gets clicked.
| Gate here | Usually no gate |
|---|---|
| Publishing anything outside the company | Drafting, research, reading code |
| Merging to main or deploying | Running tests, lint, local builds |
| Changing production data or schemas | Changes on a branch under review |
| Spending money or sending messages to customers | Internal notes and summaries |
| Changing permissions, secrets or access | Reading configuration |
- Put the gate on the step, not on the tool, when the risk depends on context.
- Write what the approver should check in the gate prompt.
- Review the list every few weeks. Remove gates that never catch anything.
How do approval gates work in ConvOps?
A gate is part of the workflow step, not of the prompt. Claude Code, or any MCP client, works the current step and asks to advance. The engine evaluates the gate on every advance against real state. If it is not met, the cursor stays put and the agent is told exactly what is missing.
Approval has to be passed explicitly on the advance call; it is never inferred from chat text. The advance that passes the gate writes an audit row with field-level before and after on the task and the workflow instance, with two identities: the agent label the caller declares, and the person the server verified from the credential. There is no separate approver field, so the record shows the account behind the advance. Unattended runs stop at the gate and wait for a person.
Not every gate needs a person. wait_for_children holds a parent until its child tasks finish, and require_context will not close a step without a recorded why. Combine a structural condition with requires_approval and a person is asked only once the work is actually ready, which keeps each approval meaningful.
- 1
Put the gate on the step
requires_approval holds the step for a person. Structural keys hold it for real state: wait_for_children, sequential_children, task_status and require_context.
{ "id": "approve", "gate": { "requires_approval": true, "wait_for_children": true, "prompt": "Ship it?" } } - 2
Let the agent try
An early advance is refused with the unmet conditions, verbatim.
workflow_advance(task_id="…") { "current_step": { "id": "review" }, "unmet_conditions": [ "Requires explicit user approval", "1 of 1 child tasks are not completed" ] } - 3
Approve
You review the work and advance with your approval. The next step arrives; the audit keeps the agent label and your signed-in account.
What happens when the approver says no?
The work should go back to a named step, not stall. A well-designed gate defines the no path as carefully as the yes path: which step rejected work returns to, and what the approver has to see before deciding.
In Claude Code, denying an ask blocks that one tool call; the model is told the call was rejected and chooses what to try next in the same session. Nothing records where the work should go after a no.
In ConvOps, a step can name an on_rejected target, any step of the same workflow. Rejecting the advance moves the run straight to that step, for example from approve-draft back to revise-draft, and the task takes that step's status. The step history keeps a rejected row and the audit marks the verdict. A step with no on_rejected target refuses the rejection, so the run stays at the gate.
| Part | Question it answers | In ConvOps |
|---|---|---|
| Trigger | Which step needs a yes? | requires_approval on the step |
| Context | What does the approver check? | The gate prompt and the work on the task |
| Yes path | Where does approved work go? | The next step of the workflow |
| No path | Where does rejected work go? | The step named in on_rejected |
| Waiting | How long can the work wait? | Until someone answers; every step is persisted |
| Record | Who moved it on? | Audit row with the agent label and the verified account |
{
"id": "approve-draft",
"on_rejected": "revise-draft",
"gate": { "requires_approval": true, "prompt": "Publish this draft?" }
}
workflow_advance(task_id="…", approved=false)
→ current_step: "revise-draft"How do I remove gates as trust grows?
Start with approvals on every step. As trust grows, let the work run on its own. Remove a gate when the record shows it never changes the outcome, and keep it where the consequence is high.
| Stage | Gates | Evidence to move on |
|---|---|---|
| Start | On every step | Steps approved without changes |
| Trusted steps | Only on publish, merge and spend | Runs finish without blocked tasks |
| Unattended | Gates where consequence is high; runs wait there | Audit and run records you review weekly |
Frequently asked questions
What is the difference between a gate and a policy?
A policy is a rule the agent is given to follow. A gate is a condition the system checks before the work can move on. Policies inform; gates block. Use a gate where a mistake is costly.
Is an approval gate the same as human-in-the-loop?
An approval gate is one kind of human-in-the-loop control: the work stops before an action until a person approves. Review after the fact is another kind. Use gates where an action is costly or hard to undo.
Can Claude Code wait for my approval?
In an open session, yes: a permission ask rule or a hook ask decision prompts you. In headless runs nobody can answer, so the call is denied and the work stops. Waiting for days needs state outside the session.
How many approval gates should an agent workflow have?
Start with one on every step while you learn the process. Then keep gates where a wrong move is expensive or hard to undo: publishing, merging, deploying, spending, and changing access.
What happens to the work when I reject it in ConvOps?
If the step names an on_rejected target, the run moves straight to that step, such as a revise step, and the history records the rejection. If the step has no on_rejected target, the rejection is refused and the run stays at the gate.
How do I know who approved a step in ConvOps?
The advance that passes the gate is written to the audit with the agent's label and the verified account the call ran under. There is no separate approver field, so the record shows the account behind the advance.
Do approval gates work with tools other than Claude Code?
ConvOps gates live in the workflow, so they apply to any MCP client that works the task: Claude Code, Codex, Cursor, ChatGPT, OpenCode and others. Claude Code permission rules and hooks apply only inside Claude Code.
terms in this guide
go deeper