🏠 Research Hub All papers & projects 🔬 MAS-Zero Designing MAS with Zero Supervision 🔬 MAS-Orchestra Holistic Orchestration & Controlled Benchmarks 📊 Reasoning Survey Inference Scaling, Learning to Reason & Agents

EvoHarnessBench

Can Your Agents Keep Pace with an Evolving Harness?

A benchmark that places non-stationarity in the harness — tools, skills, and specialist agents — rather than in the task stream. 17 streams, 802 tasks, two evaluation modes: deployment and self-evolving adaptation.

What makesEvoHarnessBenchdifferent

  • Outer harness evolution + optional inner self-evolving adaptation
  • Three axes of harness: tools, skills and agents
  • Two evaluation modes: deployment and self-evolving adaptation. Inspect the evolve-eval skill
  • Deterministic and widely applicable construction, with no LLM in the loop. Inspect the evolve-benchmark skill

Abstract

Modern LLM-based agents operate through a harness of tools, reusable skills, and specialist agents. In practice this harness continually evolves as new capabilities are added. Existing continual-learning benchmarks typically place non-stationarity in the task stream while keeping the harness fixed. EvoHarnessBench inverts that: the externally supplied harness itself expands, stage by stage.

The benchmark contains 17 multi-stage streams constructed deterministically from verifier-based benchmarks (EnterpriseOps-Gym and Agents’ Last Exam), comprising 802 tasks instantiated as 1,510 axis-specific evaluation examples. Two complementary settings isolate the central challenges: deployment evaluation (retention as the harness expands, with no experience carried forward) and self-evolving adaptation evaluation (whether accumulated persistent state remains useful as new capabilities arrive).

Three Persistent Gaps

Current agents do not yet keep pace when the harness grows.

Gap 1Harness expansion is not free

Frontier agents can lose competence on previously solved tasks simply as the externally supplied harness expands — harness-induced forgetting. Model parameters are unchanged; only the exposed capability set grows. The largest case is −34.7% BWT on the agents axis (ALE, deployment).

Gap 2Self-evolving adaptation remains inconsistent

Improvements vary substantially across harness stages, capability axes, and environments. MemToolAgent lifts tools on EOG to 38.6%, while on ALE most methods stay close to or below the deployment baseline.

Gap 3Retention and adaptation can pull in different directions

Methods that best preserve previously accessible competence often impair adaptation to newly introduced capabilities, and vice versa. Positive BWT with negative FWT is a recurring pattern.

Key Takeaways

Lesson 1 · DeploymentHarness change itself creates a retention problem

Negative deployment BWT appears even when neither model parameters nor persistent state are updated. The mechanism differs across axes: tools enlarge the executable action space, skills must be retrieved and engaged, and agents add delegation and coordination demands. Retention must be evaluated as the harness itself evolves.

Lesson 2 · AdaptationEffective adaptation depends on both the harness axis and the adaptation context

Task-relevant exposure helps for tools and skills, whereas agent adaptation benefits more from a stable broader pool. Inner adaptation cannot be considered independently of outer harness evolution.

Lesson 3 · TransferAdaptation gains do not imply retention gains, or vice versa

FWT and BWT reveal behavior hidden by aggregate performance. A system can improve overall while becoming worse either at exploiting newly introduced capabilities or at preserving earlier competence, so the two should be evaluated separately along the harness trajectory.

What it takes to keep pace

These results motivate bi-level adaptation: inner updates from current experience, plus an outer objective that checks whether those updates still hold once the harness moves again — including detecting when persistent artifacts have gone stale.

BibTeX

@article{evoharnessbench2026,
  title={EvoHarnessBench: Can Your Agents Keep Pace with an Evolving Harness?},
  author={Ke, Zixuan and Patil, Vaidehi and Shi, Haizhou and Li, Yang
          and Liu, Ye and Shekkizhar, Sarath and Koul, Anurag
          and Wang, Jiayu and Nguyen, Xuan Phi and Yavuz, Semih
          and Bansal, Mohit and Joty, Shafiq},
  year={2026}
}

Made with ♥ by

*Equal contribution  ·  †Core contributors  ·  ‡Senior authors
1Salesforce Research  ·  2UNC Chapel Hill (internship at Salesforce Research)  ·  3UNC Chapel Hill  ·  4University of Wisconsin–Madison

How it works

The benchmark separates outer harness evolution (tools, skills, and agents accumulating across stages) from optional inner self-evolving adaptation (persistent state updated inside a stage). Then: how streams are built, and how expansion looks on each axis.

The harness is the outer loop. Adaptation, if any, is inner.
Outer · harness evolution   H1 ⊆ H2 ⊆ H3 tools skills agents
H1core
H2+ mid-tail
H3+ long tail

Capabilities are released from core to long tail. Earlier ones stay as affordances or distractors. This study isolates monotonic growth — nothing is retired.

Inner · adaptation   optional, inside the current stage Ht
Taskx at stage t
→
Leadunder Ht
→
Orchestrationsub-agents 1..N
→
Environmentresponse y
Feedback f · may update persistent state zt
Across · persistent state zt
memory learned skills prompts code

Sits outside every stage: the inner loop may write it, and it outlives the harness version that produced it. Deployment evaluation leaves it empty; self-evolving evaluation carries it forward.

carried across stage
How the benchmarks are built One seed dataset EnterpriseOps-Gym — sliced by frequency into evolving versions, one track per axis.
Two evaluation modes · three axes
RQ1

Does harness expansion itself degrade previously solved tasks?

RQ2

Can self-evolving adaptation keep pace as capabilities arrive?

RQ3

Do retention and adaptation pull in different directions?

RQ4

How do the three axes — tools, skills, agents — differ?

Evaluation framework
Systems Single-agent Multi-agent
Modes Deployment Self-evolving adaptation
Methods Memory Prompt Code

Benchmark Statistics

17 streams from two seed benchmarks, constructed deterministically from EnterpriseOps-Gym and Agents’ Last Exam. No LLM-generated tasks. Capabilities are released from core to long tail.

17
harness streams
802
unique tasks
520
tools
42
latent skills
62
specialist agents

Figure 2 · harness growth

Tasks stay focused while the available harness grows

Each stage adds adaptation and held-out test tasks. The curves track the cumulative tools, skills, or agents the system must navigate.

