8–12 min read · 2026-08-18

Lego-RL: Harness-Native Reinforcement Learning for Coding Agents


TL;DR

Coding agents rely on a harness to manage context, execute tools, and decide when to stop. Lego-RL lets us train inside that existing loop. We focus on three requirements: preserve the information needed for faithful policy updates, keep execution and rewards reliable, and make failures observable at the trajectory level.

Starting from Qwen3.5-35B-A3B, we train a separate policy in each of OpenHands SDK, Claude Code, and OpenCode without changing their control flow. Their best evaluated checkpoints improve SWE-bench Verified from 64.0 / 62.4 / 57.2 to 70.4 / 68.2 / 66.6. Each result comes from one training run.

SWE-bench Verified (%)
OpenHands SDK64.0 → 70.4 (+6.4)
Claude Code62.4 → 68.2 (+5.8)
OpenCode57.2 → 66.6 (+9.4)
Train/Inference Correlation
≥ 0.998
Sampled-token probability Pearson r; median over steps
Training Speed-up
2.5×
Measured async vs sync; ≈1.9× after compute-throughput correction
Training task pool
2,699
Tasks retained after difficulty screening

Why train inside the native harness?

A coding agent does more than ask a model for the next token. Its harness decides which files and tool outputs enter the context, when to compact the conversation, and how to continue after a failed command. Adapting that loop to a trainer can change the environment in which the policy learns. We ask what it takes to train directly inside the harness that will run the agent.

The same Qwen3.5-35B-A3B checkpoint scores 64.0, 62.4, and 57.2 on SWE-bench Verified under OpenHands SDK, Claude Code, and OpenCode. This 6.8-point spread shows that the surrounding harness matters to measured performance. It motivates preserving the native interaction loop during training; it does not, by itself, establish that native training outperforms a rewritten harness.

ModelOpenHands SDKClaude CodeOpenCode
Qwen3.5-35B-A3B (starting point)64.062.457.2
Qwen3.6-35B-A3B (next-generation base)67.463.460.6
KAT-Coder-V2.5-Dev (post-trained from Qwen3.6)67.066.864.8
Lego-RL-Qwen3.5-35B-A3B (one policy per harness)70.468.266.6
Table 1. SWE-bench Verified across native harnesses. SWE-bench Verified (%). Lego-RL reports the best evaluated checkpoint from one separate training run per harness; scores are single-run point estimates.

The trained policies score above the starting checkpoint in all three harnesses. They also exceed Qwen3.6-35B-A3B by 3.0, 4.8, and 6.0 points under this evaluation protocol. These comparisons measure the outcome of the full training recipe; they do not isolate the contribution of any one component.

All three runs begin with the same checkpoint and use the same 2,699 tasks and 200k context budget for three epochs (126 steps). Training reward increases in each run without an observed collapse in policy entropy. The validation curves below show how the separately trained policies develop over time.

The policies use that budget differently. Mean response length grows from 43.5k to 90.9k tokens on OpenHands SDK and from 41k to 51k on Claude Code. Differences in context management may help explain this pattern, but we have not isolated their effect in an ablation. A shared context limit does not imply equal realized inference cost.

The trajectories also reveal a change in how the agent checks its work. In the sampled OpenHands SDK trajectories, the share that reread an edited file rises from 73.6% to 98.1%, while recovery after a failed command changes much less. We examine these behaviors below, alongside the additional interaction cost.

Framework capability comparison

This comparison records the capabilities described in the paper’s cited sources, rather than a live audit of current releases. The first four capability columns concern faithful optimization, the next three reliable execution, and the last observability.

