Skip to main content
The scripts are in the repository, not in the installed package. Clone it, then cd recipes/papers/harness-and-weights before running the commands below. Browse this recipe on GitHub.
Paper: Harness-Aware Self-Evolving: Co-Evolving Model Weights, Harness, and Task Solutions (HASE), Haochen Luo et al., arXiv:2607.03935, July 2026. https://arxiv.org/abs/2607.03935. With SIA: Self Improving AI with Harness & Weight Updates, Prannay Hebbar et al., arXiv:2605.27276 [2], and Prime Agent: A Self-Improving RLM Harness, Seth Karten et al., arXiv:2608.23552 [3]. Book: the reward is a program that runs the code against hidden tests, the rollouts are grouped per prompt, and the harness (the tool, the turn cap, the text the model sees) is part of the setup a score is only comparable under [4], [5]. Claim: HASE’s abstract: “a single model can generate task solutions or edit selected harness components in a multi-turn action space”, and this “enables a single Qwen3-8B model to match the text-classification performance of a GPT-OSS-120B model that uses Claude Code as the harness proposer. In alpha factor mining, HASE outperforms the reported GPT-OSS-120B baseline. HASE also repairs imperfect evaluation components and converges to state-of-the-art performance in circle-packing algorithm discovery.” SIA’s abstract, on Chinese legal charge classification, GPU kernel optimisation and single-cell RNA denoising: “Combining both levers outperforms scaffold iteration alone on all three benchmarks.” Prime Agent moves only the harness (“A persistent IPython REPL … while Continual Harness preserves histories, memories, skills, prompts, and subagent specifications across trajectories”) and “raises ARC-AGI-3 RHAE Best@1 from 30% to 95.5%” with Opus 5; it trains no weights. The change: the both arm trains the weights under the searched harness and is evaluated under it; the harness arm searches only the harness, the weights arm trains only the weights, and neither does neither. All four on the same held-out tasks. What you will learn: how to put the two levers under one measurement. A harness here is a wai.Harness with a skills text in its instructions (Prime Agent’s idea: the harness carries a SKILLS.md the proposer edits between rounds [3]), an optional run_python tool and a turn cap; the search over harnesses is the Meta-Harness loop imported from ../meta-harness (ledger, proposal, gate [6]). The weights are GRPO with TRL and vLLM colocate, the rollouts under the current harness’s instructions, the reward wai.verify.CodeExec on hidden tests. wai.harness.attribute over the 2x2 grid (base and trained weights, under the baseline and the searched harness) says which lever moved the score. You need nothing for --dry-run. --smoke and the full run need Modal (MODAL_TOKEN_ID and MODAL_TOKEN_SECRET, or modal token set) and one H100; WHILEAI_API_KEY is optional and posts the four arms to the platform.