adaptation tasks test tasks EOG available harness ALE available harness required per task real-world release trend
Stage labels show newly released capabilities; in the combined view they read EOG / ALE. EOG bars are solid and ALE bars are hatched; the green curve is the paper's real-world release reference, shown as a relative growth trend.

Task Gallery

Every task the benchmark evaluates on, as the agent receives it: a single request against a live environment, feasible at the stage that introduced its capabilities and graded only on the state it leaves behind. Open one to read its prompt, the harness it was offered, and the checks it is scored against — then open any tool, skill, specialist or piece of software it names to read what that is. The same request can appear under more than one axis, since what the agent is handed differs even when the request does not.

Loading the task corpus…

1,061 evaluation tasks read from the benchmark corpus — the test split of every domain the paper reports, across all harness stages. Prompts, oracle harness, staged files and verifiers are reproduced verbatim; nothing here is recomputed. Figures are equally literal: an EnterpriseOps figure is a screenshot of that task's own environment, seeded from the exact SQL the harness feeds the gym and captured before the agent touches anything, and an ALE figure is the artifact staged in the task directory — the drawing, the slide, the clip — scaled for the web and otherwise untouched. 839 of the 1,061 tasks carry one.

A note on the EnterpriseOps pictures. The 503 tasks draw on 51 distinct seeded databases, so tasks in one domain generally show different environments — but those 51 render as 28 distinct pictures, because each app view shows a curated slice of its gym and a number of seeds differ only in timestamps, in rows below the fold, or in tables the view never displays. Where two environments are pixel-identical they share one file. Only this app view is shown; the relational view that explains how a task is graded is a different question and is not reproduced here.

Results

Start with the evaluation protocol, then switch axes to compare deployment and self-evolving methods on EOG and ALE. Pass rates are verifier success on the held-out cumulative evaluation split.

Evaluation Protocol

A harness stream is a nested sequence H1 ⊆ H2 ⊆ ⋯ ⊆ HT. Each task is feasible at its assigned stage and requires at least one newly introduced capability. At test time the system receives the full cumulative harness — earlier capabilities remain as affordances or distractors.

Deployment evaluation

No persistent state across stages. A fresh instance is evaluated on the cumulative eval set under Ht. Isolates the direct effect of harness expansion: can the agent exploit new capabilities while withstanding distraction from the growing pool?

Self-evolving adaptation

Persistent state zt (memory, learned skills, prompts, or code) may be updated from an adaptation split and carried forward. Measures whether accumulated experience supports new capabilities — or goes stale and misdirects execution.

We report pass rate and cost, plus transfer metrics that separate new-task adaptation from retention:

FWT · forward transfer on newly introduced tasks BWT · backward transfer on earlier cohorts SAS · ReAct, Codex, Claude Code MAS · AutoGen, DeLM Memory · Raw, ReasoningBank, MemToolAgent, G-Memory, LEGOMem Prompt · GEPA Code · Meta-Harness

Cross-axis comparison

ToolsSkillsAgents
Deployment expansionAccuracy ↑, cost ↑Little effectEnvironment-dependent
Largest harness-induced forgetting−5.3%−4.0%−34.7%
Best adaptation gain+27.8% MemToolAgent+27.5% GEPA+110.2% Meta-Harness
Best adaptation contextTask-specificTask-specificFinal full pool from the beginning
Observed bottleneckExtracting useful tool experienceEngaging the right skillsBalancing delegation coverage and selectivity

Adaptation gains are relative to the cumulative deployment baseline on EOG. Forgetting is worst-case deployment BWT.

Tools.

Broader catalogs can raise pass rate (26.0% → 30.2% on EOG) while inflating tokens (27.2M → 75.8M). MemToolAgent is the strongest adapter (38.6%), but ALE gains do not follow.

Skills.

Pool expansion is nearly free at deployment (Codex stays at 18.9% on EOG). The hard part is engagement: default GPT-5 systems invoke almost none of the offered skills; GEPA is the clearest adapter (24.1%).

Agents.

The hardest axis. Task-specific Codex reaches only 6.5% on EOG and cumulative deployment 8.8%. Meta-Harness lifts that to 18.5% (+110.2% relative), while ALE shows the most severe forgetting in the paper (−34.7% BWT).

Obs. 1 · Deployment.

Broader tool exposure improves accuracy but raises cost. The larger catalog is not only a distractor — agents can use extra affordances, at the price of more search.

Obs. 2 · Adaptation.

MemToolAgent 38.6%, ReasoningBank 36.9%, Meta-Harness 35.2% vs. 30.2% deployment on EOG. On ALE most methods stay at or below the baseline.

Obs. 3 · Transfer.

Deployment BWT is negative on both environments. On ALE, GEPA and Meta-Harness improve retention but FWT drops to −28.5% and −11.1% — inner adaptation can over-specialize.

Obs. 4–5 · Context & memory.

Task-specific tools during MemToolAgent adaptation cut time 23.9h → 17.9h with similar pass rate. Structured memories beat raw replay; schemas and overly shared memories can hurt.

Pass rate under the cumulative tool harness

Grey = task-specific reference. Green = best overall method. ▣ = multi-agent. Values are pass %.

FWT / BWT

Full table

Obs. 1 · Deployment.

Broader skill exposure has little aggregate effect. Codex stays at 18.9% on EOG; tokens barely move. Skills must be retrieved rather than remaining as always-on actions.

Obs. 2 · Adaptation.

GEPA is the clear gain on EOG (18.9% → 24.1%). Memory methods are modest or mixed; Codex native memory slightly drops pass rate.

Obs. 3 · Transfer.

Mild deployment forgetting. GEPA reaches +14.9% BWT on ALE with −4.0% FWT — preserving old procedures does not imply learning new ones.

Obs. 4–6 · Engagement.

Default GPT-5 invokes almost no offered skills; task-specific GPT-5.5 invokes 82%, Claude Code 15%, task-specific GEPA raises GPT-5 to 31%. Skill learning needs grounded refinement, coverage, and control over sequential accumulation.

Pass rate under the cumulative skill pool

FWT / BWT

Full table

Obs. 1 · Deployment.

Environment-dependent. Codex improves on EOG (6.5% → 8.8%) and degrades on ALE (5.3% → 4.2%). Coordination is hard even with a task-specific pool.

Obs. 2 · Adaptation.

