pr-bounce
Live PR rejection-rate leaderboard for AI coding agents on public GitHub. Claude Code, GitHub Copilot, Cursor, OpenAI Codex, Devin, Aider, Gemini CLI, Cline, ShioriCode — when their pull requests don't merge, why don't they?
A public dashboard that ranks each major AI coding agent by what fraction of its public-GitHub pull requests actually get merged — and, for the ones that didn't, clusters the maintainer's closing comment into a structured rejection-reason taxonomy using Claude Haiku 4.5.
Live: https://holyai.me/pr-bounce/
Stack: Node.js 20 · Express · better-sqlite3 (WAL) · node-cron · helmet · compression
Port: 4816 · BASE_PATH: /pr-bounce · Auth: none — every endpoint is public.
Why now
Agentic coding has eaten the world. Claude Code's adoption arc, Copilot's full migration to agent mode in 2026, Cursor's PR-authoring agents, OpenAI Codex's codex/... branch workflow, Devin's GitHub bot, Aider, Gemini CLI, Cline, ShioriCode — they all push PRs into public repos in volumes that were impossible 18 months ago. GitHub finally added Copilot PR merge metrics to the usage-metrics API in April 2026, and individual vendors publish their own acceptance rates. But there is no vendor-neutral, side-by-side public dashboard answering the obvious question:
Of the PRs an AI agent opens, what percentage actually get merged — and when they don't, why?
pr-bounce is that dashboard. It is the rejection-side complement to agent-bloat (PR size, merged-only) — and it adds an LLM-classified reason taxonomy on top.
What it shows
- Leaderboard — per agent, in the last
7d / 30d / 90d / all: PRs detected, merged %, bounced %, open %, top three rejection reasons. - Bounce wall — most recent 50 closed-unmerged AI PRs, with the snippet of the maintainer's closing comment and the classifier's reason badge.
- Reason taxonomy chart — distribution of rejection reasons across the window (
hallucinated,out-of-scope,over-engineered,missing-tests,style-conflict,duplicate-or-superseded,wontfix-or-not-planned,other). - Top repos — repos that receive the most AI PRs and their per-repo merge rate.
- Agent drill-down — per-agent KPI strip, daily trend sparkline, recent PRs.
- SVG badge —
GET /pr-bounce/api/badge?agent=<slug>returns a shields-style SVG with the agent's current merge percentage. Drop into a README.
Where the data comes from (no mocks)
Every numeric value is derived at runtime from public endpoints.
| Endpoint | Used for | Cadence |
|---|---|---|
| GET api.github.com/search/issues?q=is:pr "<signature>" | Discover AI-attributed PRs regardless of state | every 30 minutes, one agent per slot |
| GET api.github.com/repos/{owner}/{repo}/pulls/{number} | Authoritative state, additions, deletions, files changed | on first sight & after open-stale poll |
| GET api.github.com/repos/{owner}/{repo}/issues/{number}/comments | Last maintainer comment near close, for the reason classifier | once per newly-closed-unmerged PR |
| GET api.github.com/rate_limit | Rate-limit header surfaced on /health | once per refresh |
| POST api.anthropic.com/v1/messages (Claude Haiku 4.5) | Classify closing comment into one of 7 reason classes | hourly batch, only if ANTHROPIC_API_KEY is set |
If a refresh discovers zero PRs for an agent, that row is rendered empty and fetch_log records status: "empty". The app does not invent numbers. Without ANTHROPIC_API_KEY (or OPENROUTER_API_KEY fallback) the classifier is silently disabled — PRs are still indexed; the reason badge just doesn't appear.
Detection signatures
Detection is a case-insensitive substring match on the PR body + commit trailers. Priority order: claude-code > copilot > cursor > codex > devin > aider > gemini-cli > cline > shioricode. Signatures live in lib/agents.js.
| slug | signal |
|---|---|
| claude-code | Co-Authored-By: Claude <[email protected]> / Generated with [Claude Code] |
| copilot | Co-authored-by: Copilot <[email protected]> / co-authored-by: copilot-swe-agent |
| cursor | Co-authored-by: Cursor / https://cursor.com/agents |
| codex | Co-authored-by: openai-codex / Generated with OpenAI Codex |
| devin | devin-ai-integration[bot] |
| aider | Co-authored-by: aider / aider.chat |
| gemini-cli | Co-authored-by: gemini-cli / gemini-code-assist[bot] |
| cline | Co-authored-by: cline / Generated with [Cline] |
| shioricode | Co-authored-by: shioricode |
Reason taxonomy (7 classes + other)
A fixed prompt at lib/classifier.js asks Claude Haiku 4.5 for one of:
hallucinated— references nonexistent APIs, methods, files, or dependencies.out-of-scope— solves the wrong problem.over-engineered— unnecessary abstraction or refactor.missing-tests— no tests, broken tests, or coverage gaps.style-conflict— lint, format, naming, or convention violations.duplicate-or-superseded— duplicate of another PR / superseded.wontfix-or-not-planned— maintainer rejects the premise.
Anything that does not clearly fit one of the seven → other, with Haiku's verbatim reasoning kept in reason_notes.
API surface (all public, no auth)
GET /pr-bounce/health
GET /pr-bounce/api/agents
GET /pr-bounce/api/leaderboard?window=7d|30d|90d|all
GET /pr-bounce/api/recent?agent=<slug>&state=any|merged|closed|open&limit=50
GET /pr-bounce/api/bounces?limit=50
GET /pr-bounce/api/agent/:slug?window=...
GET /pr-bounce/api/repo/:owner/:repo
GET /pr-bounce/api/by-repo?window=...&limit=20
GET /pr-bounce/api/reasons?window=...
GET /pr-bounce/api/trend?agent=<slug>&days=30
GET /pr-bounce/api/badge?agent=<slug>
GET /pr-bounce/api/fetch-log?limit=30
GET /pr-bounce/api/rate-limit
POST /pr-bounce/api/refresh
JSON unless explicitly SVG. CORS is open. /pr-bounce/health is auth-free and returns 200 as long as the process is up.
Cron schedule
| schedule | task |
|---|---|
| /30 | discover one agent's PRs (rotates through 9 agents) |
| 7 | re-poll open PRs whose state we haven't refreshed in 4h+ |
| 21 | fetch closing comments for new closed-unmerged PRs |
| 41 * | classify a batch of pending comments with Haiku |
| +5s after boot | one discover tick so a fresh container has data within a minute |
Embeddable badge

Available slugs: claude-code, copilot, cursor, codex, devin, aider, gemini-cli, cline, shioricode.
Colour bands: ≥60% merge = green, 40–59% = amber, <40% = red, no data = grey.
Run locally
cp .env.example .env
npm install
npm start
# open http://localhost:4816/pr-bounce/
Health: curl http://localhost:4816/pr-bounce/health
Stack
- Node.js 20+, Express 4
better-sqlite3in WAL modenode-cronfor the four cron jobshelmet+compression- Vanilla JS SPA — no framework, no CDN, no Tailwind, all SVG inline
License
Internal / proprietary — part of the Holy AI product gallery.