← Back to blog
Product July 26, 2026 by Javier Arancibia

The Ratchet — How My AI Fleet Merges Its Own PRs

An AI agent that opens pull requests is the easy part. The hard part is what happens next.

My AutoMaintainer fleet opens several PRs per day on my repos. Each one is verified — built, tested, fixed, re-verified — before it ever reaches GitHub. But verified is not the same as merged. Someone still has to click the button.

That someone was me. And I was the bottleneck.

The naive solution is dangerous

The obvious fix is auto-merge: if CI is green, merge it. Every platform has this. It is also how you ship a broken production at 3am.

A green CI check tells you the tests pass. It does not tell you:

  • Whether the PR is actually a fix or a feature disguised as a fix
  • Whether it touches security-sensitive code
  • Whether it's 200 lines of test boilerplate or 200 lines of auth logic
  • Whether the title matches what the diff actually does

A static rules engine helps — "merge if small, if fix:, if CI green" — but it blocks 80% of PRs because most real PRs are slightly over the line limit or have a feat: prefix. Then you're back to reviewing them manually. The rules engine just moved the bottleneck.

The ratchet

I did not want a rules engine. I wanted an AI agent that could read the diff and make judgment calls — the same thing a human reviewer does, but at 4am without complaining.

So I built a ratchet: four levels of escalating cleverness. The merger tries the strict gate first. If that fails, it tries a smarter evaluation. If that fails, it delegates to another agent. Only if all of that fails does it escalate to me.

Level 1 — Strict gate (first time seeing the PR)

All nine safety rules must pass: small (≤100 lines), safe type (fix:/docs:/test:, not feat:), CI green, no security-sensitive paths, no CHANGES_REQUESTED reviews, not a draft, age ≥30min, no active run on the repo, no veto labels.

Most small fix/docs/test PRs merge here. This is the easy 40%.

Level 2 — Relaxed gate (blocked for 2 hours)

For PRs that failed level 1 on a soft rule (size or type), the merger gets smarter:

  • Size: re-count effective lines — exclude test files, docs, generated JSON. A 138-line PR with 67 effective lines of actual code is safe. A 138-line PR with 130 lines of auth logic is not. The line count was never the real signal; the code line count was.
  • Type: read the diff. If the title says feat: but the content has no new exported symbols, no new API endpoints, no new config options — it's a fix wearing a feature's hat. Re-classify and merge. If it genuinely adds new exports, keep it blocked.

This is where most blocked PRs self-unblock. The merger is doing what a human reviewer does: looking past the label at the actual change.

Level 3 — Delegate (blocked for 4 hours)

If the PR is still blocked, the merger doesn't escalate to me — it escalates to another agent. It files a GitHub issue asking the operator (the loop that creates PRs) to split the PR, address review feedback, or separate a feature from its fix.

The operator picks it up on its next cycle. The merger keeps retrying the original PR in case the operator's work unblocks it.

Level 4 — Escalate to human (blocked for 6+ hours)

Only now does the merger ask for my attention. It adds a needs-human-review label and posts a comment with the full history: what blocked it, what self-unblock attempts were tried, what issue was filed. I review, merge manually, remove the label, and the merger picks it back up.

In practice, about 1 in 10 PRs reach level 4. The other 9 self-unblock or get delegated.

Three loops, one fleet

The merger is one of three AI loops running on a single 10GB LXC container on my Intrane network:

  • am-operator — spawns fix runs via AutoMaintainer, files issues, opens PRs. Cannot merge. Recycles every 1 hour.
  • am-operator-buddy — read-only advisory observer. Writes steering suggestions. No power at all. Recycles every 4 hours.
  • am-merger — merges safe PRs and deploys. Cannot spawn runs. Recycles every 4 hours.

The separation of powers is deliberate. The operator creates PRs but cannot approve them. The merger approves PRs but cannot create them. No single loop can both write and approve its own work — the same principle that prevents a single developer from merging their own PR without review.

What happened last night

I deployed the ratchet merger at 02:51 UTC. Here's what it did in its first two passes:

Pass 1 (strict): merged 2 PRs — a 2-line fix and a 22-line panel fix. Closed 1 superseded PR. Gated out 5 PRs (too big or feat: type). Logged each with effective-line pre-computation.

Pass 2 (relaxed, 2 hours later): read the diffs on the 5 blocked PRs and merged 4 of them:

  • A 138-line PR with 67 effective lines (rest were tests) → merged
  • A 203-line PR with 50 effective lines (rest were tests) → merged
  • A feat: PR with no new exports (re-classified as fix) → merged
  • A feat: PR with no new endpoints (re-classified as fix) → merged

One PR stayed blocked — a 176-line security hardening change that's genuinely too big and touches auth code. It will go to level 3 (delegate to operator for splitting) on the next pass.

Total: 7 PRs merged and deployed in one night, zero human review. I woke up to a clean PR queue and a deployed panel + worker.

Why this works

The ratchet works because it treats the merger as an agent, not a rules engine. A rules engine has one setting: pass or fail. An agent can try, fail, reason about why it failed, and try a smarter approach.

The key insight is that most blocked PRs are blocked for bad reasons. The title says feat: but the diff is a fix. The line count is 138 but 70 of those are tests. A human reviewer would catch this in 10 seconds. The ratchet lets the AI do the same thing — just slower, across multiple passes, with full audit logging.

The PRs that stay blocked should stay blocked. A 176-line auth change is exactly the kind of thing I want to review manually. The ratchet doesn't try to merge everything — it tries to merge everything safe, and escalates the rest with context.

The safety net

Three labels give me control without making me a bottleneck:

  • no-merge — permanent veto. The merger never touches this PR again.
  • needs-human-review — pause. The merger stops trying until I remove the label.
  • human-approved — override. Bypasses size and type rules (not security-sensitive paths). I use this when I've already reviewed a PR and want it merged on the next pass.

Everything is logged to JSONL files on disk: every merge attempt, every blocked decision, every escalation. I can grep escalated blocked.jsonl to see only what needs my attention. The full history is there if I want to audit why a PR was merged.

Open source

The operator loop and the advisory buddy are open source — any AutoMaintainer user can run them. The drain/recycle/handoff pattern is harness-agnostic (Devin, Claude Code, OpenCode, Pi, Tau, Cursor).

The merger itself is maintainer-private. The concept and ratchet strategy are documented in the repo, but the actual safety gate, prompt, and deploy scripts are not shipped. Auto-merging without understanding the risks is how you ship a broken production. If you want to build your own, start with the ratchet concept, write a safety gate tailored to your repos, and test on a non-production repo first.

The point

The goal was never to remove humans from the loop. The goal was to remove humans from the boring part of the loop — the 80% of PRs that are small, safe, and verified — so that when I do review, I'm reviewing the 20% that actually needs my judgment.

Last night I slept. My fleet shipped 7 PRs. I woke up to a clean queue and a deployed product. That's the point.

Try AutoMaintainer — the fleet that ships while you sleep.

Enjoyed this post?

Follow for more on agent-first engineering, self-hosted systems, and building for autonomy.

Follow @javimosch