AI coding

Designing Reviewer Abstention: A Band Between Pass and Fail

Antonio Evans10 min read

A horizontal number line from 0 to 1 labeled 'reviewer score'.
Key points

Key points

  1. 1A reviewer with two thresholds (confident fail below, confident pass above) can decline to decide instead of guessing on borderline items.
  2. 2Scores in the abstention band must be logged with the score, band, model identifier, item identifier, and timestamp so you can audit and tune later.
  3. 3Route abstentions to a second reviewer with a different model or prompt first, and reserve the human queue for disagreement and breaching categories.
  4. 4Set a per-category abstention limit as a circuit breaker so that a drifting reviewer cannot quietly dump its whole workload on people.

Why a single threshold forces bad guesses

Every automated reviewer eventually meets an item it cannot call. With one threshold, that item still gets a verdict: the score lands a hair above or below the line and the reviewer commits to pass or fail. That is a guess dressed up as a decision, and it fails in both directions. A pass lets a defect through; a fail burns a human's time on a false alarm.

The cost of those wrong guesses is now measurable. A study of 31,073 CodeRabbit review-feedback pairs across 239 GitHub repositories found that developers rejected 56.3% of agent-generated reviews, mainly because of false alarms, redundant or out-of-scope suggestions, and misalignment with developer intent, as reported in CRJudgeBench. Meanwhile, the review agents benchmarked in c-CRAB collectively solved only around 40% of human-authored review tasks. A reviewer that confidently answers everything is overclaiming on roughly half its output.

Selective classification gives the formal name to the fix: a model may abstain on uncertain inputs in order to raise the accuracy of the predictions it does make, as described in Calibrated Selective Classification. The same paper makes a sharper point that matters for design. A model can be accurate on average and still attach high confidence to wrong answers, so abstention has to be built on confidence estimates that actually track correctness. That is a calibration question, not a threshold question, and it is why one number is not enough.

The human side has a hard limit too. Defect detection in code review drops to near zero past 400 lines reviewed at once, according to the SmartBear and Cisco study summarized in this verification guide. Under heavy AI adoption, median PR review time rose 5 times and incidents per pull request tripled, per Faros AI's data cited in the same guide. You cannot fix a reviewer's uncertainty by throwing every borderline item at a human, because the humans are already saturated.

The two-threshold design

Replace the single cutoff with a band. Pick a lower threshold L and an upper threshold H on the reviewer's score, where higher means more confident the item passes.

Score rangeDecisionWhat happens next
score < LFailConfident rejection, no second look needed
L ≤ score ≤ HAbstainReviewer declines to decide, item is routed
score > HPassConfident acceptance, item moves on

The band is the whole design. Its width is a product decision with two consequences: a wider band raises the abstention rate and costs you routing capacity, while a narrower band pushes more items into confident verdicts that are more likely to be wrong. Start wider than you think you need and tighten as you gather labeled outcomes. The human-in-the-loop design patterns guide makes the same recommendation for confidence thresholds generally: set them conservatively at first, err toward routing more cases to review, then tighten as you accumulate labeled corrections and can measure the actual error rate on borderline items.

A minimal implementation looks like this:

LOW, HIGH = 0.35, 0.65

def decide(score, item):
    if score < LOW:
        return "fail"
    if score > HIGH:
        return "pass"
    return "abstain"

That is not the interesting part. The interesting part is that L and H should not come from a symmetric guess. Derive them from consequences. If a false pass costs ten times more than a false fail, push H up until almost everything below it abstains or fails. Classifier calibration work explains why a single ROC-optimized operating point is the wrong tool here: it assumes one cutoff separates the classes optimally, and borderline cases are precisely the ones that assumption breaks on.

The score itself must be a probability you can trust, not a raw logit or an arbitrary rubric total. Scikit-learn's calibration guide describes reliability diagrams, which plot the fraction of positives against predicted probability per bin; if items scored at 0.6 actually pass 45% of the time, your band is misplaced. For LLM judges, calibration matters even more because scores are sensitive to prompt phrasing, label order, response length, and position, as the LLM-as-a-judge survey documents. An uncalibrated judge's 0.5 is a number, not a probability.

Designing the score that feeds the band

Abstention only helps if the score is honest about uncertainty. Three practical choices matter.

First, prefer direct scoring with explicit criteria over a single holistic number. Evidently's guide separates pairwise comparison from direct scoring by criteria, and notes that judges can be built for both offline and online evaluation. Direct scoring against a rubric gives you a number you can compare across items and tune; pairwise comparison gives you a ranking with no natural band.

Second, ask for the uncertainty explicitly. A judge prompt that returns only "pass" or "fail" cannot abstain coherently. Ask for a score and a one-line reason, then derive the band from the score. Arize's guide recommends starting from the failure you need to catch, using code for deterministic checks and an LLM judge only where interpreting meaning is required, and validating the judge against reviewed examples before relying on its scores. That validation step is where L and H come from.

