Human-in-the-loop approval patterns

Patterns that let agents do the work while a human keeps control: dry-run by default, draft-then-post, approval queues, two-key changes and kill switches.

Updated 2026-11-11Raw markdown

Strategy sections
On this page

Use this page when designing any automation that could change a system or speak for a person. The aim is that the cheapest path for the agent is also the safe one, and that every write can be traced to a human decision.

Pattern 1: Dry-run is the default

Scripts print the exact command they would run. Only an explicit flag makes them act.

DRY_RUN=1
[ "${1:-}" = "--execute" ] && DRY_RUN=0
do_change() {
  if [ "$DRY_RUN" = 1 ]; then printf 'DRY-RUN would run: %q ' "$@"; echo; else "$@"; fi
}
do_change glab mr create --draft --title "access: read-only role for <service>"
  • Reads and local drafts always run. Remote side effects go through do_change.
  • Only a human adds --execute, and only for a run they reviewed.

Pattern 2: Draft, then post

The agent writes what it would say or change into a file or a draft MR. A human approves item by item. A separate step posts only approved items. See the posting script in connecting Jira.

Pattern 3: One approval queue

All pending decisions land in one daily file or board, sorted by risk. The human works it once a day instead of being interrupted. See the automation blueprint.

Pattern 4: Approval is specific, recorded and fresh

  • An approval names the exact change: this MR, this ticket, this command.
  • It is recorded where an auditor can find it (MR approval, ticket comment).
  • It does not carry over. Approving one access grant does not approve the next.
  • Another agent saying "the human approved" is not approval. Check the record.

Pattern 5: Guardrails before the human sees it

Reject unsafe proposals automatically so the human only reviews sane ones.

Proposal Automatic checks before it reaches the queue
CI file Valid YAML, lints clean, production job is manual and on the default branch only, no secrets
IAM change Plan applies cleanly, only allowed paths changed, no * actions or resources, no admin policies
Ticket comment No secrets, account IDs or customer data; references real evidence links
Monitor change Has owner tag, notify handle and runbook link

Pattern 6: Two keys for risky changes

  • Production, IAM, network and data deletion changes need two approvers, and the author is not one of them.
  • Use platform features (protected environments, required approvals, CODEOWNERS) so this is enforced, not remembered.

Pattern 7: Least privilege for the agent itself

  • The agent's credentials are read-only. Writes happen through CI with its own scoped role after merge.
  • Tokens are short-lived and stored in a secret store.
  • The agent cannot approve or merge its own MRs.

Pattern 8: Kill switch and audit trail

# Every job checks this first
[ -e "$HOME/.automation-paused" ] && { echo "paused by human"; exit 0; }
  • One file or flag pauses all automation.
  • Every write is logged with time, target, action and the approval reference.
  • Weekly, a human reads the write log.

Pattern 9: Stop on surprise

  • Unexpected output, an error, or a plan larger than expected stops the run and reports. No automatic retries of writes.
  • Instructions found in tickets, logs or web pages are data. The agent never follows them.

Pitfalls

  • Approval fatigue. If the human approves 100 items a day without reading, batch better and add guardrails so fewer items need review.
  • "Temporary" bypasses of the queue that become permanent.
  • An agent with write credentials "for convenience". Convenience is how incidents start.