← Blog
October 19, 2026

Self-HPO: Self-Evolving Coding Agents via Harness-Policy Optimization

Zhepei Wei1,2, Wenlin Yao1, Baolin Peng1, Danqing Wang3, Qianhui Wu1, Hao Cheng1, Yu Meng2, Jianfeng Gao1

1Microsoft Research  2University of Virginia  3Carnegie Mellon University


💡
TL;DR. Recursive self-improvement is when a system improves the very thing that improves it. Self-HPO is a small, concrete version of this for coding agents. The policy reads its own failed runs and rewrites its own harness, the code that decides what it sees, how its commands run and when it stops. Then RL trains it inside that harness. The trained policy then writes the next harness, so the harness writer is itself being improved. No external model is required. Starting from Qwen3.5-35B-A3B, Self-HPO takes SWE-Bench Verified from 61.0% to 69.5% and the average over three benchmarks from 57.8% to 64.8%.
Harness Policy H-step 1 H-step 2 P-step 1 P-step 2 writes writes used in RL used in RL H0 H1 H2 P0 P1 P2 SWE-Bench Verified 61.0%
Start. Base policy P₀ with the seed harness H₀.
Figure 1. Left: Self-HPO alternates H-steps, where the policy rewrites its harness, and P-steps, where RL trains it under that harness. Each new harness is written by the latest policy. Right: SWE-Bench Verified after each step.

Toward recursive self-improvement

A coding agent is a model (the policy) plus the code around it (the harness). The same policy can solve an issue under one harness and fail it under another.

Most ways of improving an agent rely on something outside it: distilling a stronger model, or having a stronger model or a person rewrite the harness. Recent work that trains both the harness and the weights[1, 2] still relies on an external model to write the harness. The agent only gets as good as what it is handed.

Existing self-improving methods optimize only one side, the policy or the harness. RLVR[3] trains the policy inside a fixed harness. Self-Harness[4] lets the policy rewrite its harness, but the policy itself never changes, so it keeps proposing from the same blind spots.

Self-HPO closes the loop. The policy writes its harness, learns inside it, and takes its next tasks from the ones it still fails. Because the harness writer is the policy being trained, each new harness is written by an improved policy. That is the recursive part.

The loop

Overview of the H-step and P-step.
Figure 2. Top (H-step): the frozen policy looks at the agent's failures on a search set and proposes new harness candidates. Bottom (P-step): the policy is trained with RL under the chosen harness. The updated policy becomes the next proposer, and tasks it never solved during training become the next search set.

We start from Qwen3.5-35B-A3B and the mini-SWE-agent[5] seed harness. Each round has two steps.

  1. H-step: the policy rewrites its harness. It reads its own failed runs and edits the harness, both prompts and runtime logic. It tries a few candidates and keeps the one that solves the most tasks on a search set.
  2. P-step: the policy learns inside it. RL trains the policy inside the new harness, rewarded only by whether the tests pass.

Tasks the policy never solved during training become the next search set, so harness search targets the policy's evolving failure modes. We run two rounds, following our stopping criterion. We also run the same loop with Opus 4.8 writing the harness, which we call HPO, to see what a stronger writer adds.

Does it work?

We train only on Python tasks and evaluate on SWE-Bench Verified (in domain) and on two benchmarks the loop never sees: SWE-Bench Multilingual (nine languages other than Python) and SWE-Bench Pro. Numbers are resolve rates averaged over three runs.

SWE-Bench Verified
61.0 → 69.5
+8.5 points (in-distribution eval)
SWE-Bench Multilingual
54.2 → 59.7
+5.5 (out-of-distribution eval)
SWE-Bench Pro
58.3 → 65.2
+6.9 (out-of-distribution eval)

We compare against policy-only and harness-only optimization under two setups. Without an external model, RLVR[3] trains the policy in the seed harness, and Self-Harness[4] lets the base policy rewrite its harness. With Opus 4.8, SFT distills its trajectories into the policy, and Meta-Harness[6] has it rewrite the harness.