FrameworkBlack-box harnessToken-in / Token-outHistory alignR3Fully asyncSandboxAnti-cheatObservable
verl–✓–✓✓△–△
slime✓✓✓✓✓△△–
MOLT△✓–✓✓–––
SkyRL-Agent△△△✓✓✓––
AReaL✓△––✓–––
Agent Lightning△✓––△––△
Polar✓✓✓–✓✓––
rLLM✓✓–✓✓✓–✓
OpenForgeRL✓△––✓✓––
ALE (ROLL/ROCK)––––✓✓△–
Lego-RL✓✓✓✓✓✓✓✓
Table 2. Agentic RL framework capabilities. Paper-time comparison from paper Table 1 and its references. ✓ supported; △ partial or conditional; – not reported in the cited sources, rather than confirmed absent. R3 denotes rollout-routing replay.
Validation score by training step. Use the step slider or data table for exact values.0.560.60.640.680.720255075100125Validation scoreTraining stepOpenHands SDK: Training step 0, 0.64OpenHands SDK: Training step 5, 0.634OpenHands SDK: Training step 10, 0.622OpenHands SDK: Training step 15, 0.636OpenHands SDK: Training step 20, 0.646OpenHands SDK: Training step 25, 0.66OpenHands SDK: Training step 30, 0.67OpenHands SDK: Training step 35, 0.662OpenHands SDK: Training step 40, 0.656OpenHands SDK: Training step 45, 0.672OpenHands SDK: Training step 50, 0.688OpenHands SDK: Training step 55, 0.644OpenHands SDK: Training step 60, 0.684OpenHands SDK: Training step 65, 0.68OpenHands SDK: Training step 70, 0.686OpenHands SDK: Training step 75, 0.672OpenHands SDK: Training step 80, 0.696OpenHands SDK: Training step 85, 0.656OpenHands SDK: Training step 90, 0.666OpenHands SDK: Training step 95, 0.704OpenHands SDK: Training step 100, 0.678OpenHands SDK: Training step 105, 0.684OpenHands SDK: Training step 110, 0.692OpenHands SDK: Training step 115, 0.678OpenHands SDK: Training step 120, 0.686OpenHands SDK: Training step 125, 0.686Claude Code: Training step 0, 0.624Claude Code: Training step 10, 0.59Claude Code: Training step 20, 0.63Claude Code: Training step 30, 0.63Claude Code: Training step 40, 0.656Claude Code: Training step 50, 0.668Claude Code: Training step 60, 0.646Claude Code: Training step 70, 0.67Claude Code: Training step 80, 0.65Claude Code: Training step 90, 0.678Claude Code: Training step 100, 0.644Claude Code: Training step 110, 0.682Claude Code: Training step 120, 0.664OpenCode: Training step 0, 0.572OpenCode: Training step 10, 0.616OpenCode: Training step 20, 0.622OpenCode: Training step 30, 0.644OpenCode: Training step 40, 0.638OpenCode: Training step 50, 0.666OpenCode: Training step 60, 0.636OpenCode: Training step 70, 0.608OpenCode: Training step 80, 0.624OpenCode: Training step 90, 0.654OpenCode: Training step 100, 0.646OpenCode: Training step 110, 0.638OpenCode: Training step 120, 0.648
OpenHands SDK 0.686Training step 125Claude Code 0.664Training step 120OpenCode 0.648Training step 120
View exact values
Validation score · plotted values
Training stepOpenHands SDKClaude CodeOpenCode
00.6400.6240.572
50.634——
100.6220.5900.616
150.636——
200.6460.6300.622
250.660——
300.6700.6300.644
350.662——
400.6560.6560.638
450.672——
500.6880.6680.666
550.644——
600.6840.6460.636
650.680——
700.6860.6700.608
750.672——
800.6960.6500.624
850.656——
900.6660.6780.654
950.704——
1000.6780.6440.646
1050.684——
1100.6920.6820.638
1150.678——
1200.6860.6640.648
1250.686——
Data notes

Matched runs through step 126. Reward and entropy use a centered 5-point mean; validation and response length are unsmoothed.

Figure 1. Training dynamics across three harnesses. The same starting policy follows different training trajectories in each native harness.

Connecting the agent to the trainer

Lego-RL connects verl for optimization with Harbor for task execution. The harness runs in a fresh sandbox and sends model requests through a proxy. The proxy captures generation-time tokens and metadata; the task’s executable verifier supplies the reward. The harness keeps control of the interaction loop.

This separation lets the same training and execution infrastructure serve all three harnesses. Adding a harness requires an adapter to launch it, configure its model endpoint, and collect its result, followed by checks that its request and history handling are captured correctly.

Implementation: workers, proxy, and sandbox
  • verl handles actor updates and weight sync (FSDP / Megatron-LM / VeOmni), PPO / GRPO / GSPO, a vLLM rollout pool, and sync / fully async / partial-rollout scheduling.
  • Harbor wraps a problem statement, a repository snapshot, and an executable verifier into one task. It starts a sandbox, runs the agent, runs the task's tests for a 0/1 reward, then tears the environment down. Production uses Kubernetes (one pod per trajectory). Smaller runs use local or remote Docker.