Recipe

  1. Base: Qwen/Qwen2.5-Coder-1.5B-Instruct (--base). The first smoke ran the plain Qwen/Qwen2.5-1.5B-Instruct and it passed 0, 4 and 2 of 128 rollouts under the three harnesses at temperature 1.0 (half the baseline’s replies timed out, the rest were NameError, TypeError and wrong numbers): a floor GRPO gets almost no signal from, so the coder variant of the same size is the default and that measurement is the reason. Data: tasks.py builds 143 quant coding tasks in 11 families over one seeded price table (eight tickers, 500 trading days, random.Random(0), standard library only, so the dry run, CI and the grader’s sandbox all build the same bars). Every task is one function over the table: max drawdown, rolling Sharpe, RSI, realized volatility, a momentum factor’s Spearman rank correlation with next-day returns, a top-k long-short return, golden crosses, beta, ATR, VWAP, the longest up streak, with the ticker and the window varied by template. Each carries privileged.tests, asserts against the reference implementation’s answer computed at build time; the answer is never in the prompt. The split is by family: 5 families train (64 tasks), 6 are held out (79), and decontaminate(train, against=holdout) drops 0. Split by task instead (--split task) and the same check drops 71 of 71, because two tasks from one template are near-copies under the 8-gram rule [4]; that is why family is the default, and it is also the harder question, since the holdout asks for quantities training never showed.
  2. Lever 1, the harness. candidates/*.py, in name order, each harness(model) -> wai.Harness: 00_baseline is the bare instructions, no skills, no tool, one turn; 01_skills adds a SKILLS.md text (how to read the table, sample standard deviation, what to return); 02_skills_tool adds run_python (the model’s code on the seeded table, the printed result back as the next message) and three turns. Every candidate plays every task --k times on the base weights; ledger.jsonl, proposal.md and the gate (compare_runs on the holdout, interval excluding zero) are the Meta-Harness recipe’s own functions [6]. The gate’s pick is the searched harness; when no candidate clears it, the best on the train split is used and the run says so.
  3. Lever 2, the weights. GRPO with TRL GRPOTrainer and LoRA, vLLM colocated for rollouts, one H100, the zero-rl-format-reward recipe’s image and its memory fix (half a group per forward, twice the accumulation, expandable_segments). 40 optimizer steps, 8 generations, 6 prompts a step, 1024 completion tokens, lr 1e-4, KL 1e-4, dapo loss. The system prompt is the harness’s instructions; the reward is CodeExec on the hidden tests. weights trains under 00_baseline; both trains under the searched harness.
  4. Eval: pass@1 on the same held-out tasks, --k samples a task at temperature 1.0, each arm played under its harness (tool loop and all). The untrained base under the baseline harness is evaluated --eval-runs times for the noise floor (eval_variance). Four cells, paired by task (compare_runs): harness vs neither, weights vs neither, both vs harness, both vs weights. wai.harness.attribute over the full grid.
  5. One training seed per arm: the verdict on any trained pair is unresolved until each trained arm has a second seed (--train-seed).

Run

On Windows, from the recipe directory: PYTHONUTF8=1 PYTHONIOENCODING=utf-8 uv run --with modal --with datasets python recipe.py --smoke.

A candidate

One file, one change. This is candidates/02_skills_tool.py: the skills text from candidate 01 plus what the tool is for, the run_python tool, and three turns (run, read, answer). build in harnesses.py turns it into the harness a vLLM engine plays, or the scripted stand-in when the model name starts with scripted.
The fingerprint hashes the instructions (skills text included), the tool name and the Disclosure fields, so an edit to SKILLS is a new harness version, and every row it produces carries it. A block with a print goes to the tool (the table is bars there, and BARS); a block without one is the answer. The proposer is you, or the coding agent running skills/harness-search: read out/proposal.md, edit the skills text into candidates/03_<name>.py, run again.

What you get

python recipe.py --dry-run, offline, the three checked-in candidates on the scripted stand-in, 16 tasks a split, 4 rollouts a task:
On the scripted stand-in these numbers are what the planted rates make them: they show the loop (build, freeze, search, gate, the grid with its two holes named, the paired pair that can be computed) and are not a replication. The grader is real: every row is the candidate’s code plus the hidden tests in a fresh interpreter, and the tool candidate’s 128 steps are 128 real run_python calls. python recipe.py --smoke, Modal, one H100, 2026-09-21: 2 GRPO steps a trained arm, 16 tasks a split, 4 rollouts a task, the coder base. This is the live path proven end to end, not a result; at two steps the trained cells are the base cells:
The GPU minutes are the recipe’s own clock over the three function bodies (engine start, play, grade, train); Modal bills container time, which adds the cold start of each of the three containers. The two training steps logged a mean reward of 0.04 and 0.06 with two thirds of the groups at zero standard deviation: on this task set the coder base passes about one rollout in twenty at temperature 1.0, which is the floor the full run climbs from. The full run’s numbers: see the Result section below.

Result

python recipe.py, the full run, 2026-09-21, one H100 on Modal, 79 held-out tasks in six families the training never showed, 4 rollouts a task, 40 GRPO steps a trained arm. The output, verbatim:
Recipe vs baseline (both vs weights): +0.060 [+0.028, +0.095] over 79 paired tasks. Verdict: unresolved. One training seed per arm; a second seed on each trained arm (--train-seed) passed as train_runs= would resolve it to moved or flat. The eval checks pass: the interval excludes zero and the delta clears the eval’s own re-run band (run_std 0.0032 from 3 base re-runs, band 0.019 = t(df=2) 4.30 x sqrt(2) x run_std). The claim under test was that both beats harness and weights. It beats weights. It does not beat harness: +0.00 [-0.04, +0.04] on the same 79 tasks. The trained weights under the skills harness land exactly where the base weights under the skills harness land (0.085 both), and the attribution says the same thing from the other side: the harness explains 95% of the spread across the four cells (interval 71 to 99), the model 2% (0 to 24), and the model ranking flips (under the bare harness the trained weights lead, under the skills harness they tie). On this base, this task set and this budget, the skills text did what forty GRPO steps did, and adding the steps to the text added nothing the interval can see. That is a flat result on the papers’ claim and it is reported as one. What the search found is its own result. The gate picked 01_skills, the skills text with no tool: +0.08 [+0.05, +0.12] over the bare baseline on the holdout, on the base weights, with no training at all. 02_skills_tool, the same text plus run_python and three turns, did worse than the text alone (0.05 against 0.09 on the holdout) while making 288 tool calls: the 1.5B model runs its code, reads a traceback or a number, and does not repair it. Prime Agent’s REPL [3] pays off for a model that can act on what it reads; this one cannot yet. The proposer’s next candidate should say what to do with the output, not only that the tool exists. The trained weights under the bare harness moved +0.02 [+0.01, +0.04] from a base of 0.003, and their replies grew from 821 to 1,623 characters; under the skills harness the trained replies are 1,130 characters against the base’s 800. The training reward on the train split climbed from 0.04 to above 0.5 by step 30, so the policy learned the five training families; what carried to the six held-out families is the two points above. Runs on the platform: https://while.ai/platform/runs?agent=harness-and-weights (four versions of the agent harness-and-weights, behavior quant_code, each with its harness fingerprint and the run record).

Checks

Two notes on the grid. The trained level of the model axis is two separately trained adapters, one under each harness (that is what co-training means in HASE and SIA), so the attribution’s model axis reads “trained under the harness it is evaluated under”, not one adapter moved between harnesses. And the base model’s own two cells come from the same search rows that picked the harness, so the harness cell is the gate’s holdout evidence, not a fresh draw; the weights and both cells are fresh.

Climb

Round 2 is a second training seed on both trained arms, which is what resolves the verdict. Round 3 is the proposer’s next candidate: a skills text that says what to do with the tool’s output, since the REPL alone lost to the text alone.

Learned

  • The two arms the papers do not run are the ones that make the claim testable: harness alone and weights alone, on the same holdout, paired by task. Without them “both beats scaffold iteration alone” has no interval. With them, on this setup, both beat weights by six points and harness by none.
  • A skills text is cheaper than forty GRPO steps and, here, worth the same: +0.08 from a text edit on the base weights, +0.02 from training under the bare harness, and training under the text added nothing to the text. The attribution report says the harness explains 95% of the spread. Search the harness first; train the weights when the harness has stopped moving.
  • A REPL is only a lever for a model that acts on what it reads. The 1.5B coder made 288 tool calls and scored below the same skills text without the tool. Prime Agent’s gains [3] came with Opus 5; the tool is not the mechanism, the reading is.
  • A templated task set and an 8-gram contamination rule do not mix: split by task, and every train prompt is a near-copy of a holdout prompt. Split by template family, and the holdout is clean and harder. The recipe measures both and says which it uses.
  • A skills file in the harness is a prompt edit with a fingerprint: wai.Harness makes the version without anyone naming it, so the proposer can edit SKILLS.md between rounds and every row still says which text it ran under.
Verified 2026-09-21, whileai 0.114, TRL 1.13.0 + PEFT 0.21.0 on torch 2.13.0, vLLM 0.29.0. 36.1 GPU minutes, $2.38 on one H100 by the recipe’s clock (function bodies; Modal bills the three cold starts too). Runs: https://while.ai/platform/runs?agent=harness-and-weights

Artifacts on Hugging Face

Part of the Papers, replicated collection in the while-ai org.

References

  1. Luo, H., Huang, Y., Luo, S., Liu, F., Li, L., Hu, Z., Feng, J., Liu, Q. Harness-Aware Self-Evolving: Co-Evolving Model Weights, Harness, and Task Solutions. arXiv:2607.03935, 2026.
  2. Hebbar, P., Manawat, Y., Verboomen, S., Ivanova, A., Palanimalai, S., Bhatia, K., Baskaran, V. SIA: Self Improving AI with Harness & Weight Updates. arXiv:2605.27276, 2026.
  3. Karten, S., Zhang, A. L., Thomas, K., Müller, S., Bakouch, E., Auras, D., Senghaas, M., Obeid, F., Dunas, K., Hagemann, J., Jaghouar, S. Prime Agent: A Self-Improving RLM Harness. arXiv:2608.23552, 2026.
  4. Lambert, N. Reinforcement Learning from Human Feedback. arXiv:2504.12501, 2025. Chapter Evaluation.
  5. Zhang, Wang, Ge, Xu, Hamm, Reddy. Stop Comparing LLM Agents Without Disclosing the Harness. arXiv:2605.23950, 2026.
  6. Lee, Y., Nair, R., Zhang, Q., Lee, K., Khattab, O., Finn, C. Meta-Harness: End-to-End Optimization of Model Harnesses. arXiv:2603.28052, 2026.
  7. Lambert, N. et al. Tülu 3: Pushing Frontiers in Open Language Model Post-Training. arXiv:2411.15124, 2024.
  8. Gao, L., Schulman, J., Hilton, J. Scaling Laws for Reward Model Overoptimization. ICML 2023. arXiv:2210.10760.
Last modified on September 22, 2026