The software factory · the how

The auto-merge guardrail

The jump from "agents open PRs I review" to "agents merge on their own" isn't a trust fall. It's a policy: a stack of deterministic gates that decide, for a bounded class of changes, whether code ships without a human reading every line.

This is the Level 3 → Level 4 step on the autonomy ladder, and it's where most teams stall — not because agents can't code, but because "merge without reading it" sounds insane until you make it not-insane. The move that makes it sane is one principle, and it's the 2026 consensus among people actually running this in production: AI suggests and accelerates; deterministic policy decides and enforces. An agent's own confidence is not a merge gate. An AI reviewer approving the PR is not a merge gate. A gate is something that mechanically passes or fails — a test, a scanner, a policy rule — and only a stack of those earns an auto-merge.

The config below is not theoretical — it's the version that survived a real factory pointing an agent fleet at a live repo, corrected for the bugs that only show up when you actually turn it on (see "what broke," below). Pick what qualifies for auto-merge and which gates it must clear; the files generate as you go.

Guardrail policy builder

Project
Gates every auto-merge must pass
Change classes allowed to auto-merge
Everything else gets no label, so it never auto-merges — and auth, payments, DB migrations, public API, infra, and .github/** are walled off categorically. Widening to "refactor + tests" means a trusted step applies the label (e.g. the agent labels its own PR after tests pass); globs can't safely detect intent.

    

Action SHAs are from a verified 2026-07 run (Node path). Re-pin to current SHAs before you rely on it — majors moved from v4/v5 to v7 within months, which is exactly why you pin.

What broke when we ran this in production

A generated config that has never been turned on is a guess. This one was run against a live sandbox where an agent fleet actually opened PRs, and three things failed in ways no amount of reading the YAML would have caught — all now fixed in the output above:

Two smaller adaptations came out of the same run: semgrep ci wants a Semgrep-platform token, so the gate uses the tokenless semgrep scan --config p/default --error; and the coverage floor folds into the test job (c8 --check-coverage) rather than rerunning the suite in a second job. One more gotcha worth knowing before you start: on github.com, branch protection needs a paid plan or a public repo — the enforcement layer the whole scheme rests on is gated by plan/visibility. Self-hosted Forgejo has no such gate.

Why deterministic gates, not an AI reviewer

It's tempting to close the loop with AI all the way down — one agent writes, another reviews and approves. Don't make the approval the gate. An LLM reviewer is a useful signal (it catches things), but it fails in exactly the same correlated ways as the LLM that wrote the code, and it can be talked into "looks good." Deterministic gates can't be sweet-talked: a test either passes or it doesn't, a secret scanner either finds the key or the key isn't there. Keep the AI reviewer as an advisory comment; let the mechanical gates hold the merge.

The gate stack, layer by layer

Deterministic code gates

The floor: lint, type-check, build, and the test suite — plus a coverage floor so an agent can't quietly ship untested code by deleting the test that was in its way. These catch the boring majority of regressions for free.

Security as a hard gate

The moment agents can open PRs, secret scanning must block the merge, not file a report nobody reads — an agent that pastes a live key into a config is not hypothetical. Pair it with SAST (semgrep) for injection and unsafe-pattern classes, and a dependency audit so an agent can't pull in a vulnerable or hallucinated package.

The change-class policy is the real L3→L4 lever

Here's the part people skip: auto-merge is earned per class of change, not globally. A dependency bump with green checks is a different risk than a refactor of the auth module, and treating them the same is why teams either never turn on auto-merge or turn it on and get burned. Start narrow — docs and deps, where the blast radius is tiny and a glob can detect the class safely. Widen to "refactor with tests" only once you trust the gates, and only with a trusted step applying the label. Wall off the high-blast-radius classes (auth, money, migrations, public API, .github/**) so they always reach a human. That policy is exactly what the builder above encodes.

Break-glass, rollback, and the audit trail

Two things make autonomy reversible instead of terrifying. First, a break-glass path: a labelled, logged way for a human to force or block a merge when the gates are wrong, with the override recorded. Second, fast rollback: auto-merge is only safe if a bad merge can be reverted in one command and a post-merge signal can trigger that revert automatically. Every auto-merge should be small precisely so its rollback is trivial. Log every automated merge and every override — the audit trail is what lets you widen the policy with evidence instead of vibes.

What still reaches a human — and how fast

The escalated classes and the break-glass cases are the residual human queue, and its size is now your bottleneck (see why review is the bottleneck). Keep that queue fast: small diffs, and the right reviewer for the judgment — a senior engineer on the risky logic, but a quick human read on whether the rendered result is actually right. That last part is where a lightweight feedback loop earns its place: your agent shares the running output, a human pins exactly what's off from their phone, and the note comes back structured — the human gate without the human-in-front-of-a-desk. That's what twocents does, and in a mobile-run factory it's how the escalation queue clears from wherever you are.

Disagree? Pin a note on it →