Largest relative gains of any axis on EOG: Meta-Harness 18.5% from 8.8%. ALE does not follow — only GEPA clearly improves (6.3%).

Obs. 3 · Transfer.

EOG is comparatively favorable (GEPA +14.3% FWT). ALE deployment Codex hits −34.7% BWT, the strongest forgetting in the paper.

Obs. 4–6 · Delegation.

Adaptation helps required-agent recall (79% → 89%), not precision (~90%). Forgetting is lost coverage of previously successful specialists, not distractor takeover. Agent adaptation prefers a stable full pool over a changing sequence.

Pass rate under the cumulative agent pool

“—” = not completed within a comparable compute budget.

FWT / BWT

Full table

Case Studies

Aggregate pass rates say that an expanding harness hurts. A case says how. Every case in the benchmark is here — a single task at a single (adapt stage, test stage) pair, with every system’s attempt beside it: the prompt as the model received it, what the graders checked, what the harness offered, and what each system actually reached for. The list opens on the cases where the systems disagree most, since a task they all solve or all fail shows nothing; clear that filter to browse the rest.

Loading the case table…

3,395 cases and 17,992 system attempts, read from the benchmark’s per-task record — one row per (task, system, adapt stage, test stage) cell. Nothing on this page is recomputed from the trials.

Evaluate Your Own Agent

Evaluate your agent on EOG and ALE through the hosted API service, which provides the environments and graders. For local use, download the published Hugging Face datasets to inspect tasks and harness resources.

Choose how to use EvoHarnessBench
  1. create sessiona fresh, isolated environment
  2. your agent actsMCP tool calls, or a submitted artifact
  3. gradehidden verifiers read the environment
  4. session freedautomatically, on scope exit

run_benchmark does all four once per task. EOG tasks use MCP tools and ALE tasks use artifact files; run_leaderboard combines results from the hosted benchmarks.

EOG · MCP tools, SQL-graded

Dockerized enterprise apps

The agent acts by calling MCP tools bound to a fresh per-session database, and hidden SQL verifiers grade the state it leaves behind. You get task.mcp_servers and task.mcp_session(s). Eight published streams — calendar, CSM, drive, email, HR, hybrid, ITSM, teams — plus enterprise_tri_hybrid, a cross-gym stress domain no published table counts.

ALE · sandbox, artifact-graded

A file sandbox

The task stages input files; the agent works however it likes and submits a deliverable, scored by the task’s own evaluate(). You get task.inputs(), task.fetch_input(p) and task.submit_text(p, s). ALE is a flat layout, so pass domain=None, and its tasks are deliberately hard — a low score is the norm.

If you just want a fast, interactive evaluation. Inspect the evolve-eval skill. This optional skill uses the service’s simple_agentic_evals SDK to guide your coding agent through hosted evaluation. Set your service key and install the authenticated package with the command below, then ask your coding agent to “use the evolve-eval skill to evaluate yourself on the hosted EvoHarnessBench tasks.” It checks the hosted target, reviews the agent configuration and evaluation budget with you, and reports the results. You can also ask it focused API questions such as “How do I get per-verifier results?”; it will give the matching SDK or HTTP access path without starting a run. Credentials stay in environment variables.

bash · install evolve-eval
: "${EVAL_SERVICE_API_KEY:?Required — sign in and open MyAuthtoken}" &&
curl -fsSL -H "Authorization: Bearer $EVAL_SERVICE_API_KEY" \
  https://educator-marrow-cultural.ngrok-free.dev/resources/evolve-eval/install.sh | bash

# Then tell your coding agent: Use evolve-eval to evaluate yourself on the hosted EvoHarnessBench tasks.

For repeatable experiments and leaderboard runs, follow the SDK path from authentication through agent integration, scoring, and continual evaluation.

Detailed evaluation workflow

The hosted service is authenticated, so the first step happens in a browser rather than in code. Log in to the demo, click MyAuthtoken, and copy the service API key it shows you. The same key covers both halves of a run: the scoring calls, and the MCP tool calls your agent makes while it acts.

Log in, then MyAuthtoken

Sign in at the demo and open MyAuthtoken from the signed-in view. That page issues the key; it is yours, so keep it out of shared notebooks and out of version control.

Log in to get a key

Two different keys are in play, and confusing them is the most common setup failure. EVAL_SERVICE_API_KEY opens this service and comes from MyAuthtoken. OPENAI_API_KEY is whatever your method thinks with, and it comes from your own account: the service hosts the environment and the grader, but it does not pay for your inference. Export them once and nothing downstream has to mention either again — EvalClient() reads the first and sends it as a bearer token.

bash
export EVAL_SERVICE_API_KEY="<paste the key from MyAuthtoken>"   # this service
export OPENAI_API_KEY="sk-..."                                  # what YOUR method thinks with

Prompting works too, and is the easier route in a notebook where you would rather not leave either key in a cell that might get committed.

python
import getpass, os

os.environ["EVAL_SERVICE_API_KEY"] = getpass.getpass("Eval service key (MyAuthtoken): ")
os.environ["OPENAI_API_KEY"]       = getpass.getpass("OpenAI API key: ")

Your OpenAI key is only ever seen by the agent you hand it to. The one deliberate exception is the two provided harnesses, "react" and "codex" — they execute on the service, so the key travels with that request, and they require it. Omit it and the SDK raises MissingAPIKey before anything is sent, rather than letting a forgotten key score a clean-looking 0.0 across a whole sweep.

The client SDK is served by the service, so there is no PyPI account, no repo checkout, and no service URL anywhere in your code afterwards. EvalClient() resolves its endpoint on its own: the base_url argument, then $EVAL_SERVICE_URL, then the URL baked into the served wheel.

bash
SDK_BASE="https://educator-marrow-cultural.ngrok-free.dev"
: "${EVAL_SERVICE_API_KEY:?Required — get it from MyAuthtoken first}" &&
WHEEL=$(curl -sSL -H "Authorization: Bearer $EVAL_SERVICE_API_KEY" "$SDK_BASE/sdk" \
  | python -c 'import json,sys; d=json.load(sys.stdin); print(d["path"]) if "path" in d else sys.exit("SDK download denied: " + d.get("detail", "unknown error"))') &&
curl -fsSL -H "Authorization: Bearer $EVAL_SERVICE_API_KEY" \
  "$SDK_BASE$WHEEL" -o "/tmp/${WHEEL##*/}" &&
