Auto-merge pull requests, behind locks
Rusuban merges a pull request on its own only when two things agree. Your rules say this kind of PR may merge without a person, and eight gates pass on the exact commit it checked. Agents never merge: the server does, or nothing does.
At a glance
| Who decides a PR may merge alone | A Claude Code judge, from the rules in your repo |
| Who merges | Rusuban's server, never an agent |
| What it checks | CI, reviews, review threads, mergeability, the judgment, your locks, a recorded proof, the merge switch |
| On which commit | The head it checked, with gh pr merge --match-head-commit |
| How fast | At most one merge per read of the project, every 90 seconds by default |
| Your override | A card's merge option merges that head once CI, threads, conflicts and the switch allow |
| The off switch | One switch in the dashboard's header pauses every merge |
How a PR comes to merge
- Your rules say which PRs may merge alone. For example: "A bug fix with a test merges alone. A new dependency comes to me."
- A judge reads the PR against them and decides, for that head, whether it may merge without a person, and why.
- The server computes eight gates from live GitHub state, at every read.
- When all eight pass and merges are armed, it merges that head and journals the merge with its gates. A new commit sends the PR back to the judge.
A PR that does not merge shows, on its node in the dashboard, the first gate that stops it.
The eight gates
| Gate | Passes when |
|---|---|
| CI | At least one check on the head, each one green, neutral or skipped, none pending, failed or waiting for an approval |
| Reviews | No latest review of the head requests changes, and every required reviewer reviewed it |
| Threads | No review thread is open |
| Mergeable | Not a draft, no conflict, and rebaseable when the repo merges by rebase |
| Judgment | The repo has rules, and the PR was judged on this head to merge alone |
| Locks | It touches neither .rusuban/ nor a locked path, renames no file, and stays within your size limits |
| Evidence | A proof is recorded on its head when it needs one |
| Switch | Merges are armed |
The locks reference lists each gate's exact conditions.
Your locks
A repo adds its own locks in .rusuban/locks.json: paths a PR may not touch alone, size limits, required checks and reviewers, and zones that need a proof, such as a benchmark.
{
"lockedPaths": [".github/**", "package.json"],
"maxLines": 300,
"requiredChecks": ["test"]
}
No text lifts a lock, not even your rules. A locks.json that does not validate stops every merge of its repo until it is fixed, so a typo never leaves a lock silently missing.
Locks that always hold
- CI green on the exact head, with at least one check.
- No open review thread.
- The head unchanged since the judgment.
- The rules never change without a person. A PR that touches
.rusuban/never merges alone. - No direct push to a default branch. It is denied to agents, and a commit that reaches the branch outside a PR is flagged.
Compared with GitHub's auto-merge
GitHub's auto-merge, once someone turns it on for a pull request, merges it when the branch's required reviews and status checks pass. Rusuban decides from your rules which PRs may merge at all, then checks its own gates whatever the branch requires: no open thread, the head it judged, your path and size locks. A PR merged another way, by a person or by GitHub's auto-merge, is flagged as a merge Rusuban did not make.
Get started
- Install Rusuban and keep merges paused at first.
- Write in your rules which PRs may merge alone, and put in
locks.jsonwhat the server must check itself. - Read the judgments for a while, then arm the switch.