Make Every Expansion Count:Learning to Search Efficiently
in LLM Code Repair
1Technion2NVIDIA Research
Abstract
LLM code-repair systems fix faulty programs by repeatedly generating revisions and running unit tests. Each revision is costly, making the choice of which repair to pursue important for search efficiency. The adaptive policies we compare against make this choice using test pass rates and search statistics, without reading the detailed test output or proposed edit descriptions. We introduce CodeTriage, a learned search policy whose small 0.6B-parameter model reads test output, failure diagnoses, and proposed edits to predict how many repair steps each edit could require to reach a fix. These predictions prioritize all pending edits across programs before generating and testing the revised code. On held-out repair tasks, CodeTriage finds fixes with 21% fewer program revisions and 12% fewer output tokens than the strongest adaptive baseline on each metric. The same model transfers without retraining to eleven unseen repair LLMs, achieving the highest success rate averaged across budgets for each one. Ablations show the value of interpreting proposed edits together with test output and diagnoses, and of learning to compare edits across different programs. Additionally, we release a corpus of 12.2 million program versions generated by four LLMs, organized into branching repair sequences with proposed edits and test results.
1 Problem setting
A repair LLM, the proposer, reads the test output (execution feedback), explains the failure, and describes candidate repairs as proposed edits; each specifies what to change and why, before the revised program is generated. The frontier contains every proposed edit that has yet to be tried, including alternatives for programs explored earlier. A search policy chooses one edge from the frontier. Expanding it generates the revised program, runs the full test suite, and adds the evaluated result as a child node, whose own k proposed edits join the frontier. This operation is one expansion, and the objective is to minimize the number of expansions required to find the first solution.
The fraction of passing tests does not reveal why a program fails or how difficult it will be to repair, and the best edit for one program may be less promising than the second-best edit for another. A search policy should therefore compare proposed edits across programs before paying to generate and test the revised code. CodeTriage changes the search policy while leaving the proposer unchanged. Below it is replayed on an exhaustive tree, in which every edge through depth D has already been expanded, so its decisions can be compared with every baseline on the same candidates and outcomes.
max_tasks_completed(n, T, task_durations, m)Scheduling tasks among adventurers within a time budget.Given n task durations, a total time T, and m adventurers who each take at most one task, return the maximum number of tasks that can be completed. The seven supplied tests match KodCode’s reference solution: sort durations ascending, take tasks while the running total stays within T, and stop at m.
Prompt as seen by the repair LLM
def max_tasks_completed(n: int, T: int, task_durations: List[int], m: int) -> int:
"""
Determines the maximum number of tasks that can be completed within a given total time
by scheduling the tasks optimally among the available adventurers.
Parameters:
n (int): Number of tasks
T (int): Total available time
task_durations (list of int): List of durations for each task
m (int): Number of available adventurers
Returns:
int: Maximum number of tasks that can be completed
"""
from solution import max_tasks_completed
def test_example_1():
assert max_tasks_completed(4, 10, [2, 3, 5, 7], 3) == 3
def test_example_2():
assert max_tasks_completed(5, 8, [1, 2, 3, 4, 5], 2) == 2
def test_single_task():
assert max_tasks_completed(1, 5, [4], 1) == 1
def test_insufficient_time():
assert max_tasks_completed(3, 5, [2, 4, 6], 3) == 1
def test_exact_fit_time():
assert max_tasks_completed(3, 6, [2, 2, 2], 3) == 3
def test_more_adventurers_than_tasks():
assert max_tasks_completed(3, 10, [1, 2, 3], 5) == 3
def test_large_input():
assert max_tasks_completed(10, 20, [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], 4) == 4(a) Repair tree
Program → expanded edit (with its priority) → generated child, tested on the task’s supplied tests
Scroll horizontally to inspect all branches ↔
(b) Frontier
Ranked by s(e)
Every unexpanded proposal across all evaluated programs, ordered by the recorded priority s(e). Select one to inspect it; its parent program highlights in the tree.
(d) Footprint on the full tree
Programs one policy generated before its first fix, on the same 121-node tree · select a policy
(c) Program and tests
Code_Contests_39361_C; the tree was generated by Qwen3-Coder-30B with k = 3 edits per program and depth 4, giving 121 programs of which 2 (identical code) are fixes, both at depth 4. (a) Programs the policy expanded, p₀ … p₁₄ in expansion order, with rings showing their test pass rate; numbered boxes are the expanded edits ej (j is the child’s node id in the tree) with their recorded priority, and a +n badge counts a program’s proposals that were never expanded. The two depth-1 edits that raise the pass rate to 5/7 score highest but both drop the adventurer limit and lead nowhere; the 3/7 edit that keeps it is the live one. Ten of fourteen expansions are dead ends, yet every baseline needs at least 35 on this tree except one AB-MCTS seed that needs 13. (b) The unexpanded proposals, ranked by s(e). (c) Program diff, recorded test outcomes, pytest feedback, and the generator’s diagnosis for the selected program or proposal; for proposals, what the exhaustive tree knows about the branch is shown separately. (d) One policy’s footprint at a time on the same tree: CodeTriage, inference seed 0 of each stochastic baseline, and the deterministic BFS, DFS, and pass-rate. Recorded costs are AB-MCTS 13–65, REx 42–58, BG-MCTS 52, DFS and pass-rate 84, BFS 94, and random 31–92 across seeds. Shading encodes expansion order from light to dark. DFS and pass-rate orders were not exported and are replayed on the exhaustive tree with deterministic tie-breaking, reproducing their recorded cost of 84; on this tree they expand the same 84 programs in a different order. The random replay differs from the recorded runs, whose random stream depended on batch composition. This root sits in the favourable tail; the test-split means are 11.54 expansions for CodeTriage and 14.59 for the best baseline. All 121 programs terminate and were re-executed against the task’s tests; check.py repeats this for the expanded ones.2 Method
Exploring every proposed edit is costly because each expansion requires generating a revised program and running its unit tests. Under a limited budget, the policy must prioritize edits across programs and repair depths before their outcomes are known. CodeTriage makes these comparisons by predicting a distribution over each edit’s solve distance: the fewest successive expansions needed to reach a fix through that edit. The model reads the proposed edit together with the parent state, which contains the execution feedback, the failure diagnosis, and four numerical features: pass rate, normalized depth, failed-test fraction, and normalized program length. The edge’s remaining depth enters the priority rule (Eq. 1) but is not an input to the model.
Parent state and proposed edits
Priority model
Frontier priorities
Ranked frontier
Each frontier edge is serialized with its parent’s execution feedback and diagnosis; the distributions and priorities shown are the recorded ones.
From solve distance to search priority
For an edge e = (v, u), the solve distance t(e) counts the expansion of e plus the shortest path from its child u to an observed solution. If u is at depth d in a tree of maximum depth D, then b = D − d + 1 is the largest solve distance observable for e. The model outputs probabilities (p1, …, pD, p>D), and F(m) = Σj ≤ m pj is the predicted probability of finding a fix within m expansions. The priority should favor edits that are likely to lead to a fix within the remaining depth and require fewer repair steps, so it averages these probabilities over each available number of steps, the normalized area under F up to b:
Averaging gives more weight to fixes predicted sooner and no weight to distances beyond the remaining depth, and dividing by b keeps scores on the same [0, 1] scale across depths. Search is deterministic: at each step, it expands the frontier edge with the largest score. Training uses a discrete-survival likelihood[3]. When a solution is observed within the remaining depth, the exact target t ≤ b contributes −log pt; otherwise the tree reveals only that t > b, a censored observation, and the loss sums the probability mass beyond b, −log(1 − F(b)), rather than treating the branch as permanently unsolvable.
3 Experiments
Exhaustive-tree evaluation. Policies are evaluated on 5,001 complete Qwen3-Coder-30B repair trees from 982 held-out tasks; every policy encounters the same candidates and outcomes, isolating the quality of its search decisions. Every tree contains a solution, so the primary cost is the mean number of expansions to a solution, E[T], reported alongside output tokens through the first fix. CodeTriage is trained with multiple seeds and a single checkpoint is selected on validation performance; stochastic baselines are averaged over inference seeds; ablations and diagnostics use the 4,996-tree validation split. Baselines are random, BFS, DFS, and pass-rate, and the adaptive REx[4], AB-MCTS[5], and BG-MCTS[6], each with its published or validation-tuned settings. Test uncertainties are bootstrap standard errors clustered by the 982 source tasks.
Cross-proposer deployment. The same policy is deployed without retraining or proposer-specific tuning in live search with eleven unseen proposers. Live roots are not screened for solvability, so results report success S(B) and realized cost C(B) under a budget B, where an unsuccessful run consumes the full budget, and AUSC(B), the normalized area under the success curve. Every deployment root is retained and each proposer is weighted equally.
Reported values from the manuscript tables. Pareto and calibration curves are reconstructed from the paper figures; their coordinate readouts are approximate (≈).
On the exhaustive-tree test set, CodeTriage uses 21% fewer expansions than AB-MCTS, the strongest adaptive baseline (11.54 compared with 14.59), and 30% fewer than random search (16.54). It uses 12% fewer output tokens than REx, the most token-efficient adaptive baseline (14.96k compared with 17.03k), and 21% fewer than random search. Averaged across the eleven unseen proposers, it matches or exceeds every adaptive baseline’s success rate at every reported budget at an equal or lower mean expansion cost; at a budget of 16 expansions it solves 84.8% of problems compared with 83.8% for REx, using 4.60 expansions on average compared with 4.79. Its AUSC(36) is the highest for all eleven proposers, with gains of up to 2.13 percentage points that are statistically significant on nine, and the mean across proposers improves from 81.60 to 82.50.
Matched ablations on the validation split (Table 1) locate the gains. Removing parent-state text raises cost by 16% (11.80 to 13.64 expansions), removing the proposed-edit text by 22% (14.38), and removing both by 45% (17.14). A ranker trained only on sibling pairs needs 13% more expansions than the distributional model (13.32 compared with 11.79), and extending the same loss across the mixed-parent, mixed-depth frontier recovers most of that gap. Holding the trained model fixed, scoring by the one-step probability p1 alone raises cost by 15% and ranking separately within each depth by 18%. Full baseline configurations, uncertainty estimates, and priority-model overhead are reported in the manuscript and appendix.
4 Repair-tree corpus
The corpus consists of exhaustive repair trees that record the proposed edits, the programs they produce, and the test results for those programs. For each proposed edit with a successful repair path, the tree gives its solve distance; branches cut off at the depth limit provide lower bounds. The tasks are approximately 8,700 Python programming problems from the hard subset of KodCode-V1[7], spanning Codeforces[8], TACO[9], Code Contests[10], and APPS[11]. Qwen3-Coder-30B[1] generates diagnoses and up to k = 3 proposed edits per node and up to five initial solutions per problem, with maximum depth D = 4; the resulting corpus contains 45,424 trees. Matched corpora from Devstral-Small-2[12], Qwen3-Coder-Next[13], and Nemotron-Cascade2[14] expand the same initial programs under the same task-level splits, isolating the effect of proposer-generated repair trajectories. Together, the four corpora contain 12.2 million executed nodes.
| Repair proposer | Executed nodes | Mean nodes / root |
|---|---|---|
| 3,967,516 | 87.3 | |
| 3,634,262 | 80.0 | |
| 2,329,481 | 51.3 | |
| 2,256,185 | 49.7 | |
| Total | 12,187,444 | — |
Data are split by programming problem: all trees derived from a problem belong to the same train, validation, or test split. A node solves the task when its pass rate is 1.0. Trees with at least one reachable fix are retained intact, so branches that do not reach a solution within their remaining depth are preserved as censored observations rather than labeled unsolvable.
The corpus and its dataset card will be released together with the code.
Citation
@article{sharony2026codetriage,
title = {Make Every Expansion Count: Learning to Search Efficiently in LLM Code Repair},
author = {Sharony, Elad and Greenberg, Ido and Song, Jialin and Mannor, Shie and Chechik, Gal and Meirom, Eli},
journal = {arXiv preprint},
year = {2026}
}References
- Yang, A., et al. (2025). Qwen3 technical report. arXiv:2505.09388.
- Hu, E. J., et al. (2021). LoRA: Low-rank adaptation of large language models. arXiv:2106.09685.
- Tutz, G., & Schmid, M. (2016). Modeling discrete time-to-event data. Springer.
- Tang, H., Hu, K., Zhou, J., Zhong, S., Zheng, W.-L., Si, X., & Ellis, K. (2024). Code repair with LLMs gives an exploration–exploitation tradeoff. NeurIPS.
- Inoue, Y., Misaki, K., Imajuku, Y., Kuroki, S., Nakamura, T., & Akiba, T. (2026). Wider or deeper? Scaling LLM inference-time compute with adaptive branching tree search. NeurIPS.
- Miyamoto, S., Oba, D., & Okazaki, N. (2026). Aligning tree-search policies with fixed token budgets in test-time scaling of LLMs. arXiv:2602.09574.
- Xu, Z., Liu, Y., Yin, Y., Zhou, M., & Poovendran, R. (2025). KodCode: A diverse, challenging, and verifiable synthetic dataset for coding. Findings of ACL 2025.
- Codeforces. Codeforces. codeforces.com.
- Li, R., et al. (2023). TACO: Topics in algorithmic code generation dataset. arXiv:2312.14852.
- Li, Y., et al. (2022). Competition-level code generation with AlphaCode. Science.
- Hendrycks, D., et al. (2021). Measuring coding challenge competence with APPS. arXiv:2105.09938.
- Mistral AI (2025). Devstral Small 2. huggingface.co.
- Cao, R., et al. (2026). Qwen3-Coder-Next technical report. arXiv:2603.00729.
- Yang, Z., et al. (2026). Nemotron-Cascade 2: Post-training LLMs with cascade RL and multi-domain on-policy distillation. arXiv:2603.19220.