python -m pip install "/tmp/${WHEEL##*/}"

Install the wheel the deployment is currently serving, not merely a wheel: a stale pre-installed copy shadows it and then drifts from the API silently. The notebooks check simple_agentic_evals.__version__ against GET /sdk and reinstall when the two disagree.

python
from simple_agentic_evals import (CommandAgent, EvalClient, ServiceError,
                                  catalog_targets, resolve_target,
                                  run_benchmark, run_leaderboard,
                                  react_agent, acp_codex_agent, to_openai_tools)

client    = EvalClient()      # endpoint and auth resolved for you -- no URL to paste
LLM_MODEL = "gpt-4o-mini"     # whatever YOUR method thinks with

# Every /v1/* route needs the key, health included, so this one call both connects
# and proves the key -- a bad one fails HERE rather than three sections later.
print("connected ->", client.health())

Never infer support from a benchmark name. Resolution checks the live catalog’s adapter health, track, mode, harness and action surface. If any requested capability is missing, the target is unavailable through the hosted service. Infrastructure and grader failures remain errors rather than being converted to zero.

python
print(client.health())
for target in catalog_targets(client):
    print(target["dataset"], target["benchmark"],
          "runnable=", target.get("runnable"),
          "health=", target.get("health"),
          "actions=", target.get("action_types"))

# Hosted is selected only when every requested capability is healthy.
choice = resolve_target("eog", dataset="evovling_tools",
                        mode="deployment", harness="react", action_type="mcp",
                        client=client)
assert choice["location"] == "hosted", choice
print(choice["reason"])

Continue only when the catalog confirms the requested hosted configuration is runnable. If it is unavailable, select a supported configuration or inspect the published datasets.

First wrap your method as the callable below, then use Connect any agent to connect it through the EOG tool interface or ALE input/output files. Once both pieces are in place, run_benchmark can evaluate fresh, isolated instances across the full task scope.

run_benchmark takes anything callable that accepts a task. There is no base class to subclass and nothing to register, so if your method already exists you are writing an adapter, not a port. You never write with task: either — a fresh environment is provisioned before each call and torn down after it.

return nothing it needs

The grader never reads your return value; it reads the environment you acted on. What you leave in the gym database or in the sandbox filesystem is your answer.

close what you open

mcp.close() before returning, from a finally: — a crash mid-task should still release the session.

report your tokens

Your model calls never touch the service, so it cannot count them. return {"total_tokens": n} fills the cost column; omit it and the row reads not measured rather than a misleading 0.

python
def solve_eog(task, max_steps=6):
    """EOG: act on the gym through MCP tools. Replace the loop with your method."""
    used = 0
    mcp = task.mcp_session(task.mcp_servers[0])
    try:
        tools = to_openai_tools(mcp.list_tools())      # MCP schemas -> OpenAI specs
        msgs = [{"role": "system", "content": task.system_prompt or ""},
                {"role": "user",   "content": task.user_prompt or ""}]
        for _ in range(max_steps):
            r = oai.chat.completions.create(model=LLM_MODEL, messages=msgs, tools=tools)
            used += r.usage.total_tokens
            m = r.choices[0].message
            msgs.append(m.model_dump(exclude_none=True))
            if not m.tool_calls:                       # no call -> the agent is done
                break
            for tc in m.tool_calls:
                out = mcp.call_tool(tc.function.name,
                                    json.loads(tc.function.arguments or "{}"))
                msgs.append({"role": "tool", "tool_call_id": tc.id,
                             "content": json.dumps(out)[:4000]})
    finally:
        mcp.close()                                    # rule 2 -- even if we raised
    return {"total_tokens": used}


def solve_ale(task):
    """ALE: read the staged inputs, write the deliverable. One shot, no tools."""
    files = task.inputs()
    head = task.fetch_input(files[0]["path"])[:4000] if files else ""
    r = oai.chat.completions.create(model=LLM_MODEL, messages=[
        {"role": "system", "content": task.system_prompt or ""},
        {"role": "user", "content": f"{task.user_prompt}\n\n{head}"}])
    task.submit_text(task.output_path, r.choices[0].message.content or "")
    return {"total_tokens": r.usage.total_tokens}


def my_agent(task):
    """One entry point for callable MCP and artifact adapters."""
    if task.action_type == "mcp":
        return solve_eog(task)
    if task.action_type == "sandbox":
        return solve_ale(task)
    raise RuntimeError(f"Unsupported hosted action type: {task.action_type}")

Branch on task.action_type, not a benchmark name. Swap the bodies for LangGraph, your planner or another provider while using the hosted EOG tools or ALE file interface.

Whatever you already have — a prompt optimizer, a memory, a multi-agent scaffold, a container, a CLI — connects through one wrapper and one of two interfaces. Your policy runs in your process and calls the service, so its language, framework and runtime stay yours; anything reachable from inside a Python function can be scored.

EOG tool-use interface — MCP. task.mcp_url(server) with a bearer header is a standard MCP endpoint over streamable HTTP, so any conformant client connects to it, the official MCP SDK included. Underneath, the contract every agent framework consumes is just a list of JSON-Schema tool declarations plus a call(name, args) function — which is why a policy we have never seen still plugs in.

python
server  = task.mcp_servers[0]
url     = task.mcp_url(server)          # hand these two to your own MCP client
headers = {"Authorization": f"Bearer {EVAL_SERVICE_API_KEY}", **server.headers}

# Or use the bundled minimal client. These two calls are the entire seam --
# everything else in the SDK is a convenience on top of them.
mcp   = task.mcp_session(server)
tools = mcp.list_tools()                # 1. WHAT YOUR POLICY CAN DO   (plain JSON Schema)
mcp.call_tool(tools[0]["name"], {})     # 2. HOW YOUR POLICY ACTS      (a plain function)
mcp.close()

ALE artifact-delivery interface — input/output files. fetch_inputs_to(dir) stages the task’s files on your disk and submit_dir(dir) ships back whatever your policy wrote. The contract is a filesystem, so a container, a CLI, a remote job or another language all sit behind it equally well. Submitted paths are normalized under the task’s output/, and non-UTF-8 files are base64-encoded automatically so binaries survive.

python
import subprocess, tempfile
from pathlib import Path


