'Approve All' Is How Your Agent Ships the Dangerous One A developer building agentproto argues that coding agents should auto-approve read-only commands and gate state-changing writes, rather than relying on manual approval or blanket permissions. The approach avoids approval fatigue and destructive errors by classifying actions by mutation risk at the tool level. Disclosure up front: I build agentproto , one of the tools in this market. The primitives in the second half are real and the commands are checkable; the problem in the first half stands on its own. Corrections welcome — file an issue. You gave your coding agent a shell. Now you live in one of two bad houses. In the first house, every risky command stops and asks you. Can I run git push? Can I delete this file? Can I curl this? You approve, approve, In the second house, you got tired of that and ran the agent with --dangerously-skip-permissions . Now it never asks. It also never asks before the rm -rf in the wrong directory, or the DROP TABLE against the wrong DATABASE URL , while you're at lunch. The one idea, if you remember nothing else: Auto-approve reads. Gate writes. The line isn'trisky vs. safe— it's does this change state. Both houses share one bug: they treat "should the agent need approval?" as a property of your attention — how much you're willing to click — instead of a property of the action . Fix that, and the whole problem changes shape. Ask ten teams where the approval boundary goes and you'll get three wrong answers. By tool category : "shell is dangerous, file-read is fine" — except cat ~/.aws/credentials is a read that exfiltrates and echo can clobber a file. By reversibility : "block the irreversible stuff" — except you can't reliably tell at call time what's reversible. By vibes : skip-all until something breaks. The boundary that actually holds is simpler and it's not about the tool at all: Draw it at state mutation.One widely-cited write-up on human-oversight for code agents Niklas Heidloff lands on the exact rule:auto-approve only theNot by tool, not by commands that don't change state; gate the ones that do. reversibility — by whether the world is different afterward. A pure read can run free. A write waits. And here's why "just approve each one" was never going to save you, stated by the people who studied it: manual-approve, skip-all, and static allowlists all fail the same way. Review every action and you hit approval fatigue — after hundreds That's the same wall from the trust piece https://dev.to/agentiknet/you-can-parallelize-the-typing-you-cant-parallelize-the-trust-2fkd , wearing a different hat: you are the serial bottleneck, and "approve each action" just moves the bottleneck to your clicking finger. Place yourself before you build. Four honest answers, each with its ceiling. You approve each one. Every gated call pings you; you click yes. Safe-ish, and completely unscalable — this is the approval-fatigue trap, and it caps at one agent you babysit full-time. Ceiling: you, again. You skip all of it. --dangerously-skip-permissions , YOLO mode. Fast, fully unattended, and one hallucinated path from a very bad afternoon. Ceiling: the first destructive command nobody caught. You keep a static allowlist. A file of pre-blessed commands. Better — but it's frozen; the agent hits something new and either stalls or you widen the list until it's . Ceiling: it can't tell a read from a write, so it over- or under-blocks. You classify by risk and relay only the writes. Read-only calls auto-approve; state-changing calls pause and route to a decision — a human or a policy — and the important ones surface instead of drowning. This is the only one that scales, and it's the one you can build. The rest of this piece wires it. Where are you?If your honest answer is"skip-all, and I hope,"you don't have an autonomy setup — you have an unexploded one. Everything below moves the yes/no off your attention and into the architecture. The relay only works if a call knows whether it mutates before it runs. So put the risk class on the tool contract, not in a human's head. In agentproto a tool declares what it changes; a read-only tool mutates nothing and approves itself: js // a read-only tool — nothing changes, so it runs free export const knowledgeSearch = defineTool { id: "knowledge.search", // …schema… mutates: , // touches no state approval: "auto", // ⇒ auto-approved, never interrupts you } // a write tool — declares the mutation, so it must be cleared export const applyPatch = defineTool { id: "fs.apply patch", // …schema… mutates: "fs" , // changes files on disk approval: "gated", // ⇒ pauses for a decision before it runs } That's "auto-approve reads, gate writes" as two lines of contract instead of a policy you enforce by clicking. knowledge.search from the knowledge piece https://dev.to/agentiknet/your-coding-agent-knows-the-internets-average-heres-how-to-make-it-know-yours-1aeg is literally mutates: for exactly this reason. The boundary is now data, so a machine can enforce it — which is the whole point of making approval, in the words of one human-in-the-loop build Nylas , a first-class architectural feature, not an afterthought you bolt on per tool. Now the gated calls need somewhere to go that isn't a modal blocking your terminal. agentproto turns a pending write into an item in a cross-session inbox: the agent pauses, the decision waits, and anyone — or anything — can answer it. what's waiting on a decision right now? permissions list → { id: "perm 9f", sessionId: "sess 42", tool: "fs.apply patch", summary: "write migrations/003 add index.sql" } answer it — approve once, approve-always, or deny permissions respond { id: "perm 9f", decision: "approve" } Two things this buys that a terminal prompt doesn't. First, it's out of band : the approval isn't trapped in one attached session, so a supervisor or you, from your phone can clear it without sitting in front of the agent. Second, it's programmable : decision is just approve / deny , so the responder can be a human for the calls that need judgment and a policy for the calls that don't. The relay is the L2 rung, built. The supervision ladder maps the whole climb fromyou watchtoa gate decides; this is the L2 "permission relay" rung as actual wiring. Read-only auto-approves Rung 1 , risky writes land here, and only the genuinely judgment-worthy ones ever reach a person. Approval fatigue dies because the reads never showed up in the first place. Relaying the write to a human is fine for a DROP TABLE . But for "did this diff actually fix the bug," a human clicking approve is just a slower version of the agent grading itself. Some writes need a check , not a click . The top rung replaces the human judgment call with something that scales: a gate that runs outside the working agent and has to say yes before the write lands. You attach it to the session, it fires at the end of the agent's turn, and its verdict — not the agent's self-report — decides what happens next. // attach a gate to the session; a green result stages a commit behind YOUR ack attach policy { sessionId: "sess 42", then: "commit", gate: { // a shell gate: exit 0 = pass command: "pnpm", args: "test", "--run", "payments" }, commit: { paths: "src/payments/retry.ts" , // stages EXACTLY these — never git add -A message: "fix: cap payment retries at 3 incident 2026-04 ", requireHumanAck: true // green gate ⇒ waits for your yes } } The lifecycle is the part that matters: the gate runs, and on green the policy doesn't commit — it moves to awaiting-ack and emits policy:commit-ready . The commit does not happen until you call ack policy { policyId, approve: true } , which stages only the listed paths git add --