Third, watch for the biases that flatten discrimination inside the band. Judges favor their own generations and drift with prompt casing, per Patronus's LLM-as-a-judge tutorial, which also lays out the components a judge needs: input contextualization, comparison to standards, multi-step reasoning, explanation generation, and score synthesis. A judge missing any of those produces scores that cluster mid-range, which widens your effective band without you choosing to.

A judge that cannot tell its own output from a correct answer is a special case of this. A repository-grounded judge trained to verify review comments reached 76.60% accuracy on a 359-instance test set, beating a strong general model by 6.13 points, according to CRJudgeBench. Even a purpose-built judge misjudges roughly one item in four. Design the band assuming that error rate, not zero.

The abstention log schema

An abstention is a data point, and if you do not record it you cannot tune the band, argue about it, or prove what happened. Log one row per decision, with abstentions flagged.

{
  "item_id": "pr-1842",
  "category": "auth-change",
  "score": 0.52,
  "band": [0.35, 0.65],
  "decision": "abstain",
  "reviewer_id": "reviewer-a",
  "model_id": "judge-model-v3",
  "prompt_hash": "9c1f...",
  "rubric_version": "auth-rubric-7",
  "reviewer_round": 1,
  "decided_at": "2026-05-14T09:12:44Z",
  "routed_to": "reviewer-b",
  "final_outcome": "pending"
}

The fields that earn their place:

FieldWhy it is worth storing
score and bandLets you see whether abstentions cluster at one edge, which signals a shifted L or H
model_id and prompt_hashTies a decision to the exact reviewer that made it, so a prompt change does not silently invalidate history
rubric_versionRubrics change; a score is meaningless without the criteria it was measured against
reviewer_roundDistinguishes the first reviewer from the second reviewer's verdict on the same item
categoryThe unit for the per-category abstention limit described below
final_outcomeCloses the loop: did the second reviewer and human agree with each other, and with the item's eventual fate

Storing the band alongside the score, rather than only the thresholds, means historical rows stay interpretable after you retune. If you only store the score, a later reader cannot tell whether 0.5 was inside the band when the decision was made.

This is the same discipline the verification literature asks of AI reviewers generally. The verification guide frames review as a mechanical pipeline with the compiler, diff, and test suite acting as referee, and it treats auditability as part of the pipeline rather than an afterthought. An abstention you cannot reconstruct is not an audit trail.

Routing abstentions without flooding people

The default route for an abstention should not be a human. Send it to a second reviewer first. The reason is the maker-checker principle: the reviewer that produced the original score shares its blind spots with any retry that uses the same model and same prompt. The adversarial code review guide documents the evidence that self-review fails, because LLMs measurably favor their own output, and it lays out a control model where a distinct checker with different context, instructions, and often a different model reviews the change.

So the second reviewer should differ on at least one axis:

  • Different model. A different model family can repeat fewer of the first reviewer's errors. Use a gateway that reaches several model families through one key so the second reviewer is a config change rather than a new integration.
  • Different prompt. A skeptical prompt that asks the reviewer to hunt for reasons the first verdict is wrong, rather than to re-score.
  • Different context. A fresh context window without the first reviewer's reasoning, so it cannot anchor on it.

If the second reviewer returns a confident verdict (below L or above H for its own scale), accept it and record the round. If the second reviewer also abstains, or if the two reviewers reach opposite confident verdicts, the item goes to a human queue. The human-in-the-loop patterns guide distinguishes blocking from non-blocking review: a blocking pattern halts until a human responds and suits irreversible actions, while a non-blocking pattern queues low-confidence cases and continues processing. Route to the queue asynchronously unless the item is a privileged or irreversible change.

Human attention is not uniform either. Not all humans-in-the-loop are equal maps the human step on expertise and effort, from low-effort verification ("did it work?") to high-effort wisdom ("what should we do?"). Abstentions are usually verification work: a quick check against intent. Send them to someone who can confirm quickly, and reserve the expensive discernment work for disagreements between reviewers.

When abstentions become a human queue

The rule you need is not "route abstentions to people." It is "route abstentions to people when a signal says the automated path is failing." Two signals do the work.

Per-category abstention rate. Track the share of items in each category that abstain, over a rolling window. Set a limit, say 20% for a category. When the rate crosses it, every new abstention in that category goes straight to the human queue and pages the owner. This is a circuit breaker against two failure modes: a model upgrade that shifted the score distribution, and a category the reviewer was never good at. Without it, a drifting reviewer quietly converts its whole workload into human work and nobody notices until the queue is days deep.

Reviewer disagreement. When two reviewers reach opposite confident verdicts, the automated path has failed on that item by definition. Send it to a human and tag it as a disagreement so you can count them separately from ordinary abstentions. A rising disagreement rate between two specific reviewer versions is the clearest early warning that one of them needs attention.

TriggerRoute toNotes
First reviewer abstains, second reviewer confidentAccept second verdictRecord the round and both scores
Both reviewers abstainHuman queueNothing automated can decide
Reviewers reach opposite confident verdictsHuman queue, tagged disagreementTrack disagreement rate separately
Category abstention rate above the limitHuman queue, bypassing the second reviewerPage the category owner
Item touches a privileged or irreversible actionHuman queue, blockingDo not merge or execute without sign-off