def containerized_agent(task):
    """An existing container, unchanged, scored on an ALE task.

    It never talks to the service: it reads one directory and writes another,
    exactly as it does on your laptop. We fill the first, we collect the second.
    """
    with tempfile.TemporaryDirectory() as tmp:
        work   = Path(tmp)
        indir  = work / "input"                       # we fill this one
        outdir = work / "output"                      # your system fills this one
        outdir.mkdir()

        task.fetch_inputs_to(indir)                   # 1. service -> your disk
        subprocess.run([                              # 2. your system runs, however it likes
            "docker", "run", "--rm", "-v", f"{work}:/work", "my-system:latest",
            "--input", "/work/input", "--output", "/work/output",
        ], check=True, timeout=1800)
        task.submit_dir(outdir)                       # 3. your disk -> service, then grading

    # Returning 0 would be worse than saying nothing: it reports a measurement of zero.
    return None


# Nothing to do with Docker, really. For a plain CLI, steps 1 and 3 are identical and
# step 2 becomes: subprocess.run(["my-system", "--in", indir, "--out", outdir])

That is the whole evaluation. run_benchmark walks the scope, calls your agent once per task, grades what it left behind, and hands back a report. .success_rate is Pass % — strict, every verifier on the task passed — and .accuracy is Score, the mean verifier pass rate. The distance between them is partial credit, and reporting only one of them hides which kind of method you built.

python
report = run_benchmark(my_agent, mode="deployment_eval", client=client,
                       benchmark="eog", limit=2, progress=True)
print(report)                                # the paper-style block

print(report.success_rate, report.accuracy)  # Pass % (strict), Score (partial credit)
print(report.agent_s, report.total_tokens)   # agent time, not wall clock; None if unmeasured
print(report.n_errors, report.complete)      # a non-zero n_errors did not measure what you think


def floor_agent(task):
    """Acts on nothing. Whatever this scores, the environment was giving away."""
    return {"total_tokens": 0}


floor = run_benchmark(floor_agent, "deployment_eval", client=client,
                      benchmark="eog", limit=2, progress=False)
print(f"floor {floor.accuracy:.3f}   mine {report.accuracy:.3f}")
# ^ if these two match, your agent is not acting -- check that it reaches mcp.call_tool

limit= is for rehearsal only. It proves the adapter runs and the grader sees what you left behind, and the report stamps the run SUBSAMPLED so a debug number can never be mistaken for a result. Drop it for anything comparable.

A leaderboard row is both benchmarks, repeated so it carries a spread. Three details make the numbers comparable rather than merely plausible. Hours are summed agent duration, not wall clock — provisioning and grading are ours, so they do not count against you, which is why the row reads agent_s and not latency_s. ± is the population standard deviation over repeated runs, not a standard error and not a confidence interval; the published rows use three. And Overall pools tasks, not percentages: total solved over total attempted, so EOG’s 454 tasks outweigh ALE’s 63, and a macro-average of the two rates would be a different, larger number.

python
# The SDK runs both benchmarks, aggregates repeats, and returns leaderboard JSON.
# A limited rehearsal is stamped `partial`; token telemetry stays None if unreported.
row = run_leaderboard(my_agent, "My Method", client=client,
                      benchmarks=["eog", "ale"], cat="memory", seeds=1, limit=2)
print(json.dumps(row, indent=2))
print(row["benchmark_results"])       # results keyed by hosted benchmark id

#   Pass %    mean(r.success_rate)          strict -- every verifier on the task passed
#   Score     mean(r.accuracy)              partial credit -- mean verifier pass rate
#   h         mean(r.agent_s) / 3600        agent time; r.latency_s is wall clock, not this
#   Tok M     mean(r.total_tokens) / 1e6    None rather than 0 when nothing reported usage
#   Overall   sum(n_success) / sum(n_tasks) pooled across both, over tasks not percentages
#   +/-       pstdev over the `seeds` repeats

# A publishable no-limit run is expensive and requires explicit acknowledgement:
# row = run_leaderboard(my_agent, "My Method", client=client, seeds=3,
#                       benchmarks=["eog", "ale"],
#                       confirm_full_cost=True)

# Two switches worth knowing before you commit:
#   dataset=  the evolving axis -- "evovling_tools" | "evovling_skills" | "evovling_agents",
#             each a separate leaderboard.
#   mode=     the question. "deployment_eval" is a fixed method under a growing harness and
#             is the right default; a method that LEARNS between stages belongs in
#             "self_evolving_adapt_eval" -- the next tab.

version was never an arbitrary shard. Every domain arrives in stages, and each stage adds tools, SKILL.md bundles or roster specialists and brings its own cohort of tasks. Two populations move at once, and crossing them gives the object the whole evolving study is built on: a lower-triangular performance matrix, cohort τ evaluated under harness stage t, defined only for τ ≤ t because a cohort does not exist until its stage is reached. One flag fills it.

python
report = run_benchmark(my_agent, "deployment_eval", client=client,
                       domain="calendar", matrix=True)    # costs roughly T x the last row

#                  cohort v1   cohort v2   cohort v3
#   under H_v1          0.31           .           .    <- adaptation: v1 tasks, v1 tools
#   under H_v2          0.28        0.40           .    <- retention:  v1 tasks, v2 tools exist
#   under H_v3          0.25        0.36        0.22    <- the last row is what ACC pools
#
# Down a column is one cohort's story as the world grows around it. 0.31 -> 0.28 -> 0.25 is
# FORGETTING: nothing about those tasks changed, only the harness did. That column delta is
# what BWT summarizes.

print(report.bwt, report.fwt)     # cohort-size-weighted means, exactly as the appendix defines
print(report.per_stage_acc)       # the diagonal, by stage

deployment_eval isolates the harness’s effect by holding the method fixed. self_evolving_adapt_eval asks the other question — whether learning keeps up with growth — by handing your method each stage’s training split before that stage is evaluated. BWT is meaningful in both; FWT only exists in self-evolving, because it measures adaptation, and omitting adapt there is an error rather than a silent fallback.

python
class EvolvingAgent:
    """A policy with state: `adapt` writes it, `__call__` reads it.

    run_benchmark picks up `.adapt` automatically, so this object is the whole
    integration -- no registration, no adapt= argument needed.
    """

    def __init__(self):
        self.notes = []

    def adapt(self, stage, tasks):      # once per stage, with that stage's TRAIN split
        for t in tasks:
            with t:                     # prompts exist only once a task is provisioned
                self.notes.append(f"[v{stage}] {(t.user_prompt or '')[:140]}")

    def __call__(self, task):
        # `act` is the loop from "Wrap your method", with room in the system prompt
        # for whatever the policy has learned so far.
        return act(task, notes="\n".join(self.notes[-8:]))


