← All posts

A coding agent opened 306 PRs on our repos since April. 289 merged.

· Sinatra

Nineteen weeks of real data from running a background coding agent on two production codebases: the merge rate, what got closed without merging, and why the human gate is doing most of the work.

The question we get asked most about agent-written code is some version of "sure, but does any of it actually ship?" It deserves a real answer with real numbers, and we have some: every pull request our agent has opened on our own two codebases since its first one on April 27, 2026, and what happened to each one.

The short version: 306 opened, 289 merged, 14 closed without merging, 3 still open as of September 9. That is 94 percent of everything it ever opened landing in main.

Full disclosure, and it matters for how you read this: we build Sinatra, the agent that opened these PRs, and both repos are ours. This is a best-case dataset, and the rest of the post is mostly about why. Both repos are private, so you are taking our word for the counts; the last section says exactly how we pulled them so you can run the same thing on your own repos.

The dataset

Two codebases, both real production software, not demo repos:

RepoWhat it isOpenedMergedClosed unmergedStill open
sinatra (product monorepo)The product itself: API, Temporal worker, webapp, this website250237103
hellomarshyA pet sitting marketplace, a founder side business565240

The window is April 27 to September 9, 2026, so call it nineteen weeks, the first six of them on a development install before the production app went live in June. In that time the agent opened more PRs on the product monorepo than one of its two human founders has all year.

Yes, the agent builds its own product. About 250 of these PRs are changes to its own codebase, which we find funny and also genuinely useful, because it means the people reviewing agent PRs all day are the same people deciding how the agent should work.

What the 14 unmerged ones were

A closed-without-merge PR is the interesting failure case, so we went through them. They fall into three buckets:

  • Rejected in review. The change worked but we didn't want it built that way. One example: a hardening change to our egress proxy, closed with a note that a different approach at the DNS layer made it unnecessary.
  • Superseded. Two takes on the same internal blog-post task: the first got replaced by a redo within the hour (the original ticket had a typo in its title, which is its own lesson about tickets), and the survivor was eventually dropped too.
  • Stale. The underlying ticket stopped mattering before anyone had review time, usually because the surrounding code moved.

Notably absent from the list: PRs closed because the code was broken garbage. That is not because the agent never produces bad diffs. It is because bad diffs mostly get caught earlier, in the sandbox test run or in the agent's own self-review, and come back as a revision rather than reaching a human as a doomed PR.

Why the rate is high, honestly

The tempting headline is "the model is good." The real answer is that a merge rate measures the whole loop around the model, and most of the loop is unglamorous gating:

A human reviews every PR, and the agent cannot merge. There is no auto-merge path. 289 merges means a human read 289 diffs and pressed the button. The merge rate is high partly because the bar is real; if the agent could merge its own work, the number would be meaningless.

The tickets are written for an agent. We write acceptance criteria, reproduction steps, and file pointers, because vague tickets produce vague PRs. Most of the merge rate is determined before the agent starts, in the ticket. We wrote up how we write tickets for AI agents separately.

Tests run in the sandbox, and failures come back as revisions. The agent opens a draft PR while it works, and the configured test command has to pass before the PR is marked ready for review. A failing run does not vanish; it shows up as more work on the same branch instead of as a broken PR in your queue.

The agent reviews its own diff first. Before a human looks, it posts its own findings on the PR. Reviewers start from a list instead of from zero, which is a big part of why reviewing 20 agent PRs a week is tolerable at all. There is a longer post on reviewing AI-generated PRs.

What your numbers would look like

Worse than this, at first, and that is not a dodge. This dataset has every advantage: the people writing the tickets built the agent, know exactly what it handles well, and route the wrong kind of work away from it before it starts. You will spend your first week or two finding that boundary for your own codebase.

The honest expectations: well-specified bug fixes and contained features merge at something like the rates above. Vague tickets, large refactors, and anything requiring taste about your architecture will need revision rounds or a human takeover. And the usual failure is not broken code. It is a clean PR that solves the wrong problem, because the ticket underspecified the right one.

The other number nobody publishes: review time. A 94 percent merge rate on PRs nobody has time to review is worth nothing. Ticket size is the lever we found for this, because a tight issue comes back as a diff one person can read in minutes.

How we counted

Both repos are private, so here is the method instead of a link. The agent opens PRs as a GitHub App, so every PR it has ever opened is authored by an App identity: sinatraai in production, plus a second App that one of us runs for local development against the same monorepo. We listed every PR by those two authors with the GitHub CLI and bucketed them by state. Nothing was excluded, and the closed-without-merge bucket is the full list, not a curated one.

gh pr list --repo <owner>/<repo> --search "author:app/sinatraai" \
  --state all --limit 1000 --json state \
  --jq 'group_by(.state) | map({(.[0].state): length}) | add'

If you run Sinatra on your own repos, that same command gives you your own number, which is the one that actually matters. The counts here were pulled September 9, 2026; the open ones will have resolved by the time you read this, and knowing this repo, a few dozen more will exist.

If you want to try the loop on your own backlog: assign a Linear issue to the agent, or add the Sinatra label to a GitHub issue, and it comes back with a PR. The free tier is 5 tasks a day on your own key or a Claude or ChatGPT subscription, every day, no card.