EnvHarness

EnvHarness: Awakening Static Worlds for Agent Learning

Chengsong Huang1*, Zifeng Wang2†, Rujun Han2, Jun Yan2, Yanfei Chen2, Zoey CuiZhu2, Ke Jiang2, Peng Xia4, Han Yu2, Yufan Zhuang2, Yifei Ming2, Jiaqi Pan3, Bhavana Dalvi Mishra2, Jiaxin Huang1, Burak Gokturk2, Tomas Pfister2 and Chen-Yu Lee2†

1Washington University in St. Louis · 2Google Cloud AI Research
3Google Cloud · 4University of North Carolina at Chapel Hill

*This work was done while Chengsong interned at Google Cloud AI Research  ·  Corresponding authors

Agents got a harness. Now the environment gets one.

SKILL LEARNING · THREE BENCHMARKS
38.7
38.5
41.6↑ +3.1
WebArena
47.7
49.9
52.6↑ +2.7
SWE-bench Verified
61.7
62.4
68.3↑ +5.9
ALFWorld
Base agent Learned in real envs Learned in EnvHarness envs
ENVIRONMENT SCALING · SWE-BENCH VERIFIED
THE IDEA

Wrapping, not authoring

Building environments is expensive, and once built they sit still: the same tasks, the same feedback, for every agent forever. Instead of generating new environments, EnvHarness customizes existing ones through the standard reset() / step() interface alone. Every customized task inherits the trusted, human-built verifier of its source benchmark. Three component types cover three kinds of control, and because each preserves the interface, they compose freely: E″ = w₂(w₁(E)). You’ll meet all three, as live, editable code, in the playground below.

Agent Harness
EnvHarnessours
base system frozen LLM static environment
inherent limit lack of action, memory, or loops hardcoded interaction logic
harness layer capabilities — tools, memory customization — states, rules, observations
unified output an autonomous agent a customized environment

Both scale capabilities through external layers rather than changing the core system. Same trick, other side of the loop.

PLAYGROUND

Try it in your browser

The task is the 24 game: combine the four cards with + − × ÷, two at a time, until one number is left. Make it 24 and the episode is solved. Play a hand, then open the tabs on the right. The line under each editor says what that component customizes. Edit one, apply it to the stack, and compare the two panels on the left: what the policy observes, and what the environment actually holds.

warming up the Python runtime…
Toy24Env · live
bare env
observe() what the policy sees
get_env_state() env internals
next action: combine(i=?, j=?, op=?)
Pick two cards, then an operator.
trajectory
no actions yet
your component
Python · executed by CPython
waiting for the Python runtime…
no components applied
THE ENVRIGGER

The EnvRigger loop

A component is policy-agnostic, but which component to write is not. The EnvRigger runs the loop below to decide. It treats the policy as a black box: it rolls the policy out in the current environment, reads the successful and the failed trajectories, diagnoses a systemic flaw, writes a component that targets it, and stacks the candidate provisionally so the same policy can be tested inside it. Acceptance is decided on the fresh rollouts alone — does the customized environment cultivate the missing capability while staying solvable? If not, the trajectories flow back into Write until a candidate passes or the revision budget runs out. The reset()/step() contract, the tasks and the verifiers are untouched throughout.

EXECUTION LOOP · POLICY
ENVRIGGER
POLICY
step 1
action
observation
EnvHarness
EnvHarness
EnvHarness
env
env′
✓ a new environment
Observe
Diagnose
Write
Validate
revise ↻
trajectories
fresh rollouts
appends
EnvHarness
τ · trajectories
τ′ · fresh
EnvHarness
policy agentEnvRiggerenvironment
POLICY AGENT
It interacts with the environment
$ edit sklearn/impute.py
$ git commit -m "fix SimpleImputer"
→ submit patch            # tests never run
✗ FAIL: hidden test breaks

Rollouts come back as trajectories τ, successes and failures alike.

ENVRIGGER
It reads τ and names the flaw
“Systemic weakness: the agent submits patches without running the failing test first. Success depends on luck, not verification.”

Diagnosis targets the pattern, not one bad episode. It also fixes the direction: disrupt fragile shortcuts for a strong policy, scaffold the missing steps for a struggling one.

ENVRIGGER
It writes an EnvHarness component
class _Contract(Contract):          # f_T axis
    def modify_transition(self, action, response, env_state):
        cmd = bash_command(action)
        if "pytest" in cmd or "runtests.py" in cmd:
            env_state.extras["ran_tests"] = True
        if is_submission(cmd) and not env_state.extras.get("ran_tests"):
            return failed(response,
                "githook: pre-commit hook 'verify-tests' failed. "
                "Run the test suite before submitting.")
        return response