evolve = run_benchmark(EvolvingAgent(), "self_evolving_adapt_eval", client=client,
                       domain="calendar", matrix=True)
print(evolve.bwt, evolve.fwt)           # FWT is None in deployment mode; here it is real

Run it yourself, at three levels

All three notebooks use the hosted service’s client and session API. The environments and graders run on the service. The notebooks share setup and health checks, then each answers one question — pick by how far you intend to go.

The Hugging Face release provides datasets for local inspection and analysis. To run and score your agent, please use the hosted API service.

All three sit in the EvoHarnessBench collection on Hugging Face.

Inspect the datasets

A dataset is the evolving axis, a config is {domain}_v{stage}, and the split is train or test. The example below reads a published task and its metadata.

python
from datasets import load_dataset

# One axis of harness evolution; one stage of one domain; the evaluation split.
ds = load_dataset("ZixuanKe/evovling_tools", "hr_v1", split="test")

row = ds[0]
row["user_prompt"]         # the request, exactly as the agent receives it
row["oracle_tools"]        # the annotated required tools
row["cummulative_tools"]   # tools available up to this stage
row["verifiers"]           # verifier metadata
row["gym_servers_config"]  # gym and snapshot metadata

Those columns are the EOG shape. The ale_* configs carry task_prompt, input_files, agent_must_do and the task taxonomy instead, because an ALE task is graded on a submitted artifact rather than on database state.

Hosted evaluation

Run your agent with the hosted service

The dataset downloads provide task records and harness resources. The hosted service supplies the running EOG and ALE environments and graders needed to evaluate an agent. Follow the hosted workflow for authentication, agent integration, and scoring.

Leaderboard

Every reported system, one axis of harness evolution at a time. Systems run the same tasks under the same accumulated harness, so the columns are comparable down an axis — but not across them, since each axis has its own task set. Each row states its backbone model and the harness it ran in, because the field is not uniform: the controlled systems hold GPT-5 fixed, SkillOpt runs GPT-5.5, and Claude Code is Sonnet-4.6 inside its own harness. Filter to one model and one harness to compare shared backbones and scaffolds. Click a system name for its configuration, search budget, and available results by stream and stage.

System details

Transcribed from the paper's three main results tables, plus the Claude Code deployment table and the skill-learning ablations from the appendix. Pass (%) is strict: every verifier on a task has to pass. Score is the mean verifier pass rate, so the distance between them is partial credit. ± is the population standard deviation over 3 runs — not a standard error, and not a confidence interval. Hours are summed agent duration rather than wall clock, and tokens count input plus output. The record carries no dollar cost, so none is shown. The paper reports Claude Code and the skill-learning ablations separately from its main tables; here they are ranked in the same field, with the model and harness on every row in place of that separation. Two caveats survive the merge. Claude Code changes both model and harness, so it is a controlled comparison only against itself. The skill-learning rows vary only skill provenance and were run on EOG alone, so their ALE columns and overall rate are empty; the paper does not state their task count, and the EOG count in the header is the evolving-skills task set rather than a figure quoted for those rows.

Construct Your Own Evolving Benchmark

EvoHarnessBench ships 17 streams constructed from two seed benchmarks, EnterpriseOps-Gym and Agents’ Last Exam. The construction recipe itself is general: point it at a seed of your own and it gives you an evolving benchmark on the same terms.

If you just want a fast, interactive build. Inspect the evolve-benchmark skill, then install the authenticated package with the command below and ask your coding agent to “use the evolve-benchmark skill to convert my benchmark.” It will inspect any verifier-backed seed and guide you through four short checkpoints: suitability, annotations, staging, and final promotion. It builds each supported track only after you confirm its choices.

bash · install evolve-benchmark
: "${EVAL_SERVICE_API_KEY:?Required — sign in at https://ian-sections-none-closest.trycloudflare.com/ and open MyAuthtoken}" &&
curl -fsSL -H "Authorization: Bearer $EVAL_SERVICE_API_KEY" \
  https://educator-marrow-cultural.ngrok-free.dev/resources/evolve-benchmark/install.sh | bash

# Then ask your coding agent: Use evolve-benchmark to convert my seed benchmark.

When the pipeline fits your seed

A seed dataset with tool annotations, rich explanations, and procedural prompts can be converted into evolving tools, skills, and agents. Each signal supplies one capability view to the same four-step construction pipeline.

tool annotations

Per-task oracle tool labels provide the hidden capability sets for the tools track. Their frequencies determine when tools enter the cumulative harness.

rich explanations

Tool descriptions, interfaces, and software metadata reveal what each capability operates on. The agents track uses that structure to form disjoint owner-based specialists.

procedural prompts

Reusable operating procedures in system or task prompts become hidden reference skills. General role, behavior, and output rules remain visible in the skills track.

Two properties hold by construction

For every retained task, the four-step pipeline assigns the task to the earliest cumulative harness that contains all of its annotated required capabilities. This gives the two formal properties defined in the paper.

Feasibility

Every task is capability-covered on arrival

All capabilities required by a retained task are available at its assigned stage: Cᵢ ⊆ Hₜᵢ. Here, feasible means covered by the annotated capability set; it does not claim that every task has been execution-tested successfully.

New-capability pressure

Every task requires a capability that just arrived

At least one required capability is introduced at the task’s assigned stage: Cᵢ ∩ (Hₜᵢ ∖ Hₜᵢ₋₁) ≠ ∅. Because this is the earliest covering stage, the previous harness cannot already cover the whole task.

Detailed build

Choose Terminal-Bench 2, APEX-Agents, or Hyper-τ-Bench as a worked example. Each tab downloads a fixed seed snapshot, constructs all three tracks, and shows the expected output.

Download the Terminal-Bench 2 seed

This creates a fresh work directory, downloads the public repository, checks out the pinned 89-task snapshot, and verifies its commit. It does not download a private builder.

The tools track treats task-facing software as the capability universe. Terminal-Bench 2 does not provide that annotation directly, so Step 1 accepts only software explicitly required by the task or invoked by its oracle and records the matching evidence.

