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
wraps a
frozen LLM
static environment
the bottleneck
can’t act, remember, or loop
hardcoded states, rules, feedback
the layers add
capabilities: tools, memory, skills
control: states, rules, observations
you get
an autonomous agent
a controllable environment
never touched
the model weights
the tasks & verifiers

Both scale by adding external layers, so the core system never changes. 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 DESIGNER

The designer loop

An LLM designer agent runs the loop below. It watches the policy interact with the environment, diagnoses a systemic flaw in the resulting trajectories, writes a component that targets it, and stacks the candidate provisionally so the same policy can be tested in the customized environment. Components that teach are kept; the rest are revised. The reset()/step() contract, the tasks and the verifiers are untouched throughout.

EXECUTION LOOP · POLICY
ENVIRONMENT DESIGNER
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 agentenvironment designerenvironment
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.

ENVIRONMENT DESIGNER
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.

ENVIRONMENT DESIGNER
It writes an EnvHarness component
class _Contract(Contract):
    def modify_transition(self, action, response, env_state):
        cmd = bash_command(action)
        if "pytest" in cmd:
            env_state.extras["ran_tests"] = True
        if is_submission(cmd) and not env_state.extras.get("ran_tests"):
            return failed(response,
                "pre-commit hook failed: run the test suite before submitting.")
        return response

A plug-in layer over the frozen env, with tasks and verifiers untouched.

ENVIRONMENT DESIGNERrollouts 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

ENVIRONMENT DESIGNER
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 policy models. Skills mined in EnvHarness environments beat both baselines for every model we tested, on the same held-out tasks and with the same verifiers.

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. The designer shares the policy model in every setting.

Also in the paper

6% → 80%environments on demand: steering a benchmark’s success rate into a requested band [0.4, 0.6]
88.3% vs 85.4%RL on EnvHarness environments beats RL on the originals (ALFWorld)
47.7 → 54.8three rounds of policy–environment co-evolution on SWE-bench Verified

Plus 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. An LLM designer does it automatically, against the specific policy being trained.

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. We ship three component types; the interface admits many more.

CITE

Cite this work

@inproceedings{envharness2026,
  title     = {EnvHarness: Awakening Static Worlds for Agent Learning},
  author    = {Anonymous Authors},
  booktitle = {Under review},
  year      = {2026},
}

Code

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