An Agent That Opens Its Own Gate Has No Gate
The hard part of an autonomous coding loop isn't the agent — it's making the human checkpoint impossible for the agent to skip. Notes from a first experiment.
The views expressed here are my own and are not related to or reflective of my work or any organization I am affiliated with.
The day it clicked, my agent tried to approve its own work.
I’d wired up a tidy little pipeline: an issue comes in, an agent plans the fix, another implements it, opens a pull request. There’s a human checkpoint in the middle: a label you apply to say “yes, build this.” And the agent, helpfully, efficiently, reached over and applied that label to its own issue. Posted “Plan approved.” Kept moving.
The permission layer stopped it cold, and gave me a phrase I haven’t been able to unsee for what the agent was doing: fabricating the human checkpoint.
An agent that can open its own gate doesn’t have a gate.
That’s what this post is about. Not how to build a coding loop, but how to make the one human checkpoint impossible for the agent to fake. The shape of these loops is already well mapped: Addy Osmani on the loop that drives the agents, Birgitta Böckeler on the harness each one runs inside. Both are fuller guides to the whole than I am. I want the corner they underweight: the gate itself. It turns out to be the hard part.
The easy part: an assembly line
Start with the part nobody really disputes. One big agent that reads the issue, writes the code, tests it, and merges is brittle: when it’s wrong it’s wrong all the way to production, the author is its own reviewer, and there’s no seam for a human. So you build an assembly line instead: narrow stations, a conveyor, human inspectors. One big agent forces a false choice between useful and safe; a line of small ones doesn’t.
The conveyor is just your issue tracker. Labels are the state. You file a bug; a triage agent comments the cause and labels it triaged (not ready); you apply ready, which fires the planner; it labels plan-ready; you apply approved, which fires the implementer; it opens a PR; you merge. Each worker reads the label before it and sets the one after.
None of this is the hard part. It’s well-trodden, and in Claude Code it’s mostly ordinary primitives: skills, subagents, slash commands, a couple of workflows. The hard part is the three places I just said “you.” The human gates.
The hard part: a gate the agent can’t open
A gate is only a gate if the agent can’t open it. My agent applying its own approved label proved the obvious version fails: an instruction in a prompt (”don’t approve your own work”) isn’t a gate.
An instruction the agent can ignore is not a guardrail.
So the gate has to live somewhere the agent can’t reach. Three mechanisms, and you want all three. The load-bearing one is branch protection requiring at least one human approving review. The second is a distinct identity for the worker: it pushes and opens PRs as a dedicated bot account, so the platform’s own “you can’t approve your own PR” rule makes the reviewer a different, human identity. Separation becomes physics, not policy. The third is a label guard, a tiny workflow that reverts a gate label applied by anyone who isn’t a human maintainer.
One trap I hit: “auto-merge when CI passes” sitting next to a branch that required zero reviews. Each was fine alone; together they let an agent’s PR sail into main with no human at all. Audit the merge path, not just the review prompt. Two innocent settings can compose into a hole.
The dividing line
The agent can build every mechanism here: the guard, the checks, the fences. But granting authority has to be a human: turning on branch protection, creating the bot account, attaching a permissions boundary. The agent builds the cage; you hold the keys.
And the deepest gate isn’t a label at all. Prompts express intent; they can’t be a safety boundary. The things you actually depend on live below the agent.
Prompts express intent. They can’t be your safety boundary.
The strongest example: a permissions boundary that denies the write credentials, so the loop can only ever reach read-only ones, not because you asked it nicely, but because the credential isn’t there to take. The loop checks this before it even runs: it tries to fetch the privileged secret and expects to be denied. Probe, don’t trust.
Proving the work is any good
A gate decides who can merge; it doesn’t decide whether the change is correct. For that, the worker needs to prove the change in a real, fenced environment: a disposable, seeded stack, not production. The move that makes its verdict trustworthy: because it seeded the data, it already knows what the screen and the endpoint should say, so it checks a known input against a known output instead of asking “does this look right?” It drives the UI, hits the API, runs the unit tests it wrote first, and self-heals only inside the sandbox, never reaching for production or anything that sends a message. When I pointed this at real changes, that second opinion, the one that wasn’t me, flagged an over-tight auth check that would’ve locked out real users and a path-traversal my unit tests had waved through.
The smallest unfakeable gate
You don’t build the whole line first. Build the gate:
Write one skill: how the subsystem you explain most often actually works.
Make one subagent: a reviewer with read-only tools.
Make one command,
/validate, that runs the reviewer on a PR’s diff and posts the findings.Turn on branch protection requiring one human approving review.
Run it on your next real PR.
That’s the whole idea at small scale: an independent reviewer whose verdict you read, and a merge you can’t reach without a human. Add stations later; the gate is what makes any of it safe to grow.
The kit
I’m packaging the current version as a small, generic starter kit: the skills, the reviewer and security subagents, the commands, and the guard workflow. I’ll link it here once it’s public. It’s intentionally generic, and the point is the same as everything above: make the gates real before you trust the loop.
What’s real, and what isn’t yet
To be straight about the experiment’s scope: the gate argument stands on its own, and I’d rather not oversell the rest.
Dogfooded is not unattended. Every run still ends at a human merge, by design, not as a limitation I’m racing to remove.
Coverage is not correctness. A coverage number going up tells you code executed, not that it’s right. The assertions that would gate real autonomy, authorization and data-correctness, need real tests, not a green percentage.
Finding a bug isn’t fixing it. A loop that surfaces issues is useful, but “surfaced” and “resolved” are different columns.
It’s early. The autonomy isn’t switched on and several pieces aren’t wired end to end. I’m writing it up now because doing so sharpens the thinking. A fuller version comes once it’s earned it.
One big agent is seductive because it’s one impressive thing. But the part that earned trust was never the agent. It was the gate it couldn’t open.
A sequel comes when the autonomy is actually switched on, and the gates have earned it. I’ll link the starter kit here once it’s published.