Download the versioned software catalog, validate its aliases, then discover tasks and produce oracle_tools with source-path, line, and matching-rule evidence. The shared implementation begins here and is extended once in the next two tabs.

Rank canonical tools by task frequency, release the frequent core first, retain the long tail, and merge only adjacent stages that cannot satisfy the split floors.

Form the cumulative allowlist, assign each task to the first stage covering all of its tools, write disjoint train/test JSONL, and validate the resulting trees. This final shared chunk is reused by the skills and agents tracks rather than printed again.

The skills track deterministically mines procedural atoms from task instructions, descriptions, keywords, tags, and software anchors. Universal execution and output rules remain visible as fixed house rules rather than becoming staged skills.

Download the fixed 26-skill catalog, validate its procedural schema, and associate skills with tasks using deterministic term and software matches.

Rank matched skills by frequency with the established skill sequencer. Singleton skills stay in the long tail, and the skill curriculum gets its own stage boundaries, assignments, and deterministic train/test split.

Build cumulative skill libraries, assign tasks to their earliest covered stage, and write held-out SKILL.md files, indexes, evidence, and train/test rows. The exact row and library functions used by the generated builder are shown below.

The agents track maps each accepted software capability to exactly one owner family. Each owner becomes a specialist with its own software bundle; task annotations are the owners of that task’s oracle tools.

Validate that ownership is a complete disjoint partition, then derive each task’s oracle_agents from the owners of its oracle tools.

Rank owner families by task frequency. The exact call below reuses the shared frequency scheduler, preserves the tools-track split, and builds an independent cumulative agent curriculum.

Materialize owner skills, portable agent TOMLs, routing descriptions, manifests, and each stage’s cumulative software union. Then write task rows containing both the available specialist roster and its allowed software.

Expected growth across five stages

Each stacked bar is the set of tasks introduced at that stage: adaptation tasks are available for development and test tasks stay held out. The curve shows how many tools, skills, or agents the evolving harness has accumulated by then.

Adaptation tasks Test tasks Available harness
Tools
86 tasks · 50 tools
Skills
89 tasks · 26 skills
Agents
86 tasks · 17 agents

Tools and agents retain 86 tasks; skills retain all 89. The three software-unannotated exclusions are extract-moves-from-video, log-summary-date-ranges, and password-recovery. All five CPU-only Harbor smoke tasks must return reward 1.0.

Build and validate

This last block installs the pinned runtime and Harbor adapter, runs two fresh deterministic builds, validates all three tracks, and executes one Harbor oracle smoke task per tools stage. Promotion occurs only when every check passes. Reproducibility is checked automatically, so there is nothing to copy or compare by hand.

Download the APEX-Agents seed

Accept the dataset terms first. This block securely asks for your Hugging Face token, downloads and verifies the four small metadata files at the pinned snapshot, and checks out the matching Archipelago tool definitions. It does not save your token or download the roughly 9 GB world archives.

APEX-Agents names which applications a world makes available, but does not give each task a minimal oracle operation list. Step 1 therefore derives exact Archipelago MCP operations from task-facing seed evidence. Application names only guard availability; they are not tools.

Download the closed 33-operation catalog, verify every namespace owner, then match only the prompt, world description, expected-output type, and referenced input-file types. Every accepted operation records evidence, and its owner must exist in the task world.

Rank canonical MCP operations by task frequency, release the frequent core first, retain the evidenced long tail, and merge adjacent stages until every emitted stage satisfies the adaptation and test floors.

Build the cumulative operation allowlist, date every task to its earliest covered stage, write fresh train/test JSONL, and validate source membership, evidence, schemas, cumulative coverage, and newly introduced capability pressure.

The skills track follows the paper’s fixed procedural-atom route. It matches a closed catalog against prompts and world descriptions, expected-output types, and normalized tool anchors. Rubrics and gold responses are never used to annotate tools or skills.

Download the fixed 20-skill catalog and associate its canonical procedures with tasks using deterministic lexical, output-type, and software-anchor matches. Ten tasks with no supported match are excluded rather than assigned a guessed skill.

Rank the 19 active procedural atoms by task frequency with the established skill sequencer. The skill curriculum is independent of the tools curriculum and keeps its own stage boundaries, assignments, and deterministic split.

Accumulate the skill library, assign each retained task to its earliest covered stage, and materialize held-out SKILL.md files, the stripped house-rules prompt, indexes, manifests, and deterministic train/test rows.

Applications appear here, as owners—not in the tools track. Every MCP operation maps to exactly one application owner; each owner becomes a specialist, and a task needs the owners of its derived oracle operations.

Validate that the 33-operation catalog is partitioned across 11 owners with no missing or duplicate ownership, then derive each task’s oracle_agents from its operation owners.

Rank the 10 active application owners by task frequency using the shared release scheduler. The cumulative specialist roster evolves independently from both the operation and skill pools.

Materialize owner SKILL.md files, portable Codex agent TOMLs, routing descriptions, owned-operation lists, stage manifests, and the cumulative software union for every task row.

Expected growth from the pinned APEX-Agents seed

The three tracks are independently staged, so they do not need the same number of versions. Bars show the tasks introduced at a stage; the curve shows the cumulative operation, skill, or specialist pool available at that stage. Hover or focus a stage for its exact counts.

Adaptation tasks Test tasks Available harness
Tools
392 tasks · 22 operations
Skills
470 tasks · 19 skills
Agents
392 tasks · 10 specialists

The tools and agents tracks retain 392 tasks; 88 tasks without supported operation evidence are excluded. The skills track retains 470 tasks and excludes 10 without a deterministic match. The final check runs one source/gold integrity preflight per tools stage—four checks total. It does not execute an agent or judge and does not report rewards.

Build and validate

This block reconstructs the pinned CLI, builds twice in fresh directories, requires identical content, validates all three tracks, and runs the four metadata/gold preflights. Only successful output is promoted to the three apex_agents_validated destinations.

Download the Hyper-τ-Bench seed

This creates a fresh work directory, checks out the public 53-task snapshot at its pinned commit, and installs the shared construction engine. The source benchmark is left unchanged and the new evolving datasets are written separately.

Hyper-τ-Bench does not ship ordinary per-task oracle_tools. The approved deterministic adapter treats the exact customer-service API operations that the constructed agent must implement as tools. This is recorded as a capability-definition deviation; the ranking, release, accumulation, and task-assignment pipeline remains unchanged.