Lego-RL fills the gap between them.

  • AgentLoopWorker. Each trainer step hands a batch of tasks to concurrent workers. A worker owns one trial: launch the unmodified harness in a fresh sandbox, point its base URL at the proxy, let it run, then collect the verifier reward and the token-level trace. Agent execution accounts for 91.3% of trial wall time in the measured OpenHands SDK runs, so workers overlap trials to reduce waits between them.
  • An in-process proxy that speaks OpenAI and Anthropic. It is the only path between harness and policy. Token IDs, response masks, log-probabilities, and MoE expert routing get recorded at generation time (see Faithful).
  • Defenses live in the task environment, not in the harness. Anti-cheat and staged network policy sit in the sandbox because the harness cannot be edited (see Reliable).
Lego-RL training infrastructure with sandbox, in-process proxy, inference service, buffer, and trainer.
Figure 2. Harness-native training infrastructure. The unmodified harness runs in a per-trial sandbox. Every model call goes through the in-process proxy, on to the inference service, then into the trainer buffer. Only the sandbox layer has to know which harness is running.
Closed-loop Lego-RL workflow across data preparation, validation, training, live observability, and human review.
Figure 3. The closed-loop training workflow. Five stages: data preparation, run validation, training, live observability, and human review. The Agent Plugin is the control plane spanning stages 2 through 4.

The training objective uses the task’s pass/fail result to update policy-generated tokens. Each model response is conditioned on the context the harness actually supplied. The challenge is preserving that relationship while the harness edits history and trials run asynchronously.

Technical detail: the optimization objective

A task instance consists of a problem statement, an initialized repository environment, and a task-specific executable verifier. The harness belongs to the environment, not to the policy. At turn , the harness maps the current interaction and repository state to a context , the policy generates an action , and the harness executes whatever tool actions were requested, producing . A rollout is the sequence of prompt/response pairs actually exchanged at the model API, and the verifier ultimately outputs a single bit:

Only policy-generated tokens participate in training. Writing for those positions:

Every turn conditions on the harness-supplied context , not on the raw history. We maximize the expected verifier reward with group-relative advantages. Each task samples trajectories; the mean and standard deviation are computed only over admitted trajectories. For groups with at least two admitted trajectories, , with . Filtered trajectories receive zero advantage. A surviving singleton uses the implementation’s zero-mean, unit-scale fallback; an empty group contributes no reward-driven update. All three production runs use GSPO's sequence-level surrogate:

where is the policy version that generated the group, and zeroes out trajectories terminated by infrastructure failures. The asymmetric bounds give the sequence-level ratio more room to move up than down. The trainer also supports token-level objectives such as PPO and GRPO, with their corresponding advantage estimators.

For a group with at least two admitted trajectories, identical rewards give : the group contributes no reward-driven policy gradient. Task difficulty relative to the current policy therefore affects how much learning signal a batch provides. And because comes from executing code, the sandbox and verifier determine the quality of that signal.

Pillar 1: Faithful optimization

The problem: the archived transcript ≠ the token sequence at sampling time

A readable conversation transcript is useful for inspecting a run, but re-tokenizing it may not recover the sequence seen during generation. Policy-gradient training needs probabilities for the sampled tokens under the contexts that produced them. If either is reconstructed incorrectly, the resulting policy ratio is unreliable.

Real harnesses rewrite their own history:

These changes can introduce mismatches between the recorded interaction and the sequence used for optimization. The run may continue without an error, so we check alignment explicitly.

Solution 1: an in-process proxy at the serving boundary

The proxy accepts Anthropic- and OpenAI-compatible requests and records token IDs, response masks, log-probabilities, and MoE routing metadata at generation time. Tool-call echoes are matched by ID, and sub-agent conversations are isolated. Exact token reuse applies to spans that preserve the captured prefix; history rewrites require reconstruction and remain a limitation of capture fidelity.

Solution 2: replay MoE expert routing

Matching token IDs is still not enough for MoE. If vLLM routes token to experts {3, 17} at sampling time and the training forward pass picks {3, 41}, the two sides are computing different probabilities.