A plug-in layer over the frozen env, with tasks and verifiers untouched. One flaw often takes several components at once, e.g. a Stage that seeds the state plus a Contract that mediates what follows.

ENVRIGGERrollouts run by POLICY
It validates the piece on fresh rollouts
rollout 3/8 · submit → blocked: "run the test suite…"
rollout 3/8 · $ pytest tests/ … 1 failed
rollout 3/8 · fix → pytest ✓ → submit ✓

verdict: teaches verification → ACCEPT

The skill distilled from these trajectories: run the failing test before the patch and again after it, so no fix is submitted unverified.

ENVRIGGER
Click. A new environment.
env′ = EnvHarness(env)

Same interface, new lessons. The policy trains on it, and the cycle turns again.

Results

The pattern above holds across five benchmarks in four domains, and across policy models. Skills mined in EnvHarness environments beat the unmodified environments and the domain-specific generation pipelines built for each benchmark alike — on the same held-out tasks, with the same verifiers, and in shorter episodes.

EVERY POLICY MODEL IT’S APPLIED TO · SWE-BENCH VERIFIED
30.7
36.8
40.0↑ +3.2
Gemini 3.1 Flash-Lite
41.0
48.4
52.1↑ +3.7
Qwen3.6 27B
47.8
49.8
52.7↑ +2.9
Gemini 3.5 Flash
67.2
69.2
72.4↑ +3.2
Claude Sonnet 4.6
Base agent Learned in real envs Learned in EnvHarness envs

The gain over original-env skills stays nearly constant, 2.9 to 3.7 points, while the no-skill baselines span 30.7 to 67.2: open-weight and proprietary, three model families, a wide capability range. The EnvRigger runs on the same backbone as the policy in every setting, so nothing here comes from distilling a stronger model.

Also in the paper

49.6 stepsaverage episode on SWE-bench Verified: 9.8% shorter than with skills from the original environments (55.0), which run longer than no skills at all (53.6)
6% → 80%environments on demand: steering a benchmark’s success rate into a requested band [0.4, 0.6]
81.4 → 87.9reinforcement learning (GRPO) on EnvHarness environments vs. the originals, on ALFWorld
47.7 → 54.8three rounds of policy–environment co-evolution on SWE-bench Verified

Plus the Chain results on long-horizon episodes (54.3 success at 43.1 steps), leave-one-out generalization to held-out task types, nine targeted-weakness case studies across three benchmarks, and the full formalism for why components compose.

Discussion

Work on self-evolving agents has agents rewriting their prompts, growing skill libraries and editing their own scaffolds. In nearly all of it, the world the agent evolves against stays fixed.

An agent that keeps improving against a frozen benchmark eventually stops learning from it. The start states, the rules and the feedback are the same for every learner at every stage, so once the agent can solve the tasks, the environment has nothing further to teach. The missing piece is not a better agent but control over the environment it learns in.

EnvHarness supplies that half the way the agent harness did for the model: a frozen core that external layers make capable. Every component preserves the reset()/step() contract and inherits the benchmark’s human-built verifiers, so reshaping is cheap, trustworthy, and composable. The EnvRigger does it automatically, against the specific policy being trained, and keeps only what fresh rollouts confirm.

The result is a loop that evolves on both sides of the interface: the environment targets the learner’s current weaknesses, serves difficulty on demand, and co-evolves round after round as the policy improves, climbing 47.7 to 54.8 on SWE-bench over three rounds, where the same budget spent on more environments flattens out. Across five benchmarks in four domains, one interface carries all of it. We ship three component types; the interface admits many more.

CITE

Cite this work

@article{huang2026envharness,
      title={EnvHarness: Awakening Static Worlds for Agent Learning}, 
      author={Chengsong Huang and Zifeng Wang and Rujun Han and Jun Yan and Yanfei Chen and Zoey CuiZhu and Ke Jiang and Peng Xia and Han Yu and Yufan Zhuang and Yifei Ming and Jiaqi Pan and Bhavana Dalvi Mishra and Jiaxin Huang and Burak Gokturk and Tomas Pfister and Chen-Yu Lee},
      year={2026},
      eprint={2608.19880},
      archivePrefix={arXiv},
      primaryClass={cs.AI},
      url={https://arxiv.org/abs/2608.19880}, 
}

Code

The full framework is open source. The repository contains the ActionableEnv contract, the per-benchmark Bridges, the three component types, and the EnvRigger, with presets for every benchmark on this page. The README covers setup and how to reproduce each figure.