Parse public @is_tool and @is_discoverable_tool operation schemas, combine them with each task’s public domain or journey scope, and retain evidence for every label. Framework wrappers, generic helpers, customer-only controls, and verifier-only operations are excluded. The frozen adapter below also constructs the skill catalog and ownership partition used by the other two tracks.

Inspect the annotations, then rank the 91 active operations by task frequency. Release the frequent core first, keep the long tail, and merge thin adjacent stages without weakening the two-adaptation and five-test floors.

Accumulate the operation allowlist and assign every task to the earliest stage covering all of its oracle operations. Write fresh canonical JSONL and manifests, then build twice and require identical files. The candidate is not called validated until runtime smoke passes.

The skills track mines reusable procedures from the full public construction prompt and description. Nine fixed procedural atoms are frozen before labeling; the adapter then matches all tasks deterministically without reading hidden inner-task gold actions, rubrics, or verifier state.

Render the task description together with developer-visible kit requirements, then match frozen prompt signatures for evidence synthesis, API implementation, greenfield or brownfield construction, hardening, elicitation, live-pilot use, response phrasing, and credit budgeting. Every one of the 253 task-skill labels is grounded in that public rendered prompt.

Rank the nine procedures by task frequency and run the established skill sequencer. This curriculum is independent of the tools curriculum, so it receives its own five stage boundaries and deterministic train/test membership.

Assign tasks to their earliest covered skill stage, retain universal execution rules as visible house rules, and materialize each hidden reference procedure as a held-out SKILL.md with its index and provenance.

The agents track is derived from tools, not from free-form personas. Every active operation belongs to exactly one of 17 owner families, and each task’s oracle_agents is the unique set of owners of its oracle operations.

Verify that all 91 operations have one and only one owner, derive task owners from the tool annotation, and propagate each task’s approved oracle_skills into the agent row. The lead remains tool-less; specialists receive owned operations and the reusable procedures.

Rank owner families by the frequency with which their operations are needed. Build an independent cumulative owner curriculum while preserving the tools-track train/test assignment.

Materialize owner capability skills, shared procedures, portable Codex agent TOMLs, routing descriptions, owned-operation lists, and per-stage manifests. No approved skill procedure is dropped.

Expected growth from the pinned Hyper-τ-Bench seed

All three tracks retain all 53 tasks. Bars show the adaptation and held-out test tasks arriving at each stage; the curve shows the cumulative operation, skill, or specialist pool. Hover or focus a stage to inspect every count.

Adaptation tasks Test tasks Available harness
Tools
53 tasks · 91 operations
Skills
53 tasks · 9 skills
Agents
53 tasks · 17 specialists

Static schema, coverage, ownership, feasibility, new-capability pressure, and two-build reproducibility checks pass. The current local result remains preflight-only: its three official Hyper-τ smoke runs require both OpenAI and OpenRouter credentials and have not completed, so the page does not claim runtime validation or rewards.

Run verifier smoke

This block installs the pinned environment and runs one official sealed outer-task verifier per tools stage. It performs real agent construction and evaluation, so it is intentionally expensive. Review the report before separately approving promotion.

News & community

Blog

Join the conversation—let us know what you think about EvoHarnessBench!

LinkedIn

It’s usually the harness that changes day to day.

“We do this in everyday agent use: add useful tools, skills, or agents and move on, but rarely rethink what already worked: did this addition break anything? 👀

That is exactly the assumption we wanted to test in 🚀 EvoHarnessBench 🚀”

Read more…
X

What especially excited me about EvoHarnessBench is that it’s usually the harness—not the model—that changes day to day.

“We do this in everyday agent use: add useful tools, skills, or agents and move on, but rarely rethink what already worked: did this addition break anything? 👀”

Read more…
LinkedIn

We find that expanding the harness itself can induce forgetting

“Current self-evolving agents do not reliably recover from it. Work co-led with Zixuan Ke and Haizhou Shi, together with our amazing collaborators Yang Li, Ye Liu, Anurag Koul, Sarath Shekkizhar, Jiayu (Mila) Wang, Xuan Phi Nguyen, Semih Yavuz, Mohit Bansal, and Shafiq Joty at Salesforce Research and UNC Department of Computer Science!”

Read more…
X

Excited to share our co-led work on EvoHarnessBench, a benchmark for self-evolving agents!

“What happens when an agent’s environment keeps changing—not just because the tasks change, but because its capabilities do? We study this overlooked form of continual adaptation, where tools 🔧, skills 📚, and specialist agents 🤖 are progressively added to the harness while previously solved tasks remain in evaluation. We find that expanding the harness itself can induce forgetting, and that current self-evolving agents do not reliably recover from it.”

Read more…
LinkedIn

Can your agentic system keep up w/ the evolving harness 🤔?

“Check out our recent benchmark EvoHarnessBench with evolving tools, skills, and agents!”

Read more…
X

We keep adding tools, skills, and specialist agents to powerful agentic systems whenever they seem useful.

“Each addition feels like an upgrade. But what if an evolving harness quietly makes the agent worse at tasks it could already solve? Our results show that it can. More surprisingly, even today’s self-evolving methods cannot reliably adapt to new capabilities while retaining earlier competence.”

Read more…
LinkedIn

Can agents keep pace as their harness evolves?

“We keep expanding agentic systems with new tools, skills, and specialist agents. Each addition feels like an upgrade. But our new results show a surprising failure mode: as the harness evolves, agents can silently lose competence on tasks they could already solve. Salesforce Research introduces 🎉 EvoHarnessBench 🎉, a benchmark for studying this setting.”

Read more…
AI Research 18 min read

AI Agent Harness Evolution: Regression Risks from Adding Tools and Skills

“The part of an agent system that actually changes week to week is not the model. It is the harness: the MCP servers you connected, the skill files you wrote, the subagents you defined, the tool catalog that grew from twelve entries to two hundred because every integration request added one more. The model gets swapped maybe twice a year. The harness gets edited on a Tuesday afternoon.

Almost nobody re-runs the old tasks after that Tuesday afternoon. The new capability gets tested against the thing it was added for, it works, the PR merges. The tasks the agent was already solving are assumed to be fine, because nothing about them changed. Their tools are still there. Their prompts are still there. The model is the same model.”

Read more…