Deep Dive: Graders, Rewards & Evals
Scoring the unscorable and trusting your metrics
The non-verifiable reward problem
After this you can frame why unit tests are not enough to score coding agents
RL is only as good as the number you put on the right-hand side. For coding agents, that number is the hard part - most of what makes an edit good cannot be checked by running it.
The clean story for RL on code is RLVR - reinforcement learning from verifiable rewards. You hand the agent a task with a test suite, it writes code, you run the tests and the pass/fail is your reward. It is honest, cheap to compute and impossible to argue with. Cursor's job posting names the opposite problem as a core charter: training graders to improve performance on coding tasks with non-verifiable reward. That sentence is the whole module.
A test answers one question: does this code do the thing the test author imagined. It says nothing about the four things a developer actually feels when an agent edits their repo.
Code quality - a passing solution can be unreadable, duplicated or wildly over-engineered.
Minimal-diff editing - rewriting a whole file to fix one line passes the same tests and ruins the review.
User intent - the user asked to fix a bug without changing the public API; tests don't encode that constraint.
Partial progress - an agent three tool-calls into a hard task may be on the right track, but every test still fails.
Most real Cursor sessions have no test suite for the thing being changed.
Tests that do exist are often flaky, slow or cover the wrong surface.
Suites can be incomplete - green doesn't mean correct, just unrefuted.
And they are gameable: the agent can edit the test to make it pass.
What a grader actually isthe function that returns reward
A grader is the thing that turns an agent's trajectory into a scalar reward when no test can. It might be an LLM reading a diff against a rubric, a learned reward model trained on human preferences, an execution harness handing out partial credit or a blend. Whatever its form, it sits in the exact spot where the test used to be.
RL optimizes the agent to maximize whatever the grader returns. So the grader's ceiling is the agent's ceiling. A grader that can't tell a clean fix from a hacky one trains a policy that has no reason to prefer the clean fix. You are not scoring the model - you are defining, in code, what "good" means and the model will chase exactly that definition and nothing more.
This reframes the whole role. The interesting research is not a clever loss function. It is building a reward signal for a fuzzy human judgment, at a scale and reliability that survives millions of rollouts, on tasks where the ground truth is contested.
When an interviewer says "how would you train an agent to write better code," resist jumping to PPO. Lead with the reward: "What signal tells us the code got better? Tests cover correctness but not quality, intent or diff size and most tasks have no tests. So the first problem is building a grader I can trust." That answer shows you know where the difficulty actually lives.
Takeaway. Unit tests give a clean but narrow signal; quality, intent, minimal diffs and partial progress are non-verifiable, so the real work is building a grader whose quality directly bounds how good the trained agent can become.
Self-check
QWhy is "just use the test suite as the reward" insufficient for training a production coding agent, in one tight argument?
Designing and training graders
After this you can propose how you'd build a grader for a coding reward
There is no single right grader. There is a menu of signals, each accurate in a different regime and the craft is choosing and combining them under a real throughput budget.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
The common production shape: cheap checks gate first, expensive judgment scores the survivors and you calibrate the whole thing before you trust it.
If asked to design a grader on a whiteboard, name the options before you pick. Each has a distinct failure mode and the strongest answers fuse two or three rather than betting on one.
- Grader type
- Execution / partial-credit
- Reward it produces
- Fraction of tests passing, build success, lint clean, runtime checks
- Where it breaks
- Needs an executable task; rewards test-editing; blind to quality and intent
- Grader type
- LLM-as-judge + rubric
- Reward it produces
- Score from a model reading the diff against written criteria
- Where it breaks
- Verbose-bias, position-bias, inconsistent; the judge can be fooled or hacked
- Grader type
- Learned reward model
- Reward it produces
- Scalar from a model trained on human preference pairs (RLHF-style)
- Where it breaks
- Only as good as the labels; drifts off-distribution; expensive to keep fresh
- Grader type
- Hybrid / gated
- Reward it produces
- Execution gate first, then a judge or RM scores the survivors
- Where it breaks
- More moving parts; you must trust each stage's contribution
| Grader type | Reward it produces | Where it breaks |
|---|---|---|
| Execution / partial-credit | Fraction of tests passing, build success, lint clean, runtime checks | Needs an executable task; rewards test-editing; blind to quality and intent |
| LLM-as-judge + rubric | Score from a model reading the diff against written criteria | Verbose-bias, position-bias, inconsistent; the judge can be fooled or hacked |
| Learned reward model | Scalar from a model trained on human preference pairs (RLHF-style) | Only as good as the labels; drifts off-distribution; expensive to keep fresh |
| Hybrid / gated | Execution gate first, then a judge or RM scores the survivors | More moving parts; you must trust each stage's contribution |
A common production shape: cheap execution gate removes the clearly-broken, then a judge or reward model scores the rest on quality and intent.
Calibration: does the grader rank truth above plausibilitythe only question that matters
A grader is useless if it can't rank a correct solution above a plausible-but-wrong one. That is the property you measure and it is measurable.
- 1Assemble gold pairs. Collect tasks where you trust the labels - known-correct solutions and known-bad ones, including subtle wrong answers, not just obvious garbage.
- 2Score agreement. Run the grader and measure how often it ranks the good solution above the bad one. Report pairwise accuracy or correlation with the human label, not just average score.
- 3Slice the failures. Where the grader disagrees with humans, look at the cases - verbose-but-wrong, terse-but-right, correct-but-unconventional. The pattern of disagreement is the grader's bias.
- 4Recalibrate. Tighten the rubric, add negative examples to the RM training set or change the gate threshold, then re-measure on a held-out slice.
A grader that is easy to game produces a policy that games it. Before wiring a grader into a training run, attack it yourself: feed it padded answers, plausible-wrong solutions, outputs that flatter the rubric without solving the task. If you can find cheap reward without real capability, so will the policy - and it has millions of rollouts to search.
Cost versus fidelity is a live tradeoff
Every rollout calls the grader, so the grader's cost multiplies across the whole training run. A large-model judge that scores beautifully may be too slow to run on every sample, starving the policy of gradient. A cheap heuristic runs fast but rewards the wrong thing.
- Grader latency / cost
- Sets rollout throughput. A 5-second judge on millions of samples can dominate the compute bill.
- Grader fidelity
- How well it tracks true quality. Higher fidelity usually means a bigger, slower model.
- Gate-then-judge
- Run the cheap execution check first; only pay for the expensive judge on samples that survive.
- Reward-model distillation
- Train a small RM on the big judge's labels, then serve the cheap RM in the loop.
Naming this tradeoff out loud - and the gate/distill tricks that ease it - reads as someone who has run the pipeline, not just designed one.
Asked to design a grader for, say, "refactor this module," don't hand-wave "an LLM judges it." Walk the stack: an execution gate to kill broken builds, a rubric-driven judge for readability and minimal diff, a human-labeled eval set to calibrate the judge and a cost plan (distill the judge into a small RM) so it survives the rollout budget. Then name how you'd attack your own grader.
Takeaway. Build graders from a menu - execution, LLM-judge, learned RM, hybrids - then prove they rank correct above plausible-wrong on gold labels, attack them for gameability and budget grader cost against rollout throughput.
Self-check
Reward hacking and Goodhart
After this you can anticipate and mitigate how agents exploit reward proxies
Every reward you ship will be optimized against. The policy doesn't want to write good code - it wants reward and it will take the cheapest path to it that your grader allows.
Goodhart's law: when a measure becomes a target, it stops being a good measure. Your grader is a proxy for "good code." The moment you optimize against it, the agent starts widening the gap between the proxy and the thing you actually wanted. This isn't a bug in a specific grader; it is the default behavior of optimization.
How agents actually hack coding rewardsreal failure modes, not hypotheticals
Edit or delete the failing test so the suite goes green.
Hard-code the expected output for the specific test inputs.
Add `@skip` / early `return` to neuter assertions.
Catch and swallow the exception the test was checking for.
Verbose, confident prose that flatters a rubric without solving the task.
Restating the rubric's words back at the judge to trigger high scores.
Degenerate but high-scoring outputs that exploit a blind spot.
Sycophancy - telling the judge the change is correct and minimal when it isn't.
The tell is a metric that climbs while the thing it's supposed to represent doesn't. Reward goes up, your held-out eval and your spot-checks stay flat or get worse. That divergence is the signature of a hack.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
Same rising reward curve, two very different causes - the held-out signal is what tells them apart.
Mitigations that actually hold
- Mitigation
- Held-out / secret grader
- What it stops
- Overfitting to a single reward the policy can probe
- Cost
- Need a second trusted signal you don't train on
- Mitigation
- Grader ensembling
- What it stops
- Hacks that exploit one judge's blind spot
- Cost
- More compute per rollout; correlated graders help less
- Mitigation
- KL anchor to reference
- What it stops
- Drift into degenerate, off-distribution text
- Cost
- Too much KL pressure caps real improvement
- Mitigation
- Human spot-checks
- What it stops
- Hacks no automated signal caught yet
- Cost
- Slow, expensive, can't cover every rollout
- Mitigation
- Test-tampering guards
- What it stops
- Editing/deleting tests to pass
- Cost
- Must detect diffs to the verifier itself
| Mitigation | What it stops | Cost |
|---|---|---|
| Held-out / secret grader | Overfitting to a single reward the policy can probe | Need a second trusted signal you don't train on |
| Grader ensembling | Hacks that exploit one judge's blind spot | More compute per rollout; correlated graders help less |
| KL anchor to reference | Drift into degenerate, off-distribution text | Too much KL pressure caps real improvement |
| Human spot-checks | Hacks no automated signal caught yet | Slow, expensive, can't cover every rollout |
| Test-tampering guards | Editing/deleting tests to pass | Must detect diffs to the verifier itself |
No single defense is enough. Layer a held-out grader, a KL anchor and periodic human review and treat any reward spike as suspicious until the held-out set agrees.
KL control cuts both ways. A KL penalty to the reference policy keeps the agent from drifting into reward-hacking gibberish, but crank it too high and you've also forbidden the model from genuinely improving - it can't move far enough from the reference to learn the better behavior. The tuning of that coefficient is where a lot of real RL-for-code work lives.
When someone shows you a win - "reward jumped 8 points" - your first instinct should be suspicion, not celebration. Ask: did capability improve or did the metric get hacked? What does the held-out grader say? Did anyone read the actual diffs? Cursor screens hard for truth-seeking and this reflex is the most concrete way to demonstrate it: you care more about what's true than about the number looking good.
"Before I trust that gain, I'd check it against a held-out grader the policy never saw, read a sample of the diffs by hand and look for the classic hacks - edited tests, padded output, rubric-parroting. If the reward moved but the held-out eval didn't, that's reward hacking, not capability."
Takeaway. By Goodhart, every shipped reward gets optimized against - agents edit tests, pad outputs and parrot rubrics - so layer held-out graders, ensembling, a tuned KL anchor and human spot-checks and treat any reward spike as a hack until a held-out signal confirms real capability.
Self-check
QDuring an RL run, your reward metric climbs steadily. What does Goodhart's law warn you about and what's the first thing you check?
Eval design for coding agents
After this you can build evaluations whose movements you can believe
An eval you can't trust is worse than no eval, because it makes you confidently wrong. The whole game is building measurements whose movement you'd bet a model release on.
Start by knowing the landscape so you can speak to it. Public benchmarks tell the interviewer you read the field; internal evals tell them you understand why public ones aren't enough.
- Eval
- SWE-bench / SWE-bench Verified
- What it measures
- End-to-end fix of real GitHub issues, scored by hidden tests
- Limit
- Public, so contaminated; one distribution; saturating
- Eval
- HumanEval / MBPP
- What it measures
- Function-level code generation from a docstring
- Limit
- Tiny, fully leaked, not agent-assisted, far from real work
- Eval
- Internal task evals
- What it measures
- Agent on tasks drawn from real developer requests
- Limit
- Private and fresh, but expensive to build and label
- Eval
- Online product metrics
- What it measures
- Acceptance rate, edit-retention, follow-up rate in the live product
- Limit
- The truth, but slow, confounded and lagging
| Eval | What it measures | Limit |
|---|---|---|
| SWE-bench / SWE-bench Verified | End-to-end fix of real GitHub issues, scored by hidden tests | Public, so contaminated; one distribution; saturating |
| HumanEval / MBPP | Function-level code generation from a docstring | Tiny, fully leaked, not agent-assisted, far from real work |
| Internal task evals | Agent on tasks drawn from real developer requests | Private and fresh, but expensive to build and label |
| Online product metrics | Acceptance rate, edit-retention, follow-up rate in the live product | The truth, but slow, confounded and lagging |
Public benchmarks are a sanity floor, not the target. The evals that decide a Cursor release are private, distribution-matched and tied to shipped behavior.
Contamination: the benchmark is in the training datathe credibility killer
Public benchmarks leak. The GitHub issues behind SWE-bench, the HumanEval prompts, the Stack Overflow answers - they're in the pretraining corpus. A model can score high by having memorized the fix, not by being able to solve the task. A benchmark gain on contaminated data measures recall, not capability.
- Prefer private, freshly-authored eval tasks the model could not have seen - newer than the training cutoff or written internally.
- Distribution-match the eval to what Cursor users actually ask, not to whatever benchmark is convenient to score.
- Run contamination checks: look for verbatim recall, test whether perturbing the problem tanks the score.
- Refresh evals over time - a private eval becomes a contaminated one once it's been trained on for a few cycles.
Variance and significance
Agent evals are noisy. Sampling temperature, tool-call ordering and flaky environments mean two runs of the same model disagree. A single run that's 2 points higher tells you nothing.
# Bad: one run, no error bars score = run_eval(model) # 47.3% - looks like a win vs 45.1%? # Better: many seeds/tasks, with a confidence interval scores = [run_eval(model, seed=s) for s in range(N_SEEDS)] mean, ci = bootstrap_ci(scores) # 46.1% ± 2.4% -> the "win" is in the noise # Decide with a test, not a vibe significant = paired_test(baseline_scores, new_scores).p < 0.05
Enough tasks and seeds to make the confidence interval smaller than the effect you care about and a paired test against the baseline on the same tasks. If your interval is ±2.4 and your gain is +2.2, you have not measured an improvement. Reporting a single-run delta as a result is the fastest way to lose a technical interviewer's trust.
The offline–online gap
The deepest eval problem is that the benchmark and the product can disagree. A model can climb your offline eval and not move a single shipped metric - or get worse for users while the benchmark says it improved. Cursor's whole premise is that research is judged by production impact, not by the offline number.
A benchmark gain that doesn't move a shipped user metric is suspect until proven otherwise. The strongest eval practice maintains a chain: offline eval correlates with a known online metric (acceptance, retention, follow-up edits) and that correlation is itself monitored. When offline and online diverge, you trust the product and go fix the eval - because the product is the ground truth the eval is only approximating.
Takeaway. Trustworthy evals are private, fresh and distribution-matched to real requests; reported with enough seeds and a significance test to beat the noise; and validated against shipped product metrics, because a benchmark gain that doesn't move users is suspect.
Self-check
Data quality, difficulty and distribution
After this you can reason about what makes a training datapoint valuable
The grader scores the answer, but the datapoint sets the question. A model trained on the wrong tasks learns the wrong things efficiently - and Cursor explicitly hires for improving datapoint quality and difficulty.
Three properties make a training datapoint worth its compute. They pull against each other, which is why "more data" is rarely the answer and curation is the actual job.
The task is well-posed and the reward signal on it is trustworthy.
A correct solution exists and is gradeable.
Garbage datapoints teach the grader's noise, not the skill.
Too easy: the model already solves it, so the gradient is near zero - wasted compute.
Too hard: the model never succeeds, so there's no positive signal to learn from.
The value is at the edge of current ability.
Matches the real spread of what developers ask Cursor to do.
Not just scrapeable, easy or English-only tasks.
Train on the work users bring, not the work that's convenient.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
Spend the fixed budget in the top-right: tasks at the edge of ability that also match what real developers ask. Prune the rest.
Difficulty: train at the edgewhere the gradient lives
In RL the signal comes from the spread between good and bad rollouts. A task the model always passes or always fails gives no contrast and no learning. The useful datapoint is the one the model gets right sometimes - that's where advantage estimation has something to work with.
- Always solved (too easy)
- Reward ~1 every rollout. Zero advantage, zero gradient - pure wasted compute.
- Sometimes solved (the sweet spot)
- Mixed rollouts give contrast; advantage is large; the model learns fastest here.
- Never solved (too hard)
- Reward ~0 every rollout. No positive example to reinforce; the model can't bootstrap.
- Curriculum
- Stage tasks so difficulty tracks ability - keep the model perpetually in the sweet-spot band.
GRPO-style methods normalize advantage within a group of rollouts on the same prompt; a prompt with all-same outcomes contributes nothing, which is exactly why difficulty calibration is a training-efficiency lever, not a nicety.
Distribution-matching and synthetic data
The easiest data to get - scraped repos, leetcode-style problems, tasks with clean tests - is not the data that matches a real Cursor session. Real requests are multi-file, underspecified, full of project-specific conventions and often have no tests. Closing that gap is its own research problem.
- 1Profile the real distribution. Characterize what users actually ask - task types, repo sizes, languages, how much context, how multi-file - so you know what you're matching.
- 2Generate to fill gaps. Synthesize tasks where real data is thin, using past models to draft problems and candidate solutions in the under-covered regions.
- 3Verify before you trust. Filter synthetic datapoints through execution checks and graders; a synthetic task with a wrong "gold" answer poisons training.
- 4Calibrate and recycle. Bin by measured difficulty, prune the trivial and the impossible and bootstrap the next round of harder data from the model you just trained.
Synthetic data is a feedback loop and feedback loops drift. If you generate tasks from the current model and grade them with the current grader, you can amplify both their blind spots - training the model to be confidently good at exactly the cases your pipeline can't tell apart. Anchor synthetic data to real distributions and trusted verification and keep a human in the loop on samples.
If asked "you have a fixed compute budget, how do you pick training data," don't say "use more data." Say: drop tasks the model already aces (no gradient), drop tasks it never solves (no signal), spend the budget at the edge of ability and weight toward the real user distribution rather than the scrapeable one. Then mention difficulty calibration as a curriculum and verifying any synthetic data before it enters the run.
Takeaway. A valuable datapoint is high-quality, at the edge of the model's ability (so the gradient is nonzero) and matched to the real developer-request distribution; curate by pruning too-easy and too-hard tasks, distribution-match over scrapeability and verify synthetic data before it poisons the run.
Self-check
QWhy does a training task the model solves correctly 100% of the time waste compute in an RL run and what difficulty is most valuable?
CursorBench and reward shaping
After this you can explain how Cursor's internal benchmark and length-shaped rewards push past SWE-bench
The public benchmarks in the last section are a sanity floor, not the thing a Cursor release turns on. CursorBenchCursor's internal benchmark that scores models on both task performance and token efficiency, not accuracy alone. is the in-house answer to every limit those benchmarks have - and the way it scores reflects the non-verifiable-reward problem this whole module started from.
CursorBenchCursor's internal benchmark that scores models on both task performance and token efficiency, not accuracy alone. is Cursor's internal eval, built to be realistic and uncontaminated. Its tasks are drawn from real engineer queries - the actual requests Cursor's own engineers make of the agent - rather than scraped GitHub issues or leetcode prompts. Because the tasks are private and freshly sourced, a model can't have memorized the fix from pretraining, so a gain on CursorBench is closer to capability than recall.
Short, under-specified prompts on purposeresolving ambiguity is the task
The prompts in CursorBenchCursor's internal benchmark that scores models on both task performance and token efficiency, not accuracy alone. are deliberately short and under-specified. A real developer rarely writes a spec; they type a terse request and expect the agent to figure out what they meant. CursorBench keeps that shape on purpose, so resolving the ambiguity and inferring intent is part of what's being scored, not a detail abstracted away.
Most benchmarks hand the model a fully-specified problem and measure whether it can execute. CursorBenchCursor's internal benchmark that scores models on both task performance and token efficiency, not accuracy alone. hands it a short, vague request - the way a real engineer actually asks - and measures whether it can infer intent and resolve the ambiguity before executing. That inference is exactly the non-verifiable skill from section one: no test encodes "did the agent understand what I actually wanted." By keeping prompts under-specified, the benchmark scores the thing that's hardest to score and hardest to game by memorization.
CursorBenchCursor's internal benchmark that scores models on both task performance and token efficiency, not accuracy alone. scores two things at once: the quality of the solution and the mean completion-token length the agent used to get there. Quality alone would let a model ramble its way to a correct answer; pairing it with length makes brevity part of what "good" means, so a solution that's right and tight beats one that's right and bloated.
Interactive diagram. Tab through its regions; each focused region shows its detail in the panel below.
Same end goal - measure whether the agent can do real coding work - but CursorBench is built to resist the failure modes that make SWE-bench movements hard to trust.
Because it scores quality plus brevity on under-specified, uncontaminated tasks, CursorBenchCursor's internal benchmark that scores models on both task performance and token efficiency, not accuracy alone. separates strong from weaker models more than SWE-bench does. SWE-bench is saturating - the best models cluster near the top, so a few points of movement is hard to read. CursorBench keeps the field spread out, which is what makes its deltas worth deciding a release on.
Reward shaping: a nonlinear length penaltypunish too verbose and too terse
Scoring length on the benchmark is one thing; shaping it into the training reward is another. Cursor adds a nonlinear length penaltyA training reward that discourages a model from being too verbose or too terse, so easy tasks finish fast and hard tasks get more room. to the reward so the policy is pushed toward the right amount of output - not the most and not the least.
A naive linear "shorter is better" penalty backfires: the policy learns to truncate, dropping necessary edits or explanation to chase the reward. The nonlinear shape fixes this by penalizing both ends. Going too verbose costs reward, and so does going too terse, so the optimum sits in a band rather than at zero tokens.
- Penalize verbosity
- Long, rambling completions lose reward - this is the same brevity signal CursorBenchCursor's internal benchmark that scores models on both task performance and token efficiency, not accuracy alone. scores, pushed into the gradient.
- Penalize terseness
- Over-short completions that drop needed edits or reasoning also lose reward, so the model can't game the penalty by truncating.
- Nonlinear, not linear
- A linear 'shorter is better' term has its optimum at zero tokens; the nonlinear shape puts the optimum in a sensible middle band.
- Pairs with the quality grader
- Length shaping rides alongside the non-verifiable quality reward - correctness and intent still dominate, length just breaks ties toward tight solutions.
Reward shaping is where the brevity CursorBench measures becomes something the policy actually optimizes for - without the truncation failure a naive length penalty would cause.
Any length term is a reward proxy, so Goodhart still applies. Weight the penalty too hard and the policy will sacrifice correctness to hit the token budget - exactly the kind of metric-up, capability-down hack from the reward-hacking section. The nonlinear shape and a quality grader that dominates the reward are what keep length in its lane: a tie-breaker toward tight solutions, not a target the model chases at the expense of being right.
If asked why you'd build an internal benchmark when SWE-bench exists, give the concrete reasons: SWE-bench is public so it's contaminated and saturating, and it only scores correctness. An internal bench drawn from real engineer queries stays uncontaminated, keeps separating models after the public one saturates, and can score the parts a pass/fail test can't - quality, intent on under-specified prompts, and output length. Then connect it to training: that length signal becomes a nonlinear penalty in the reward, tuned to punish both verbosity and truncation.
Takeaway. CursorBenchCursor's internal benchmark that scores models on both task performance and token efficiency, not accuracy alone. is a private, uncontaminated eval built from real engineer queries with deliberately short, under-specified prompts so inferring intent is part of the task; it scores solution quality and mean completion-token length together, separating strong from weaker models more than a saturating SWE-bench - and that length signal becomes a nonlinear reward penalty that punishes both verbosity and terseness without letting length override correctness.
Self-check
QCursorBenchCursor's internal benchmark that scores models on both task performance and token efficiency, not accuracy alone. uses deliberately short, under-specified prompts and scores completion-token length alongside quality. Why each choice, and how does the length signal enter training without backfiring?