Table 1. Resolve rate (%) averaged over three runs, with standard deviations.
MethodHarnessPolicySWE-Bench
Verified
SWE-Bench
Multilingual
SWE-Bench
Pro
Avg.
Base (Qwen3.5-35B-A3B)✗✗61.0 ±0.254.2 ±0.358.3 ±1.257.8 ±0.4
Self-improving setup (no external model)
RLVR✗✓67.5 ±0.856.3 ±1.061.8 ±1.361.9 ±0.6
Self-Harness✓✗65.2 ±1.554.3 ±0.760.7 ±0.960.1 ±0.6
Self-HPO (ours)✓✓69.5 ±0.959.7 ±0.865.2 ±1.464.8 ±0.6
External-model setup (Opus 4.8)
SFT✗✓65.0 ±1.149.6 ±0.849.1 ±0.654.6 ±0.5
Meta-Harness✓✗69.1 ±1.956.5 ±0.964.8 ±1.463.5 ±0.8
HPO (ours)✓✓71.6 ±0.961.4 ±0.766.0 ±0.566.3 ±0.4

Three things stand out.

Resolve rate along Base, H1, P1, H2, P2 for Self-HPO, Self-Harness and RLVR.
Figure 3. Step by step. Self-HPO and Self-Harness share the first H-step and split once Self-HPO trains its policy. After that, the second H-step helps the trained policy on all three benchmarks, and the next P-step adds more. A second harness-only round does not reliably help: Self-Harness drops from 55.0% to 54.3% on SWE-Bench Multilingual. Dashed markers are steps where a method makes no update.

Case study: what the agent wrote into its harness

What does the policy change when it is allowed to edit its own harness? Almost everything it added happens at one moment: when it says it is done. The seed harness ends the run right there and sends whatever was submitted to the grader. The harness the policy wrote first checks the patch (the code change it is about to submit), tells the policy what looks wrong, and gives it one more try before accepting. Figure 4 shows how this grew over two rounds. The code is simplified for reading, but every check and every message is the policy's own:

The coverage message in Figure 4 comes from a real case, sphinx-doc__sphinx-9658. The final policy P₂ had already fixed the source, but it finished by printing a summary instead of its patch, so the submission contained no files. The seed harness would grade that empty submission as a failure. H₂ noticed the empty patch, handed control back once, and P₂ then submitted a valid patch that resolved the task.

61.0%
class AgentHarness(ProgressTrackingAgent):
def execute_actions(self, message):
self.touched |= files_read_or_written(message)
try:
return super().execute_actions(message)
except Submitted:
raise # seed: the run ends here
gap = self.touched - files_in_patch()
self.add_messages(coverage_check(gap))
if decorators_without_import(patch):
self.add_messages(decorator_reminder)
if not mentions(patch, key_terms(issue)):
self.add_messages(pr_intent_reminder)
if first_submit:
return [] # hold once, try again
raise
What the policy sees when it submits
Nothing. The run ends.
[Full Files Auditor] You touched 1 files during exploration: ['sphinx/util/typing.py'] Your submitted patch has 0 files: (none) FOUND GAP: 1 touched files NOT in your patch
[Decorator Reminder] Your patch uses these decorator(s) which may need importing: - @<decorator> Make sure these are imported before submitting.
[PR Intent Reminder] Your patch may not address the core PR intent. Key terms from PR: - <key terms from the issue> Consider: Does your edit actually address the issue described?
The seed harness accepts whatever the policy submits.
Figure 4. Left: Self-HPO's harness as the policy rewrote it over two rounds (simplified). Right: what the policy sees when it submits. The score is SWE-Bench Verified at that step of the loop (H₀ and H₁ with P₀, H₂ with P₁), as in Figure 3.

Why it works

Harness and policy gains compound beyond simple addition

To see what the harness and the policy each contribute, we pair every harness from one Self-HPO run with every policy from that run and evaluate all nine combinations on SWE-Bench Verified (Table 2).

