hardcode_tests, on its edit menu.
We first write the reward as a function of the episode’s terminal record and compare the verifiers a coding environment can offer. We then reproduce reward hacking: train on the public tests the agent can see, watch the training reward climb while the hidden pass rate does not, and read the dashboard signature. Three fixes follow, together with the rule behind them: the reward used for training and the measurement used for evaluation must not share a weakness. The second half covers rewards that are learned or judged rather than executed, process rewards, and potential-based shaping, the one form of reward shaping that provably leaves the optimal policy unchanged.
11.1 Verifiable rewards
A verifiable reward is computed by a program that checks the outcome, here by executing tests, rather than by a model that predicts quality. Its value is that it is cheap to trust: if the tests are right and the agent cannot see or change them, a pass means the code behaves correctly on those inputs. Its two limits are coverage, since a test suite checks finitely many inputs, and exposure, since anything the agent can observe it can learn to satisfy without solving the task.BugFixEnv calls its reward hook once, at the end of an episode, with the terminal info dictionary of Section 4.5. Writing the reward as a function of that record separates the verifier, which scores the final state, from reward terms that score the episode’s conduct. VERIFIERS holds four verifiers: all hidden tests (the book’s default), all public tests (the ones the test tool shows), and the fraction of each suite that passes. make_reward adds three optional terms: a penalty when a known exploit appears in the edit log, a format penalty per malformed call, and a cost per turn, which Chapter 12 studies. A fourth argument sets the reward of a submission that fails its verifier.
RLConfig(reward=...) or any BugFixEnv. We score the scripted policies of Section 4.6 under the four verifiers, plus one more policy that submits immediately without touching the code. Each policy plays 5 episodes on each of the 20 training tasks.
hardcode_tests makes exactly the visible tests pass. The two fractional verifiers give credit to code that was never fixed: the buggy source already passes part of each suite, so submitting immediately earns 0.397 of the hidden suite without a single edit, and the blind guesser earns more partial credit (0.538) than its binary pass rate (0.200) by a factor of 2.7. The hacker’s fractional hidden score equals the do-nothing score, because hardcode_tests only adds lines for the public inputs. The random policy shows the remaining distinction: its public pass rate (0.310) is twice its hidden one (0.160), because one of its nine calls is the exploit. Each verifier defines a different task, and only the first one is the task we want.
11.2 Dense and sparse rewards
A binary reward is sparse: most episodes of a weak policy earn exactly 0, and with a group baseline a group whose attempts all fail has zero advantage (Section 7.5). A dense reward assigns different values to more of the episodes, so more groups carry gradient. Partial credit is the obvious way to densify a test-based reward. We sample one batch of 8 groups of 8 from the RL base agent and score the same episodes with the binary and the partial hidden-test verifiers.run trains a copy of the RL base agent for 40 steps of the default configuration with a given reward, evaluating on held-out tasks every 10 steps, and held_out measures the hidden-test pass rate of a policy on the held-out tasks. Section 11.7 runs the partial-credit comparison; the binary hidden-test run of Section 11.4 is the reference for the whole chapter.
run refers to LoggedEnv, which Section 11.3 defines before it is first called with a log.
11.3 Reproducing reward hacking
To watch hacking happen we need the quantities the training reward does not show: the hidden pass rate, and how often the exploit is used.LoggedEnv wraps any environment and appends each finished episode’s terminal record, with its reward, to a list. log_by_step cuts that list into one slice per training step, using the episode counts in the history and skipping the evaluation episodes that train_rl runs through the same factory. reward_dashboard turns a slice into the three numbers an engineer should plot for any coding reward: the rate at which the training verifier passes, the rate at which an independent verifier passes, and the share of episodes with a known exploit.
eval_pass is always the hidden-test pass rate, whatever the training reward is.
hardcode_tests works on any task. Compared with the 0.20 that a blind guess gets, the hacking run ends below the policy it started from.
11.4 Fixing a hacked reward
There are three families of fixes, and they work at different places. The first changes the verifier: grade with tests the agent cannot observe. The second adds a detector: recognize known exploits in the episode and penalize or discard them. The third changes monitoring: measure an independent signal during training, so that hacking is seen even when the reward cannot be fixed. We train the first two and compare them with the hacking run; the hidden-test run is the book’s default configuration.hardcode_tests appears, which makes the exploit worth 0. It learns to read and fix just as well, 0.719 held-out, a difference within the single-seed noise of Chapter 9’s ablations. Its last-10-step public and hidden pass rates are almost equal (0.811 and 0.809), which is the dashboard telling us that, once the exploit is gone, the public tests in this gym agree with the hidden ones.
The penalty works here because the gym has exactly one exploit and the detector recognizes it perfectly. Neither holds for a real agent. Detectors are written for exploits someone has already seen, such as edits to test files, sys.exit(0) before the assertions, or literals copied from test inputs, and a policy under pressure from a penalty is pushed toward the exploits the detector does not recognize. Baker et al. (2025) report this pattern with a learned monitor: adding a chain-of-thought monitor to the training reward of a reasoning model in agentic coding environments helped “at low optimization levels”, and with more optimization the agents learned “obfuscated reward hacking, hiding their intent within the CoT”. A detector is therefore most valuable as a monitor whose output is logged and audited, and least valuable as a reward term that the policy can learn to evade.
The train/eval reward split
The three runs differ in one place: whether the reward that drives the update shares a weakness with the measurement that decides whether training worked. In the hacking run, the training reward checked the public tests, and so did nothing else; the only reason we saw the hack is thateval_pass uses the hidden tests. In general:
- The training reward must be cheap, dense enough to learn from, and as hard to exploit as possible. It is optimized, so every weakness will be found.
- The evaluation measurement must be independent of the training reward: different tests, different tasks, and, for any learned or judged component, a different model. It is not optimized, so it stays honest only if no gradient ever flows from it.
- Held-out monitoring during training is what connects them. Plot the gap between the training reward and the independent measurement every few steps. A gap that grows while the training reward climbs is the signature of Figure 11.1, and it is visible within the first 10 steps of the hacking run above.
FAIL_TO_PASS and PASS_TO_PASS tests (Section 4.7) are hidden tests in this sense when used as a training reward, and a benchmark built from them stops being an independent measurement for a model trained on the same harness.
11.5 Learned rewards and judges
Tests do not exist for every task. When they do not, the reward comes from a model: a reward model trained on human or automated preferences, or an LLM judge prompted with a rubric. Both trade verifiability for coverage.A Bradley–Terry reward model
The Bradley–Terry model assumes that a preference between two outcomes is a logistic function of the difference of their latent scores, . Fitting by maximum likelihood on labeled pairs gives the reward-model loss used in RLHF since Christiano et al. (2017), The loss sees only differences, so is identified up to an additive constant, and its scale is set by how separable the pairs are. We build pairs from the RL base agent’s own episodes: within each group, every passing episode is preferred to every failing one, with the hidden tests as the labeler. The reward model is a copy of TinyAgent with a one-unit head,make_critic from Section 7.4, read at the last token of the episode.
hardcode_tests, tests, and submits.
read, followed by an edit, an all-pass test output, and a submit. The reading hacker reproduces every one of those features and fixes nothing. An RL run against this reward model would push probability toward the reading hacker, because its score is high and its behavior is easy to learn.
Gao et al. (2022) measured the consequence at scale: as a policy is optimized against a proxy reward model, a gold-standard reward first rises and then falls, and the turning point depends on the reward model’s size and data. The proxy is accurate on the distribution it was trained on and is exploited off it, which is exactly what RL produces.
LLM judges
An LLM judge reads the transcript, or the final patch, and returns a score, often against a rubric. It needs no training and covers tasks without tests. It is also a reward computed from what the policy produced, so the policy can optimize the judge’s inputs. Its failure modes are those of the reward model plus its own: surface features (“One Token to Fool LLM-as-a-Judge” (2025) reports that strings such as a lone colon or “Let’s solve this problem step by step.” elicit false positives), sensitivity to length and confident tone, and run-to-run noise. A judge also sees only what the transcript shows. We stand in for a judge with a rubric applied to the decoded transcript: did the agent read the code, run the tests, see them pass at the end, and submit? This is a caricature of an LLM judge, not a model of one; what it shares with a real judge is that it scores evidence visible in the transcript.11.6 Process rewards and potential-based shaping
An outcome reward arrives once, at the end. A process reward scores intermediate steps: a process reward model (PRM) trained on step labels, as in Let’s Verify Step by Step (2023) and Math-Shepherd (2023), or a heuristic such as “a test went from failing to passing”. It gives credit where it is earned, which is what turn-level advantages need (Section 7.6). It also changes the task: a policy maximizes the sum of rewards, and a bonus that can be collected repeatedly, or without making progress, becomes a goal of its own. Reward shaping adds a term to the environment’s reward. Ng, Harada, and Russell (1999) showed that shaping of the form for any function of the state, leaves the optimal policy unchanged, and that, without further assumptions on the MDP, no other form of guarantees this. The sufficiency half has a short proof. Along any trajectory, the shaping terms telescope: If is 0 at terminal states (or ), the shaped return of every trajectory from is its original return minus . Taking expectations, for every policy , so in every state all actions shift by the same constant, and the argmax, the optimal policy, does not move. A bonus that is not a difference of potentials has no such guarantee. We check this on the repair MDP of Chapter 2.shape adds the expected shaping term to every state-action reward. The potential is a progress score, 0 for unread, larger for read, larger still for a correct edit, and 0 for done. The non-potential alternative pays 0.2 every time the agent reads, a “reward the good habit” process bonus.
unread is the original 0.773 because , and in every state to within , as the proof predicts. The read bonus, a process reward of 0.2 per read, makes reading forever optimal from every state, with value , more than twice what solving the task is worth. In an LLM agent the same mechanism produces an agent that inspects files indefinitely; the turn budget hides it as “never submits” (Chapter 12).
The proof also says what potential-based shaping cannot do for the book’s training loop. With and at the end of the episode, the shaped return of every episode of a task is its original return minus , a constant for that task. A group baseline subtracts it. Under outcome-level GRPO, a potential-based shaping term has no effect at all on the gradient; its value appears only when credit is assigned per turn, by a critic or a turn-level advantage that sees the individual terms (Chapter 12). And a shaping term that looks potential-based but uses a potential the agent can manipulate, or one that is not 0 at the end, is simply a new reward. The block makes this concrete in the gym: = the fraction of public tests that pass in the current source, computed after every call of an oracle episode and a hacker episode.
11.7 Design problems in practice
Problem: sparse binary rewards. Symptom: most groups have zero advantage, early learning is slow, and hard tasks are never learned. Cause: a binary verifier gives no signal until a success is sampled; a group of attempts at a task with pass rate is uninformative with probability (Section 7.5). Options: (a) larger groups or dynamic sampling, paid in rollouts (Chapter 9). (b) A curriculum that keeps tasks of medium pass rate (Chapter 14). (c) Partial credit or shaping, which densify the signal and change the task (next problem). (d) A better starting policy (SFT or distillation, Chapters 15 and 16). Default: keep the binary verifier and fix density with (a), (b), and (d); add partial credit only with a measurement of what it rewards. The hidden-test run’s history shows how sparse the binary reward is over training.
Problem: partial-credit gaming. Symptom: the reward rises while the binary pass rate does not, or the policy settles on submitting quickly. Cause: a fractional verifier pays for tests that the unfixed code already passes, so doing nothing has a positive reward, and any behavior that moves the fraction a little, including breaking code in ways that happen to pass more cases, earns credit. With GRPO’s standard-deviation normalization, those small differences get full-sized advantages (Section 11.2). Options: (a) binary reward on the task’s FAIL_TO_PASS tests, with partial credit only on tests that the original code fails. (b) Partial credit added to a binary term that dominates it. (c) Partial credit without std normalization (Dr. GRPO), so that small differences give small advantages. Default: (a). The partial-credit run of Section 11.2 measures what it bought here.
Problem: judge exploitation. Symptom: judge scores rise steadily while executed tests or human review do not; transcripts grow more confident and formulaic. Cause: the judge scores the transcript, which the policy writes, and is therefore an optimization target with the blind spots of Section 11.5. Options: (a) use a judge only where no executable check exists, and combine it with one that does. (b) Rubrics with instance-specific, checkable items (Rubrics as Rewards, 2025). (c) A judge that is not the model being trained and is refreshed or ensembled. (d) Monitor judge score against an independent measurement, as in Section 11.4. Default: (a) and (d); never let a judge be the only reward for a capability that tests can check. The rubric judge above gave the reading hacker the oracle’s score.
Problem: reward noise from flaky tests. Symptom: a correct policy’s reward varies between identical runs, groups that should be uniform have nonzero advantages, and learning slows without a visible cause. Cause: if a correct solution fails with probability (false negatives) and an incorrect submission passes with probability (false positives), the expected reward of a policy with true pass rate is , where is its submit rate. The noise rescales the signal by and adds variance; false positives also pay for submitting anything. Options: (a) rerun failing tests and take the majority, multiplying grading cost. (b) Validate every task by running its reference fix repeatedly and drop flaky tasks (Section 4.8). (c) Larger groups to average the noise. Default: (b) before training, then (a) for the remaining tests that flake. flaky_reward wraps any reward with both kinds of noise; we train with each at a rate of 0.3.
Problem: reward scale and normalization. Symptom: changing a reward’s units, or adding a small penalty term, changes the learning speed or the learned behavior far more than expected. Cause: with std-normalized group advantages, the scale of the reward cancels within a group, so multiplying it by 10 changes nothing; without std normalization (adv_mode='mean', Dr. GRPO) the advantage scales with the reward and acts like a learning-rate change. A penalty term, in turn, competes with the verifier in reward units: a format penalty of 0.5 per malformed call can outweigh a success. Options: (a) keep the verifier in [0, 1] and set every term’s size relative to it, in units of “how many failures is this worth”. (b) Std normalization, with its difficulty bias (Section 7.5). (c) Batch-level scaling of group-centered advantages. Default: (a) with (c) or std normalization, and every term logged separately. The block computes advantages for the same batch at two reward scales.
adv_mode='mean', a 0.05 step cost moves advantages by 0.05 per call whatever the verifier’s scale; under std normalization, its effect depends on how much the verifier varies within the group.
Problem: leaking tests. Symptom: training reward on the “hidden” verifier climbs faster than a fresh held-out evaluation, or solutions contain literal values from the grader. Cause: the hidden tests reach the agent through some channel: a test tool that runs them, a file in the repository, an error message, or test data copied into the training tasks. A leaked hidden test is a public test, and every exploit of Section 11.3 applies to it. Options: (a) grade outside the sandbox after the episode, and audit observations for grader content (Section 4.8). (b) Keep a third test set that no reward ever uses. (c) Canary strings in grader files and a scan of transcripts for them. Default: (a) and (b). The block simulates a leak: thetesttool, and thereforehardcode_tests, sees the hidden tests too.
test tool, hardcode_tests special-cases them too, and the hacker’s hidden reward becomes 1.00. A leak converts the training reward that Section 11.4 trusted into the one that Section 11.3 broke, and nothing in the training curves distinguishes the two. Only a third test set that never reached the agent would show it.
Summary
- A reward is the specification of the task. Writing it as a function of the episode’s terminal record separates the verifier (what counts as success) from reward terms (what the episode’s conduct costs);
make_rewardandflaky_rewardimplement both. Public tests, hidden tests, and test fractions define different tasks: the hacker scores 1.000 on the public verifier and 0 on the hidden one. - Training on the public tests reproduced reward hacking in about 10 steps: the exploit spread to 97% of episodes, the training reward reached 1.000, and the hidden pass rate on held-out tasks fell from 0.175 to 0. Its dashboard signature is a smooth rising reward, a falling independent measurement, and a rising behavior statistic. Grading with hidden tests (0.762 held-out) or penalizing the known exploit (0.719) removed it.
- The training reward and the evaluation measurement must not share a weakness: hidden tests are still optimized against, leaked tests are public tests, and only a set that no reward ever uses measures generalization. Monitor the gap between them during training.
- Learned and judged rewards are proxies that RL takes out of distribution: a Bradley–Terry reward model with 87.5% held-out pairwise accuracy scored a reading hacker at +3.08 against the oracle’s +4.31, and a rubric judge scored it equal to the oracle. Partial credit and std normalization paid submissions of unfixed code; partial credit raised the training reward and lowered the held-out pass rate.
- Potential-based shaping provably leaves the optimal policy unchanged (checked to on the repair MDP), and a read bonus made reading forever optimal. Under outcome-level GRPO a potential-based term with at the end has no effect at all; a potential computed from public tests is a public-test reward. All measurements here are single-seed mechanism demonstrations on a 158k-parameter model.
Exercises
- Prove the necessity half of Ng et al.’s result in a two-state example: construct an MDP and a shaping term that is not a difference of potentials and changes the optimal policy, then show that no reproduces your .
- Change an assumption: rerun the hacking run of Section 11.3 starting from the Chapter 6 base agent (
arl.make_base_agent(tok)) instead ofmake_rl_base. Does the exploit spread faster or slower? Relate the answer to how often each starting policy applieshardcode_testsbefore training. - Write a second exploit op for the gym, for example one that wraps the function body in
try/exceptand returns the expected value of the first public test on any error, and add it toEDIT_OPSin a copy of the environment. Train withmake_reward('public', exploit_penalty=1.0). Does the policy move to the undetected exploit? What does this say about detectors as reward terms? - Train the default configuration against the Bradley–Terry reward model of Section 11.5 instead of the hidden tests: sample with
arl.sample_step, replacebatch['rewards']byrm_scores, and update witharl.rl_update. Plot the RM score and the hidden pass rate over 40 steps. Do you see the rise-then-fall of Gao et al.? - Derive the expected GRPO advantage of an episode under the flaky reward of Section 11.7 as a function of , , and the group’s true pass rate. At what value of does the expected gradient vanish?
- Implement the potential = fraction of hidden tests passing as a per-turn shaping term with
turn_advantagesand a critic (Section 7.6). Does it speed up learning compared with outcome-only GRPO? Explain why the same term under outcome-only GRPO cannot. - Design review: your team’s SWE agent is trained with a reward of “all
FAIL_TO_PASStests pass”, and an LLM judge was added to penalize “test special-casing”. After 200 steps the judge’s flag rate fell to 0.5% and SWE-bench Verified rose by 2 points, while a fresh internal benchmark fell by 1 point. Write down the hypotheses, the measurements that would distinguish them, and the changes to the reward and the evaluation that you would propose.