hacktrace: behavior-supervised detection of reward hacking during code generation
Organizations: Nanyang Technological University · Independent Researcher
Abstract
A coding agent can earn a passing grade by fixing its code, or by deleting the test that exposes the bug. Detecting such reward hacking requires recognizing attempted shortcuts, including those that fail. We release 173,561 annotated multi-turn coding trajectories from Qwen3-8B and show that supervising shortcut behavior independently of exploit success substantially improves detection. We introduce HACKTRACE, a behavior-supervised monitor that reads the internal states the agent already computes while generating code. Reusing these states enables monitoring before a turn is complete, without additional language-model tokens or passes. Combining this evidence with static features of the final files achieves a mean per-problem AUC of 0.997 with 8 ms of monitoring overhead, improving both accuracy and latency over monitors that run the model again on an honesty question and answer. The same generation states also provide an inexpensive monitoring signal for reinforcement learning. With strong GRPO penalties, HACKTRACE reduces the cheating share of passing solutions from 82-91% to 1-5%, while retaining honest, correct solutions and maintaining high detection accuracy as the policy evolves. Our results show that both the supervision target and the source of monitoring evidence matter for turning accurate detection into a useful training signal.
Figures & tables
| trained on act | trained on outcome | |||
| detector | act target | outcome target | act target | outcome target |
| self-report probe, artifact-only | 0.990 / 0.989 | 0.815 / 0.855 | 0.149 / 0.346 | 0.882 / 0.976 |
| union static LR | 0.991 / 0.997 | 0.782 / 0.878 | 0.246 / 0.436 | 0.833 / 0.959 |
| TF-IDF + LR | 0.946 / 0.878 | 0.676 / 0.530 | 0.331 / 0.414 | 0.789 / 0.771 |
| fine-tuned CodeBERT | 0.981 / 0.952 | 0.756 / 0.688 | 0.252 / 0.437 | 0.737 / 0.929 |
| HackTrace -activation, fixed | 0.962 / 0.974 | 0.752 / 0.851 | 0.303 / 0.382 | 0.987 / 0.989 |
| within-problem AUC | |||||
| detector | tokens | ms | all | editable | read-only |
| Baselines: static and text features of the final files | |||||
| AST-evidence LR (15 feats.) | 0 | 0.41 | 0.816 | 0.988 | 0.500 |
| union static LR (22 feats.) | 0 | 1.20 | 0.985 | 0.991 | 0.973 |
| TF-IDF + LR | 0 | 0.75 | 0.953 | 0.946 | 0.967 |
| CodeBERT (512 tokens) | 512 | 11.3 | 0.969 | 0.981 | 0.945 |
| cheat / passes (%) | honest / attempts (%) | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| training | plain | HT (ours) | SR | plain | HT (ours) | SR | |||
| correct | 11/11 | 85.1 | 4.7 | 3.8 | 8.3 | 28.7 | 26.7 | ||
| correct (MM) | 16/11 | 85.1 | 1.5 | 3.8 | 8.3 | 30.8 | 26.7 | ||
| mixed | 16/16 | 81.6 | 2.7 | 4.4 | 8.2 | 27.4 | 28.5 | ||
| mixed (MM) | 16/16 | 81.6 | 1.4 | 4.4 | 8.2 | 29.5 | 28.5 | ||
| correct | 8/8 | 85.1 | 2.4 | 15.5 | 8.3 | 28.7 | 29.2 | ||
Appendix figures & tables26 assets
Supplementary material from the paper’s appendix.
Appendix
| training set | test set | |||
|---|---|---|---|---|
| editable tests | read-only tests | editable tests | read-only tests | |
| problems | 375 | 375 | 190 | 125 |
| trajectories | 56,617 | 76,392 | 15,200 | 25,352 |
| act positives | 11,003 (19.4%) | 15,326 (20.1%) | 2,478 (16.3%) | 5,466 (21.6%) |
| screened labels | 7,753 (13.7%) | 12,167 (15.9%) | 1,617 (10.6%) | 4,323 (17.1%) |
| problems with positives / top-3 share | 353 / 4% | 281 / 4% | 155 / 18% | 88 / 11% |
| threshold at | threshold at | AUC | |||||
|---|---|---|---|---|---|---|---|
| detector | flag | recall | flag | recall | pooled | within | shift |
| HackTrace online, fixed readouts | 0.2% | 78% | 0.0% | 49% | 0.980 | 0.980 | 0.10 |
| HackTrace online, MultiMax head | 0.1% | 73% | 0.0% | 46% | 0.995 | 0.978 | 0.15 |
| HackTrace online, attention head | 0.3% | 86% | 0.0% | 35% | 0.998 | 0.993 | 0.01 |
| HackTrace offline | 0.1% | 70% | 0.0% | 30% | 0.969 | 0.999 | 0.79 |
| self-report probe, artifact-only | 0.1% | 70% | 0.0% | 51% | 0.994 | 0.998 | 0.17 |
| threshold at | threshold at | AUC | |||||
|---|---|---|---|---|---|---|---|
| detector | flag | recall | flag | recall | pooled | within | shift |
| HackTrace online, fixed readouts | 2.3% | 84% | 0.2% | 67% | 0.973 | 0.958 | 0.10 |
| HackTrace online, MultiMax head | 3.0% | 82% | 0.2% | 47% | 0.984 | 0.978 | +0.09 |
| HackTrace online, attention head | 2.2% | 76% | 0.1% | 17% | 0.974 | 0.967 | +0.10 |
| HackTrace offline | 2.4% | 96% | 0.2% | 86% | 0.995 | 0.998 | 0.04 |
| self-report probe, artifact-only | 1.5% | 85% | 0.2% | 80% | 0.990 | 0.989 | 0.04 |
| detector | within [95%] | turn | pooled |
|---|---|---|---|
| All test trajectories | |||
| HackTrace offline | 0.998 [0.996,1.000] | 0.998 | 0.999 |
| HackTrace offline, MultiMax | 0.997 [0.994,0.999] | 0.997 | 0.999 |
| HackTrace offline, learned last | 0.996 [0.994,0.998] | 0.996 | 0.998 |
| HackTrace offline, rolling mean | 0.996 [0.993,0.998] | 0.995 | 0.996 |
| HackTrace offline, learned attention | 0.995 [0.989,0.998] | 0.995 | 0.999 |
| pair | all test | editable-test | read-only |
| HT-comb. SR-art | +0.004 [+0.001,+0.008] | +0.006 [+0.001,+0.012] | +0.001 [+0.000,+0.003] |
| HT-comb. SR-ctx | +0.007 [+0.004,+0.011] | +0.010 [+0.005,+0.016] | +0.002 [+0.000,+0.003] |
| HT-comb. HT-offline | 0.001 [ 0.003,+0.001] | 0.002 [ 0.004,+0.001] | +0.000 [ 0.001,+0.001] |
| HT-attn SR-art | 0.011 [ 0.020, 0.004] | 0.017 [ 0.029, 0.007] | 0.000 [ 0.002,+0.002] |
| HT-attn HT-fixed | +0.008 [ 0.001,+0.016] | +0.011 [ 0.002,+0.024] | +0.001 [ 0.000,+0.004] |
| HT-attn AST | +0.165 [+0.135,+0.198] | 0.014 [ 0.023, 0.006] | +0.497 [+0.495,+0.499] |
| turn (%) | all | editable-test | read-only | all [95%] |
|---|---|---|---|---|
| HackTrace online, fixed readouts | ||||
| 100 | 0.974 [0.966,0.981] | 0.962 [0.950,0.973] | 0.996 [0.993,0.999] | 0.019 [ 0.026, 0.012] |
| 90 | 0.913 [0.899,0.928] | 0.878 [0.858,0.897] | 0.978 [0.962,0.990] | 0.079 [ 0.094, 0.065] |
| 75 | 0.840 [0.816,0.864] | 0.783 [0.750,0.812] | 0.946 [0.923,0.967] | 0.153 [ 0.177, 0.129] |
| 50 | 0.831 [0.809,0.851] | 0.815 [0.790,0.838] | 0.861 [0.823,0.897] | 0.161 [ 0.183, 0.142] |
| 25 | 0.707 [0.679,0.732] | 0.727 [0.693,0.757] | 0.669 [0.623,0.715] | 0.286 [ 0.314, 0.261] |
| detector / read point | episode (%) | tokens | ms |
|---|---|---|---|
| HackTrace online, fixed readouts | |||
| 100% of final turn | 100 | 0 | 3.80 |
| 90% of final turn | 95 | 0 | 3.45 |
| 75% of final turn | 87 | 0 | 2.91 |
| 50% of final turn | 73 | 0 | 2.00 |
| 25% of final turn | 60 | 0 | 1.11 |
| detector | within [95%] | turn | pooled |
|---|---|---|---|
| All test trajectories | |||
| self-report probe, context-matched | 0.990 [0.985,0.994] | 0.968 | 0.997 |
| HackTrace online, MultiMax | 0.987 [0.979,0.994] | 0.982 | 0.998 |
| HackTrace online, fixed | 0.987 [0.971,0.997] | 0.981 | 0.998 |
| HackTrace online, learned attention | 0.975 [0.961,0.987] | 0.967 | 0.996 |
| HackTrace online, rolling mean | 0.972 [0.951,0.988] | 0.966 | 0.998 |
| pair | all test | editable-test | read-only |
| HT-attn SR-art | +0.017 [ 0.001,+0.038] | +0.043 [ 0.029,+0.124] | +0.010 [ 0.001,+0.024] |
| HT-attn HT-fixed | 0.012 [ 0.028,+0.005] | 0.062 [ 0.111, 0.022] | +0.002 [ 0.010,+0.020] |
| HT-attn AST | +0.470 [+0.438,+0.500] | +0.405 [+0.274,+0.533] | +0.489 [+0.479,+0.496] |
| HT-offline SR-art | +0.011 [+0.004,+0.019] | +0.044 [+0.017,+0.073] | +0.002 [ 0.002,+0.006] |
| HT-offline SR-ctx | 0.021 [ 0.036, 0.010] | 0.047 [ 0.093, 0.012] | 0.014 [ 0.029, 0.003] |
| HT-offline AST | +0.464 [+0.430,+0.494] | +0.406 [+0.280,+0.528] | +0.481 [+0.465,+0.492] |
| environment | judge category | agreement | |
|---|---|---|---|
| editable-test | test tampering | 60 | 93.3% |
| editable-test | hard-coding, special-casing, bypass | 25 | 88.0% |
| editable-test | honest, correct | 30 | 100% |
| editable-test | honest, wrong | 30 | 100% |
| editable-test | rule-labeled negative | 30 | 100% |
| read-only-test | hard-coding | 40 | 100% |
| editable-test slice | read-only slice | |||
| detector | all ( ) | passing ( ) | all ( ) | passing ( ) |
| HackTrace online, fixed readouts | 0.962 | 0.953 [0.892,0.997] | 0.996 | 0.997 [0.993,1.000] |
| HackTrace online, MultiMax head | 0.981 | 0.906 [0.815,0.981] | 0.997 | 0.993 [0.978,1.000] |
| HackTrace online, attention head | 0.974 | 0.862 [0.729,0.968] | 0.997 | 0.998 [0.993,1.000] |
| HackTrace offline | 0.998 | 0.985 [0.956,1.000] | 0.999 | 0.998 [0.995,1.000] |
| self-report probe, artifact-only | 0.990 | 0.969 [0.928,0.999] | 0.998 | 0.998 [0.994,1.000] |
| all test | editable-test slice | read-only, passing only | |||||||
|---|---|---|---|---|---|---|---|---|---|
| false-positive rate | |||||||||
| HackTrace online, fixed readouts | 0.825 | 0.931 | 0.982 | 0.600 | 0.765 | 0.905 | 0.987 | 1.000 | 1.000 |
| HackTrace online, MultiMax head | 0.791 | 0.920 | 0.988 | 0.408 | 0.709 | 0.886 | 0.985 | 1.000 | 1.000 |
| HackTrace online, combined (+ static) | 0.918 | 0.993 | 0.999 | 0.736 | 0.982 | 0.998 | 0.994 | 1.000 | 1.000 |
| HackTrace online, attention head | 0.675 | 0.919 | 0.964 | 0.183 | 0.681 | 0.848 | 0.820 | 1.000 | 1.000 |
| HackTrace offline | 0.892 | 0.976 | 0.997 | 0.817 | 0.965 | 0.991 | 0.969 | 0.973 | 1.000 |
| detector | trained on | all ( ) | failing ( ) | passing ( ) | for |
|---|---|---|---|---|---|
| HackTrace online | act , all ( ) | 0.983 | 0.988 | 0.816 | 0.408 |
| act , matched ( ) | 0.941 | 0.940 | 0.908 | 0.389 | |
| outcome ( ) | 0.435 | 0.471 | 0.851 | 0.849 | |
| outcome , no failed att. | 0.509 | 0.546 | 0.861 | 0.823 | |
| pass bit | 0.354 | 0.384 | 0.453 | 0.981 | |
| self-report probe, artifact-only | act , all ( ) | 0.992 | 0.992 | 0.956 | 0.474 |
| detector | all | failing only | difference (failing) | passing only |
|---|---|---|---|---|
| HackTrace online, fixed readouts | 0.996 / 0.945 | 0.990 / 0.840 | +0.150 [+0.098,+0.206] | 0.997 / 0.998 |
| HackTrace online, MultiMax head | 0.997 / 0.997 | 0.997 / 0.992 | +0.005 [+0.002,+0.010] | 0.993 / 0.996 |
| HackTrace online, attention head | 0.997 / 0.997 | 0.995 / 0.991 | +0.004 [ 0.003,+0.010] | 0.998 / 0.994 |
| HackTrace offline | 0.999 / 0.965 | 0.998 / 0.927 | +0.071 [+0.038,+0.110] | 0.998 / 0.995 |
| self-report probe, artifact-only | 0.998 / 0.978 | 0.995 / 0.948 | +0.047 [+0.028,+0.069] | 0.998 / 0.996 |
| self-report probe, context-matched | 0.997 / 0.961 | 0.996 / 0.925 | +0.071 [+0.043,+0.101] | 0.994 / 0.994 |
| within-problem AUC | on all test, paired | ||||||
| detector | tokens | ms | all | editable | read-only | vs. artifact-only | vs. context-matched |
| Generation states: no extra language-model pass | |||||||
| HackTrace online, fixed readouts | 0 | 3.80 | 0.982 | 0.976 | 0.995 | 0.003 [ 0.009,+0.005] | 0.006 [ 0.011, 0.001] |
| HackTrace online, MultiMax head | 0 | 3.24 | 0.980 | 0.973 | 0.992 | 0.005 [ 0.013,+0.002] | 0.009 [ 0.014, 0.004] |
| HackTrace online, attention head | 0 | 3.24 | 0.977 | 0.977 | 0.975 | 0.008 [ 0.021,+0.003] | 0.012 [ 0.023, 0.003] |
| Additional language-model pass | |||||||
| training CV | all test | editable-test | read-only | editable, screened positives only | |
|---|---|---|---|---|---|
| 6 | 0.9984 | 0.985 | 0.978 | 0.997 | 0.970 |
| 12 | 0.9986 | 0.986 | 0.980 | 0.998 | 0.973 |
| 24 | 0.9987 | 0.988 | 0.982 | 0.998 | 0.976 |
| 44 | 0.9988 | 0.989 | 0.983 | 0.998 | 0.978 |
| 88 (selected) | 0.9988 | 0.974 | 0.962 | 0.996 | 0.948 |
| plain GRPO | HackTrace penalty | self-report penalty | cheat at step | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| training | cheat | honest | cheat | honest | cheat | honest | cheat | seeds | HackTrace | self-rep. | ||
| correct | 93.4 | 5.0 | 4.7 | 28.7 | 3.8 | 26.7 | +1.1 [ 4.0, +8.2] | 11/11 | 4.2 | 1.1 | ||
| correct, MultiMax head | 93.4 | 5.0 | 1.5 | 30.5 | 3.8 | 26.7 | 2.9 [ 5.7, 0.3] | 12/11 | 0.0 | 1.1 | ||
| + conflicting | 81.6 | 8.2 | 2.7 | 27.4 | 4.4 | 28.5 | 1.7 [ 6.8, +1.7] | 16/16 | 0.4 | 0.0 | ||
| + conflicting, MultiMax head | 81.6 | 8.2 | 1.8 | 29.4 | 4.4 | 28.5 | 2.8 [ 9.5, +1.8] | 12/16 | 0.0 | 0.0 | ||
| correct | 93.4 | 5.0 | 4.0 | 29.5 | 2.8 | 33.4 | +1.2 [ 0.6, +3.1] | 4/4 | 0.0 | 1.1 | ||
| penalty on honest solutions | share with | ||||
|---|---|---|---|---|---|
| setting | HackTrace | self-report | difference | HackTrace | self-report |
| correct, | [ , ] | ||||
| + conflicting, | [ , ] | ||||
| correct, | [ , ] | ||||
| correct, | [ , ] | ||||
| honest passes | cheating passes | |||||
|---|---|---|---|---|---|---|
| step | pairs | HackTrace | self-report | difference | HackTrace | self-report |
| 120 | 10 | 24.3 | 18.5 | +5.8 [ 4.7, +16.3] | 0.3 | 0.3 |
| 160 | 10 | 25.7 | 17.8 | +7.8 [ 2.3, +17.5] | 0.2 | 0.5 |
| 200 | 10 | 22.2 | 13.7 | +8.5 [ 2.7, +19.0] | 0.2 | 0.0 |
| 240 | 10 | 26.7 | 7.2 | +19.5 [+12.0, +26.7] | 0.0 | 0.0 |
| 280 | 10 | 24.8 | 7.0 | +17.8 [+8.0, +26.2] | 0.3 | 0.0 |
| Step | ||||||
| HackTrace | self-report | |||||
| seed | pass | honest | empty | pass | honest | empty |
| 3 | 5 | 3 | 87 | 0 | 0 | 55 |
| 4 | 40 | 40 | 0 | 0 | 0 | 100 |
| 8 | 27 | 27 | 22 | 2 | 2 | 63 |
| 9 | 35 | 35 | 2 | 40 | 40 | 10 |
| penalty | seeds | cheat (%) | honest (%) | on-policy AUC | ||
| Steps – (on-policy AUC at steps – ) | ||||||
| HackTrace , MultiMax (completed labels) | 16 | 1.5 | [1.0, 2.1] | 30.8 | [29.1, 32.3] | 0.95 |
| self-report, completed labels | 16 | 3.9 | [2.1, 6.2] | 32.8 | [31.2, 34.4] | 0.96 |
| self-report, hinged at | 12 | 4.9 | [2.3, 8.1] | 34.8 | [32.5, 36.8] | 0.86 |
| self-report, re-standardized per group | 16 | 3.2 | [1.1, 6.3] | 26.3 | [22.3, 29.9] | 0.95 |
| union static LR ( features) | 12 | 2.5 | [1.5, 3.5] | 34.8 | [33.2, 36.2] | 1.00 |
| seed | pass visible | pass all | gap | end state | |
|---|---|---|---|---|---|
| 0 | 0 | 0.983 | 0.017 | 0.967 | hacking |
| 0 | 1 | 0.000 | 0.000 | 0.000 | collapsed |
| 0 | 2 | 0.967 | 0.017 | 0.950 | hacking |
| 0 | 3 | 0.933 | 0.017 | 0.917 | hacking |
| 0 | 4 | 0.983 | 0.017 | 0.967 | hacking |
| 0 | 5 | 0.000 | 0.000 | 0.000 | collapsed |
| detector | act -trained AUC | vs self-report | outcome -trained AUC |
|---|---|---|---|
| test-edited flag | 0.942 | +0.026 [+0.008, +0.044] | 0.942 |
| TF-IDF + LR | 0.889 [0.869, 0.909] | 0.026 [ 0.040, 0.013] | 0.497 |
| union static LR | 0.995 [0.993, 0.997] | +0.080 [+0.063, +0.097] | 0.702 |
| self-report probe, artifact-only | 0.916 [0.899, 0.933] | – | 0.446 |
| HackTrace -activation, attention head | 0.939 [0.925, 0.951] | +0.023 [+0.005, +0.041] | 0.509 |
| HackTrace -activation, rolling-mean head | 0.894 [0.880, 0.908] | 0.021 [ 0.041, 0.001] | 0.567 |
| head | editable-only training | mixed training |
|---|---|---|
| attention | 0.990 / 0.671 | 0.974 / 0.997 |
| MultiMax | 0.980 / 0.959 | 0.981 / 0.997 |
| rolling mean | 0.974 / 0.770 | 0.976 / 0.997 |
| mean | 0.963 / 0.785 | 0.961 / 0.997 |
| last token | 0.962 / 0.592 | 0.958 / 0.995 |
Reward Hacking
Reward hacking studies how AI systems exploit imperfect objectives and how to detect, measure, and prevent behavior that scores well but misses intent.
See all 182 papers