Replaying rollout-time routing (R3) raises sampled-token probability Pearson correlation from 0.9946 to 0.9993 and reduces mean absolute token-probability error from 0.0062 to 0.0025. Two silent bugs showed why this has to be checked. A one-position routing misalignment scored worse than no replay (0.750 vs 0.995). An undersized capture buffer for hybrid-attention models wrote out-of-bounds entries as 0, and coverage fell to 24% before the guard started raising. After the fix, coverage stays above 99.8%.

Write for the log-probability recorded at generation time and for the value the trainer recomputes. Faithful optimization requires:

This comparison uses the same weights on both sides. During asynchronous training, the trainer may have moved to ; policy ratios account for that version difference, provided the captured tokens and probabilities are correct. They cannot repair a corrupted record. Across the three production runs, the median probability Pearson correlation over training steps is at least 0.998 for each harness. We also inspect probability errors and KL divergence, since correlation alone does not establish numerical agreement.

Pillar 2: Reliable execution

Layer 1: Reward integrity. Close the paths that score without solving

Each task’s executable tests supply a binary reward: 1.0 if resolved, 0.0 otherwise. That makes the feedback directly testable, but it also makes the verifier and sandbox part of the learning signal. We found several ways to obtain a passing result without producing the intended repair.

Cheat pathIncidenceNotes
Reading the fix from local git history4.6%–20.5%log -p, show, checkout; no command blacklist covers them all
Modifying test files2.4%–19.4%tests pass by construction
Downloading the reference patch from GitHub~1.9%one request away if the network is open
Grader applying the reference patch itself~2.5%score is 1.0 regardless of the agent
Table 3. Paths to reward without solving the task.

Defenses sit in the task environment, on by default, and flip with Harbor's phases: denied while the agent runs, restored for grading.

Sandbox defenses by execution phase
ResourceAgent phaseGrading phase
NetworkEgress firewall in a privileged sidecar; public traffic dropped. The main container has no NET_ADMIN.Relaxed so graders can still install PyPI deps.
Git historyRebased to a single commit; fix objects no longer exist..git.orig restored for git apply.
Test filesNot provided; edits to test paths are rolled back.Restored, then graded.
Table 4. Sandbox defenses by execution phase.

Layer 2: Difficulty is relative to the current policy

Each task samples 8 rollouts. All-success or all-fail groups contribute no gradient, so a fixed pool gets less informative as the policy improves: on OpenHands SDK, zero-variance groups climb from 44.7% to 51.4%; OpenCode holds around 43.3%.

Epoch 1Epoch 3
0/8 successfulEpoch 1 16.48%Epoch 3 12.02%

Share of task groups · shared 0–50% scale.

Figure 4. Task difficulty changes with the policy. First versus last epoch, grouped by successes out of eight rollouts. All-fail and all-success groups have zero reward variance.

Screening starts from 36,884 OpenSWE candidates. A task has to be valid, executable, and solved 1 to 3 times out of 4 by Qwen3.6-27B on OpenHands SDK. That leaves 2,699 tasks, which also work on Claude Code and OpenCode. We ablated the cutoff on four 951-task pools: the selected band and its upper half improve; the lower half and an unscreened sample do not. 72.7% of the unscreened pool was never solved.

Difficulty screening12.4% retained from the previous stage18,982 removed

Keep tasks solved 1–3 times out of four screening attempts by Qwen3.6-27B on OpenHands SDK.

Figure 5. From OpenSWE candidates to the training pool. 12.4% of the executable pool enters training. Disjoint from SWE-bench Verified at repository and instance level.
Validation score (failure-corrected) by training step. Use the step slider or data table for exact values.0.610.630.650.680.70816243240Validation score (failure-corrected)Training steprandom: Training step 0, 0.636random: Training step 10, 0.65random: Training step 20, 0.644random: Training step 30, 0.634random: Training step 40, 0.6341–2/4: Training step 0, 0.6311–2/4: Training step 10, 0.6161–2/4: Training step 20, 0.6411–2/4: Training step 30, 0.6381–2/4: Training step 40, 0.6551–3/4: Training step 0, 0.6341–3/4: Training step 10, 0.6531–3/4: Training step 20, 0.6921–3/4: Training step 30, 0.6871–3/4: Training step 40, 0.692–3/4: Training step 0, 0.6282–3/4: Training step 10, 0.6782–3/4: Training step 20, 0.6492–3/4: Training step 30, 0.6882–3/4: Training step 40, 0.682
random 0.634Training step 401–2/4 0.655Training step 401–3/4 0.69Training step 402–3/4 0.682Training step 40
View exact values
Validation score (failure-corrected) · plotted values
Training steprandom1–2/41–3/42–3/4
00.6360.6310.6340.628
100.6500.6160.6530.678
200.6440.6410.6920.649
300.6340.6380.6870.688
400.6340.6550.6900.682

