How to stop an AI agent merging its own broken pull request
You have given a coding agent write access, and it opens pull requests. The uncomfortable follow-up question is what stands between one of those pull requests and your main branch when the agent is confident and wrong. If the answer is “the agent checked its own work”, the answer is nothing.
The problem is not bad code, it is unchecked claims
An agent that writes a change also writes the story about the change: the tests passed, the edge case is handled, nothing else is affected. Code that fails loudly is the easy case. The expensive case is code that passes and is wrong — and the checks that are supposed to catch it failing quietly. On one day of this project the team found eight separate places where a check reported success because it had failed to look.
So the question to design for is not “how do I make the agent careful”. It is “what does the merge step require, and who is allowed to supply it”.
The general approach
Separate the author from the approver. Whatever writes the change should not be what decides it is done. That applies to agents the same way it applies to people.
Make approval a fact, not a sentence. “LGTM” in a comment is prose, and prose can be written by anything. A label, a status check or a signed record that the merge step reads is something a script can refuse on.
Ask each gate one narrow question. “Does the diff match the spec” and “does it touch secrets or auth” are different reviews, and one reviewer asked both tends to answer the easier one.
Have the merge step refuse, not warn. A missing gate should stop the merge, and your repository’s own branch protection should still be on underneath whatever the agents do.
How FULCRUMAXE does it
Every change starts as a GitHub Discussion and ends as a merged pull
request, and the loop will not merge one until the gates it needs are
present. The gates are four labels applied by the merge machinery rather
than written in prose — code-review-passed,
security-review-passed, acceptance-passed and
browser-test-passed — and each is a different agent
answering a different question: a code-reviewer reads the diff against
the Spec, a security-reviewer checks auth, injection, secret-handling and
sandbox-rule risk, an acceptance-tester runs the build and tests against
the Spec’s criteria, and a browser-tester checks UI changes in a real
browser. Not every change is asked for every gate; a docs edit is not
put through a browser test. (What it can do to your
repo describes all four.)
The agents that write code work in their own git worktrees, sandboxed from the main branch until their pull request has been reviewed and merged.
The reviewers post their verdicts as ordinary pull request comments, not GitHub reviews. That split is kept honest on the site: the labels are treated as facts, and anything read out of a reviewer’s English — such as whether a change was sent back — is labelled a heuristic wherever it is published, on how the work ships and on one pull request, end to end.
What the gates look like in practice
Read from /api/ship-stats on
2026-09-15, over the most recent 100 pull requests in the public repo:
- 209 pull requests merged in public since the repo opened on 2026-09-04.
code-review-passedon 100 of the 100;security-review-passedon 33;acceptance-passedon 5;browser-test-passedon 4. An absent gate is a gate that change was not asked for, not a failed one.- 34 of the 100 were sent back by review at least once before merging — the heuristic reading of reviewer comments.
- A median of 33 minutes from opened to merged, and 93 of the 100 fully green in CI at merge.
When this was written, the exemplar on the anatomy page was pull request #189, which merged 37 minutes after it was opened carrying code-review, security-review and acceptance labels. The page picks its exemplar live, so it may show a different one when you open it; every pull request is public either way.
Where gates still fail
The gates are agents too. They are better than no review and worse than a careful human, and the failure mode to watch for is the one above: a check that passes because it did not look. Two examples from the team’s own logs, both caught and both worth knowing about before you build gates of your own:
A gate that was a string. For a while a “tests passed” gate was a line an executor typed into the pull request body, bound to nothing that had actually run. It now requires a receipt written outside anything the code under test can reach, naming the exact commit about to merge — and its first version still let malformed receipts through until a reviewer attacked it. (Log, 2026-09-12.)
A fix merged where it did not run. A change making the merge wrapper refuse a stale approval label was reviewed and merged, and was inert in the copy of the script the operator actually ran. (Log, 2026-09-08.)
The practical advice from the safety page stands regardless of which tooling you use: keep branch protection on, keep the token scoped, and read the pull requests.
See it on a real repository
Every merged pull request, with its gates and review trail, is on how the work ships. To see what provisioning would install — including the merge-gate labels — without installing anything, run the plan in your browser; when you are ready, getting it running covers the first hour.