A worked example makes the arithmetic concrete. Suppose a reviewer handles 1,000 items a day across four categories, and a first pass lands 60% confident pass, 25% confident fail, and 15% abstain. That is 150 abstentions daily going to the second reviewer. If the second reviewer resolves 80% of them confidently, 120 items resolve automatically and 30 reach humans. Now suppose a model upgrade shifts the distribution so abstentions jump to 30% of a category. The per-category limit fires, that category's abstentions go straight to people, and the team learns about the shift on day one rather than after a week of queue growth. This kind of routing and logging is ordinary engineering, but it has to be built deliberately; the secure code review agent guide makes the broader point that an automated reviewer with access to source and CI is a privileged component and should be architected as one.

Calibrating and evolving the band over time

A band is not a setting you choose once. It is a parameter you estimate and then re-estimate as outcomes accumulate.

The process is simple. Take the abstentions your humans resolved and treat their verdicts as labels. Plot a reliability diagram: bin the items by the reviewer's score and compare the fraction of items the humans passed in each bin against the score. If items scored 0.4 pass 50% of the time, your lower threshold is too high. If items scored 0.7 pass 95% of the time, your upper threshold is too low. Move L and H to sit where the calibration curve crosses your acceptable error bars for each side.

Two rules keep this honest. First, never tune the band on the same items you used to set it, or you will overfit to a week of unusual traffic. Second, version the band the same way you version prompts, and log the version with each decision, so a score recorded under one band is not compared carelessly to one recorded under another. The calibration guide from scikit-learn is a good reference for the mechanics of reliability diagrams and for the calibration methods you can apply when a judge's scores are not probabilities at all.

There is a deeper design question underneath the tuning: what the band is for. The rubric research on LLM judges found that models generate interpretable, task-aware evaluation dimensions and apply them consistently within a model, but scoring reliability degrades in factual and knowledge-intensive settings, and it is fragmented across models. That suggests the band should be wider for knowledge-intensive categories, where a judge's score is less trustworthy, and narrower where the judge is well grounded by tests or compilers. A single global band across all categories ignores the evidence.

Finally, do not let the band absorb a broken reviewer. If abstentions rise because scores clustered, and the calibration curve shows the reviewer no longer discriminates, widening the band hides the problem. The band is a decision rule, not a repair.

FAQ

FAQ

How do I choose the initial lower and upper thresholds?

Start from consequences rather than symmetry: if a false pass costs more than a false fail, set the upper threshold high so more items abstain or fail. Then calibrate against labeled examples, and follow the common guidance to start conservative and tighten as you gather corrections. The human-in-the-loop guide recommends erring toward routing more cases to review at first.

Why route abstentions to a second reviewer before a human?

Because a human queue is your scarcest resource, and a second reviewer with a different model or prompt resolves most borderline cases cheaply. The maker-checker principle says the original reviewer shares its blind spots with any retry that reuses the same model and prompt, so change at least one. The adversarial code review guide covers why self-review is the weakest option.

What exactly should the abstention log contain?

At minimum the score, the band, the model identifier, the item identifier, and a timestamp. Add the prompt hash, rubric version, reviewer round, category, and final outcome so you can tie a decision to the reviewer that made it and close the loop later. Without the band stored alongside the score, old rows become uninterpretable after a retune.

When should abstentions go straight to a human queue?

When a category's abstention rate crosses a limit you set, when two reviewers reach opposite confident verdicts, or when the item touches a privileged or irreversible action. Those three cases mean the automated path has failed on that item or on that category. Routing only the failures keeps the queue small and meaningful.

Does adding abstention actually improve reviewer accuracy?

It improves the accuracy of the verdicts the reviewer does make, which is the point of selective classification, as described in Calibrated Selective Classification. It only works if the confidence score is calibrated and the routing is real. Abstenion without a working second reviewer and human queue just moves the guess.

If you are building the routing layer, one API key for text, image, and embedding models makes it easy to run a second reviewer on a different model family; Quanta Inference is built for exactly that swap.

Sources

Sources

  1. CRJudgeBench: Can AI Detect Plausible but Invalid Code Reviews?
  2. Code Review Agent Benchmark
  3. Calibrated Selective Classification
  4. Reviewing AI-Generated Code: A Verification Discipline for ...
  5. Human in the Loop AI: Design Patterns and Governance
  6. Classifier Calibration and the End of ROC-Based Threshold ...
  7. 1.16. Probability calibration
  8. A survey on LLM-as-a-judge
  9. LLM-as-a-judge: a complete guide to using LLMs for ...
  10. LLM-as-a-Judge: Guide & Pre-built Evaluators
  11. LLM As a Judge: Tutorial and Best Practices
  12. Adversarial Code Review: Why the Maker Shouldn't Grade the Checker
  13. Not all humans-in-the-loop are equal
  14. Building a Secure AI-Powered Code Review Agent: Guide
  15. LLMs Designing and Applying Evaluation Rubrics
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles