How I built an AI code reviewer that knows when to shut up A developer built an AI code review tool that enforces conciseness in application code rather than through prompts, using a GitHub webhook, diff fetching, Claude, structured JSON findings, and a severity-based ranker that caps output with a slice() after sorting. Findings are validated against a four-value severity whitelist (BUG, WARN, NIT, PRAISE), stable-sorted, and capped at MAX_COMMENTS, with the suppressed count disclosed in the review body so a quiet reviewer isn't mistaken for one that missed issues. The developer notes the cap can work against PRs with more than eight genuine bugs and that unanchored inline comments fall back to a single top-level comment. Every AI code reviewer I tried had the same problem: it wouldn't stop talking. Rename this. Add a comment here. Consider extracting that. By the third file you've stopped reading, and a tool you've stopped reading is worse than no tool — it's a tool that will hide a real bug from you inside a wall of suggestions. So I built one with the opposite rule: say nothing unless you found something worth saying. That sounds like a prompt engineering problem. It isn't. The model will happily agree to be concise and then hand you fourteen findings anyway. Every constraint that actually held up in production is a constraint I enforce in application code, after the model has already spoken. Here's what that looks like, including three bugs I only found by pointing the thing at real pull requests and a real credit card. A reviewer you've muted is worse than no reviewer, because now there's a wall of suggestions for a real bug to hide behind. This matters most for solo developers and small teams — the people who don't already have an enterprise code-review bundle sitting on top of their existing tools. If the review output is noisy, they turn it off in week one and never come back. GitHub webhook → diff fetch → Claude → structured findings → ranker → inline comments Every stage after "Claude" exists to decide what not to show you. The model returns JSON findings, each with { path, line, severity, message } , where severity is one of four values: BUG | WARN | NIT | PRAISE . The severity string coming back from the model is validated against that whitelist — an invalid value gets the finding dropped rather than trusted. Then everything is stable-sorted by severity: js const severityRank: Record