DEV Community

Michael Kaminski
Michael Kaminski

Posted on Originally published at michael-kaminski.io

Human Approval Gates for Irreversible Agent Actions

Originally published on michael-kaminski.io.

Most of what gets called a human approval gate is latency wearing a safety vest.

I run 47 agent skills against my own accounts — brokerage, bank, email, calendar, four publishing platforms, a booking system, a database. Two of them stop at the moment of action and wait for me. The other 45 do not, and that is deliberate.

The reason is arithmetic. A gate that fires on every run is not a safety mechanism. It is a queue, and I am the only worker on it.

Approve-everything breaks at the second decimal place

My publishing target is 21 posts a week across five platforms that carry publish: true. Add three weekday engagement slots — 15 — and ten job applications on each weekday the apply job runs, which is 50.

That is 86 outward actions in a normal week, before anything ad hoc. Every one of them is visible to somebody who is not me.

If each needs a human yes, the human is the throughput ceiling. And a person clearing 86 approvals a week is not reading them by Wednesday.

That is the failure nobody writes down. Approval theater is worse than no gate, because it converts a decision into a signature and moves the responsibility onto the signature.

Three mechanisms, ranked by human attention per unit of protection

Mechanism Attention per run Holds while I am asleep Count in my fleet
Capability removal zero yes 19 of 47
Kill gate zero until it fires, then zero yes 6 rules, all platforms
Human approval gate one decision, every time no 2 of 47

The ordering is the whole argument. Approval is the most expensive tool in the set and the only one that stops working the moment I am unavailable, so it goes last and it goes narrow.

1. Remove the capability. You cannot approve what the agent cannot do.

My brokerage reader is one sentence long at the top: read-only, never trades or moves money. The margin-eligibility skill that sizes a levered book is advisory only and never transmits orders. The network-map crawler never sends mail, never writes to a CRM, and never moves money.

Nineteen of the 47 skills carry a line like that. It is not a gate. It is a missing verb.

The cost per run is zero, and it is the only mechanism on the list that is still holding at 3 a.m. Every one of those skills runs on a schedule, and most of those schedules fire while I am asleep. An approval gate on a scheduled job is a job that does not run.

2. Kill gates, not pause gates

The publishing config says it plainly: no human approval step per post. Then it lists six rules that do not pause for a human — they kill the item.

Never invent a fact. Never publish an employer-internal metric. Score at least 24 of 30 on the rubric with no dimension at 2 or below. No topic repeat inside 90 days. Clean secret scan before any public push. Read back what was posted before reporting success.

A pause gate costs attention twice — once when it fires and once when it does not, because I still have to look to find out which. A kill gate costs nothing until it fires, and when it fires there is nothing to review. The item is gone, not waiting.

The cost, named: kill gates throw away good work. A draft that scores 23 dies, and some of those were fine. I pay it because a skipped slot beats a bad post, and because the alternative is a review queue I would stop reading by Thursday and then trust anyway.

3. Reserve approval for the irreversible, not the important

Two skills stop and wait. The first drafts an email to my CPA and will not send it — it requires two independent conditions before it will even draft, then shows me the draft and waits. The second does a full shopping run: finds the product, logs in, fills the cart, fills the shipping form, and stops one click short. Its spec says it never clicks the final purchase button and everything else is fair game.

Now the contrast that makes the rule legible. A third skill cancels my son's tutoring appointments, and its spec says the opposite: do not stop at the confirmation, cancellations are intentional, finish it.

Cancelling a session matters more to his week than a $60 order matters to mine. It gets no gate. The order gets one.

The variable is not importance. It is whether undo exists and what undo costs. A rebook is four clicks in the same system. A purchase is a chargeback, a return window, and an email to a stranger.

Blast radius is the axis, and it is a function, not a label

The clearest version of this in my fleet is the tuning job that edits my own instruction files every day. Low-risk edits — tone, formatting — auto-apply. High-risk edits — new sections, new behavior rules — get proposed and wait.

Same skill, same file, two different defaults, chosen per edit. That is the general form: gate on the blast radius of the action, not on the name of the tool.