Bands: successes out of four screening attempts.

Data notes

Four pools of 951 tasks, grouped by screening successes out of four attempts. Scores use the paper’s correction for harness failures. Passes are shown every 10 steps; the degraded 2–3/4 pass at step 35 is excluded.

Figure 6. Which difficulty band should we train on? Held-out validation and training reward for four matched 951-task pools.

Infrastructure failures also affect which trajectories enter the update. We exclude timeouts and environment-setup failures from the group’s advantage statistics and assign them zero loss weight: 7.1% of Claude Code trajectories, 2.4% of OpenHands SDK, and 6.4% of OpenCode. Valid trajectories that reach a turn or token limit retain their verifier outcome.

Paper figure showing trajectory termination rates for Claude Code, OpenHands SDK, and OpenCode. Hatched bars indicate reconstructed values.
Figure 7. Trajectory termination by harness. Timeouts dominate Claude Code; environment-setup failures dominate OpenCode. Hatched values are reconstructed.

Layer 3: 91% of a single trial is the agent executing

An OpenHands SDK trial takes 920 seconds on average, with agent execution accounting for 91.3% of wall time. This stage includes model inference and tool interaction. Long and uneven trial lengths make synchronous batches wait for their slowest members; asynchronous execution lets new trials start as slots become available.

The measured comparison shows a 2.5× reduction in time per step. The runs used GPU groups with different compute throughput, however. Correcting the synchronous run’s optimizer and log-probability recomputation time gives an estimated 1.9 hours per step, versus 1.0 hour asynchronously, at a maximum staleness of one update. This correction narrows the estimated gain attributable to scheduling.

Static timeline comparing synchronous and asynchronous training over the same approximately 7.5-hour window.
Figure 8. Synchronous and asynchronous execution. Sync and async complete three and seven steps in the same 7.5-hour window. The measured mean step-time speedup is 2.5×; after compute-throughput correction, the estimate is about 1.9×.

Partial rollout keeps a ten-minute trial when the weights need to sync. It interrupts vLLM, loads the new weights, and resumes on the same replica using the prefix KV cache. The harness receives the completed generation through the original request.

Infrastructure optimizations and timings
OptimizationStageOnOffMedian speedup
Prebuilt task imagessandbox startup1.04s36.2s33.2×
Mounted agent runtimeagent startup0.51s7.82s15.4×
Lazy image pullsandbox startup1.57s2.66s1.7×
Packaged grading toolchaingrading3.81s2.72s0.71×
Table 5. Infrastructure optimizations. Lazy pull's gain is in the tail (23× worst-case, 21.6 GB → 1.59 GB network). Baking the grader into the image is overhead we accept for reward reproducibility.

Pillar 3: Observable training

Failure attribution: quickly telling policy problems from infrastructure problems

Failures can look the same on the reward curve and have different causes. One run died at step 3 because the tool-call parser was set to hermes instead of qwen3_coder. In another, validation score fell from 0.556 to 0.150. It looked like policy collapse, but only 60 of 172 trajectories had reached grading; the rest never finished environment setup.

Another parser mismatch terminated all 1,024 trajectories on the first turn. A separate run did show policy collapse. Inspecting the trajectories helped distinguish these cases; retrospective analysis of the collapse yielded an early-stop condition that would have triggered eight steps earlier. That retrospective result still needs testing on new runs.

Live UI failure diagnosis: per-step termination reasons and assisted analysis of a collapsed run.
Figure 9. Separating policy failures from infrastructure failures. Left: per-step termination-reason distribution of an environment-failure run. Right: assisted analysis of a collapsed run, and the corresponding early-stop condition.

To explore a run, start with the Claude Code dashboard’s termination breakdown, then compare the train/inference consistency diagnostics with the reward curve. These views help check whether a change in score coincides with execution failures or probability mismatches.