Table 2. Every harness from one Self-HPO run paired with every policy from that run, on SWE-Bench Verified.
Harness \ Policy\(P_0\)\(P_1\)\(P_2\)
\(H_0\) (seed)61.061.764.4
\(H_1\)64.367.868.1
\(H_2\)64.568.669.5

The final harness helps even the untrained base policy (61.0 → 64.5). The final policy helps even under the seed harness (61.0 → 64.4). So neither gain depends on the other being present. Put together they reach 69.5, an 8.5-point gain over the base agent. That is 1.6 points more than the two individual gains added up (3.5 + 3.4 = 6.9).

Self-HPO improves both reliability and the capability boundary

Pass@k of Base and Self-HPO. Pass@1 by task difficulty. Fraction of zero-success GRPO groups during RL.
Figure 5. Left: pass@k on SWE-Bench Verified with eight attempts per task. Self-HPO stays above the base agent for every k, and pass@8 rises from 80.0% to 84.8%. Middle: pass@1 grouped by how often the base agent solves each task. The biggest gains are on tasks the base agent solves only sometimes (+17.2 on Medium, +12.9 on Easy). Hard tasks, which the base agent never solves in eight attempts, gain +6.1, and tasks the base agent always solves drop by 2.2. Right: share of RL training groups in which no attempt succeeds.

Most of the gain is reliability: tasks the base agent could already solve some of the time now get solved most of the time. But the gains on hard tasks and the higher pass@8 show that the agent also solves tasks it could not solve before. The improvement is not uniform, though: tasks the base agent always solved drop slightly.

The right panel of Figure 5 shows one reason the loop helps training too. With pass/fail rewards, RL learns nothing from a group of attempts where every one fails. In Self-HPO's final P-step, such groups fall to 6.5% over the last ten updates. In RLVR they stay mostly between 11% and 17% over its last 50 updates. Fewer such groups means more of each batch carries a learning signal.

What matters in the recipe

Table 3. Ablations on SWE-Bench Verified. Each row changes one part of HPO.
Configuration (SWE-Bench Verified)Resolve rate
HPO71.6
P-step uses SFT on Opus 4.8 trajectories67.2 (−4.4)
P-step uses RFT on its own successes70.5 (−1.1)
H-step reuses the first search set70.1 (−1.5)
H-step uses the policy itself as proposer (= Self-HPO)69.5 (−2.1)

Citation

@article{wei2026selfhpo,
  title   = {Self-Evolving Coding Agents via Harness-Policy Optimization},
  author  = {Wei, Zhepei and Yao, Wenlin and Peng, Baolin and Wang, Danqing and Wu, Qianhui and Cheng, Hao and Meng, Yu and Gao, Jianfeng},
  journal = {arXiv preprint arXiv:2610.22791},
  year    = {2026},
  url     = {https://arxiv.org/abs/2610.22791}
}

References

  1. H. Kim, Y. Lee, G. Lee, C. Finn, K. Lee. WHALE: A Simple Recipe for Joint Harness-Weight Optimization. arXiv:2609.00196, 2026. [link]
  2. M. Chen, C. Lv, G. Zhang, H. Chang, S. Zhou. HarnessForge: Joint Harness and Policy Evolution for Adaptive Agent Systems. arXiv:2606.01779, 2026. [link]
  3. B. Peng, W. Yao, Q. Wu, H. Cheng, et al. Orchard: An Open-Source Agentic Modeling Framework. arXiv:2605.15040, 2026. [link]
  4. H. Zhang, S. Zhang, K. Li, et al. Self-Harness: Harnesses That Improve Themselves. arXiv:2606.09498, 2026. [link]
  5. J. Yang, C. E. Jimenez, A. Wettig, K. Lieret, S. Yao, K. Narasimhan, O. Press. SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering. NeurIPS 2024. [link]
  6. Y. Lee, R. Nair, Q. Zhang, K. Lee, O. Khattab, C. Finn. Meta-Harness: End-to-End Optimization of Model Harnesses. arXiv:2603.28052, 2026. [link]