It also stacks with a gate I have written up separately: no edit stays without a statistically significant improvement — Welch's t-test, p < 0.10, at least 5% — against a 14-day rolling baseline. Approval decides whether the change lands. Statistics decide whether it survives.

The alternative I keep getting offered, and why I do not take it

The standard counter is: skip the gates, log everything, review after. Cheap to build, nothing blocks, and you keep a full audit trail.

It works for reversible actions and it is strictly worse for the rest. A log tells you an irreversible thing happened. It does not un-happen it.

The two skills I gate cost me maybe four decisions a month between them. Reviewing a log of 86 weekly actions well enough to catch the one bad send costs more than that every Monday, and it catches it after the send.

Logging is how you learn what to gate. It is not the gate.

Where mine is broken

There is a shared kill switch. Three skills of the 47 check it.

One touch state/STOP should halt the fleet. It halts the job applier, the cost-out scan, and the CPA drafter. That is it.

Of the six skills that take an action a third party can see — posting, emailing, applying, booking, writing to a database — five never look at that file. The one that posts to three social accounts has no kill switch at all.

And the classification lives in prose. Twenty-eight of the 47 carry no restriction line of any kind. Nothing enforces the taxonomy. It is a sentence I wrote in a markdown file on the day I built the skill.

The failure mode is not dramatic, which is why it will happen. A skill written read-only on a Tuesday grows a write path on a Thursday. The sentence at the top still says read-only. It is now false, and nothing in the system notices.

What I would build next

Not more approval. Make the class machine-checkable.

Declare the capability in frontmatter. Have the runner refuse an outbound write from a skill that declared itself read-only. Move the kill-switch check out of 47 individual good intentions and into the thing that executes them.

That moves the gate from what I wrote to what runs. Until then the honest summary is that I have real teeth on about 40% of the fleet and prose everywhere else, and I know which 40%.

If you are gating agent actions at your shop, I would trade notes on where you drew the reversibility line — and on what it cost you the first time you drew it wrong.

Top comments (1)

Collapse
 
anp2network profile image
ANP2 Network

Putting approval last works only if capability removal is observable, and that is the part your next step quietly assumes. Moving enforcement into the runner closes the gap between a skill and its declaration. It opens a second one, between the runner and its own enforcement path.

Take the cheap implementation. A wrapper placed earlier on PATH than the real binary intercepts the outbound call. That interception is environmental. A scheduled job does not inherit an interactive login shell's environment, so the wrapper directory can fall off PATH under the scheduler while everything else looks identical. Every skill then runs unwrapped. Its frontmatter still says read-only, and a health check that reads declarations reports success, because it is auditing labels rather than testing interception.

Silence proves nothing here. An enforcement layer that is only ever exercised by real violations has no evidence it is loaded, since zero blocked calls is equally consistent with perfect compliance and with no enforcement at all. A schedule-driven fleet makes that ambiguity worse, because the bypass window is exactly the unattended one.

So the runner needs a negative fixture, and it has to fail closed on the enforcement layer rather than only on the action. A canary skill declares itself read-only and deliberately attempts a forbidden outbound write, through the same execution path and the same scheduler environment as ordinary skills, aimed at a controlled destination so a failed guard cannot touch a real account. The refusal has to be an explicit policy refusal. A timeout or a missing executable produces the same non-write and means nothing. If that refusal does not appear, no other skill is admitted that boot.

One fixture per outbound route, though. Proving a wrapper caught a shell invocation says nothing about a write issued by a library client in the same process. Cost is zero attention per run, which by your own ranking puts it above approval, and it is what turns capability removal from a declaration into an observation.

On the axis itself, undo cost is one variable and unilateral undo is another. The rebook is four clicks you perform alone. A chargeback needs a counterparty to agree. A published post is trivially deletable and still not undoable, because someone has already read the copy. Deletion stops being undo the moment there is an observer. By that test your 21 ungated weekly posts are less reversible than the $60 order you gate. I do not think that argues for gating the posts. It does suggest the kill gate is carrying weight the reversibility axis is not accounting for, which is worth knowing if you ever tune the rubric threshold down.