GSPO · OpenSWE · Claude CodeOpen dashboard ↗

Loading Claude Code dashboard…

Live data may extend beyond the archived step-103 snapshot.

Figure 10. Explore the live training dashboards. Live dashboards for three native harnesses and two SAO runs. In the archived Claude Code snapshot at step 103, 94.1% of 34,816 rollouts completed normally.

What did training actually change? Evidence at the behavioral level

The reward curve tells us whether performance improved. To examine what changed, we scanned 420 trajectories from each end of the OpenHands SDK production run for tool use and verification behavior. These transcript-derived measures describe actions we can observe; they do not establish the model’s intent.

Self-verification moved the most:

Behavioral metricBefore trainingAfter trainingChange
Rereading an edited file73.6%98.1%+24.5pp
Files examined before the first edit3.456.922×
Running the test suite85.0%93.6%+8.6pp
Table 6. Changes in self-verification behavior.

The later trajectories show more inspection before editing and more rereading afterward. Among trajectories with a failed command, the share that ultimately resolves the task changes from 63.9% to 66.8%, a smaller shift than the self-checking measures. These data do not establish why the behaviors changed at different rates, or whether the recovery difference is robust across repeated runs.

Improvement appears in both coverage and repeated success. The share of task groups with at least one success in eight attempts (pass@8) rises from 83.2% to 87.9%; the share with all eight attempts successful (pass8) rises from 28.3% to 39.4%. These group statistics compare the first and last thirds of training. They suggest a larger observed change in consistency, without implying that training only helps tasks the model could already solve.

There is also a cost: average turns rise from 46.6 to 83.1, while tokens per turn increase by 17%. Longer interaction histories can put pressure on turn limits even when the context budget is large. In the tool-allocation comparison below, percentages are shares of all tool calls. This differs from Table 6, where the test-running percentage counts trajectories that run tests at least once.

Share of all tool calls
Share of all tool calls · OpenHands SDK
Tool categoryBeforeAfterChange (pp)
35.65%39.31%+3.66
14.5%24.63%+10.13
7.22%12.58%+5.36
9.39%9.79%+0.4
15.79%8.51%-7.28
7.24%2.49%-4.75
1.63%1.3%-0.33
4.26%1.23%-3.03
1.07%0.15%-0.93
3.25%0.02%-3.23
0%0%0
Selected tool Run tests · shell
OpenHands SDK
Before7.22%After12.58%
+5.36 percentage points
Claude Code
Before8.96%After11.27%
+2.31 percentage points
Data notes

First and last 420 production trajectories per harness. Percentages are shares of all tool calls. 23,203 and 29,888 total calls, respectively. Bars share a 0–45% scale. Categories follow the paper’s tool-name mapping.

Figure 11. How tool use changes during training. Tool-call shares in the first and last 420 production trajectories per harness. Both runs allocate a larger share to tests and a smaller share to malformed calls.

What these experiments establish

These runs show that an existing coding-agent harness can remain in control of interaction while its policy is trained with RL. Making that work requires checking the path from sampled tokens to policy updates, from sandbox execution to reward, and from an aggregate score to the trajectories behind it. Those checks make the training result easier to interpret and failures easier to investigate.

The evidence here covers one initial model and one production run per harness, with the reported checkpoints selected by periodic validation. Repeated runs are needed to quantify uncertainty. We have not isolated the contribution of every component, or directly compared native-harness training against a matched rewritten harness. The reward-integrity defenses address observed failure modes; they are not a guarantee against every possible shortcut.

What's next

Framework updates, harness adapters, checkpoints, and task indices will stay open.

BibTeX

@article{du2026legorlharnessnativereinforcementlearning,
  title={LEGO-RL: Harness-Native Reinforcement Learning for Coding Agents},
  author={Yiming Du and Yuxin Jiang and Tao Yuan and Jianbo Dai and Shaowei Wang and Jierun Chen and Chaofan Tao and Xianzhi Yu and Lifeng Shang and Kam-Fai Wong and Xiaohui Li and Haoli Bai},
  year={2026},
  eprint={2608.17393},
  archivePrefix={arXiv},
  primaryClass={cs.AI},
  url={https://arxiv.org/abs/2608.17393},
}