Harness published — no results yetThe apparatus is built and reproducible. It has not been run here, so there are no measurements on this page and none are implied.

Original research

A Reproducible Benchmark Harness for Coding Agents

Three tasks that fail before an agent runs and pass after, a runner that measures the diff as well as the result, and a schema that refuses a record without a version. No numbers yet.

The question

On identical tasks in identical starting repositories, how do coding agents compare — not on whether they produce something plausible, but on whether a test suite that failed before passes after, and at what cost in unrelated changes?

Written by
The Copilot Stack Editorial Team
Published
Last verified
Reading time
5 min

Environment

  • Python 3.11+ with pytest
  • Harness: projects/copilot-agent-benchmark/ in this repository
  • Three tasks: decimal-rounding, sql-injection, pagination-off-by-one
  • Agent profiles: GitHub Copilot CLI, Copilot CLI with a selected model, Claude Code
/research/agent-benchmark/

Why most agent comparisons are not evidence

Almost every published comparison of coding agents shares three properties. The tasks are described but not shipped, so nobody can run them. The scoring is a judgement of output quality, so nobody can check it. And the versions are not recorded, so by the time you read it the thing being described no longer exists.

The result is a genre of article that reads like measurement and functions as opinion. This is an attempt at the other thing.

What makes a task a measurement

Each task is a small repository with a failing test suite. The agent is given a prompt and the repository. Afterwards the same suite runs again.

That gives one number that is not a proxy for anything: the tests failed, and now they pass, or they do not. No rubric, no similarity to a reference solution, no scoring of how idiomatic the code looks. There is more than one correct answer to every one of these problems, and a rubric that rewards resemblance to ours would measure conformity.

Two properties are enforced before a task counts, by harness/verify_tasks.py:

  1. The tests must fail on the starting repository. A task whose tests already pass scores every agent a point for doing nothing. This is a very easy mistake to make and completely invisible afterwards.
  2. The tests must pass with the reference solution. A task nothing can satisfy scores zero for everybody and looks like a hard problem rather than a broken one.

Both checks are run on the three tasks below, and both pass.

The three tasks

TaskCategoryThe defectWhy this one
decimal-roundingCorrectnessInvoice totals accumulate in float and use round(), so some totals are a cent out and 0.125 rounds downThe wrong code looks exactly like the right code, and the failure is a customer query rather than a crash
sql-injectionSecurityTwo queries built with % formattingTests assert the input arrived as a bound parameter, not that the SQL string looks a particular way — so patching the symptom does not pass
pagination-off-by-oneCorrectnessPages are 0-indexed against a 1-indexed contract and the page count truncatesHas a property test: every record must appear exactly once across all pages

Each ships as repo/ (the starting state), tests/ (the verifier) and solution/ (a reference fix used only by the task verifier, never shown to an agent).

What is measured besides pass and fail

Passing is not the whole story, and the columns that are usually missing are the interesting ones.

MetricWhy it earns a column
unnecessary_filesFiles changed outside the task’s allowed_paths. tests/ is never allowed — an agent that edits the test suite to make it pass has not solved the task, and this is how that is caught rather than hoped against
lines_added / lines_removedA four-hundred-line answer to a six-line problem is a finding, even when it passes
lint_ok, build_okPassing tests while breaking the build is a specific and common failure
wall_secondsMedian rather than mean, because one timeout should not become the headline
timed_outA recorded outcome, not a missing row
interventionsEntered by the operator; see below

What the harness refuses to do

The failure mode of a benchmark is not a wrong number. It is a plausible number nobody checked. So the runner stops rather than warns:

  • No agent version, no result. The version command runs first and its output is stored verbatim. A record without one is not reproducible.
  • --no-container marks the run unpublishable. It exists for development and the flag is recorded in the output file, so a convenient run cannot quietly become a published one.
  • A task that passes before the agent runs aborts the run, rather than scoring a point.
  • A missing agent is a recorded skip with a reason, not an omitted row. A comparison table missing a row has quietly answered a different question.

This is also the model benchmark

Running one agent across several selectable models uses the same tasks, the same runner and the same scorer — the model is a field on the agent profile. That is why there is one repository rather than two, and why harness/agents.yaml already carries a profile that pins a model.

Getting a number out of it

git clone <this repository>
cd projects/copilot-agent-benchmark

python3 harness/verify_tasks.py            # the tasks are still valid measurements
python3 harness/runner.py --agent copilot-cli --task all --out results/run-$(date +%F)
python3 harness/score.py results/run-$(date +%F) --format markdown

If you run this and get numbers, they are yours to publish. If you publish them and they disagree with what we eventually publish, that disagreement is the most useful thing either of us could produce — open an issue with both result files.

The rules this follows

Every study here follows the same set, written down once at research methodology: what the three status labels mean, what a page must state before it publishes, and how to reproduce or dispute a result.

What would change this page

When a run happens, this page gains a results table, a date, the versions tested and an environment description, and its status changes from harness to measured. Until then the absence of numbers here is not an omission. It is the finding: nobody has run this comparison in a way you can check, including us.

Limitations

What this cannot tell you. Stated in the frontmatter so it cannot be dropped in an edit.

  • No results are published on this page. Running the benchmark needs paid accounts for several products under test, and none of them is available in the environment where this site is built. Nothing here implies a measurement.
  • Three tasks is a harness, not a benchmark. Three small, well-specified bugs can distinguish an agent that reads a failing test from one that does not; they cannot rank two agents that both pass all three.
  • Every task is Python and every task is a bug fix. Nothing here measures writing a feature, working across services, or any language where the agent's training data is thinner.
  • Tasks are seeded, which means they are solvable by construction and the tests state exactly what correct means. Real work rarely arrives with a failing test attached.
  • The intervention count is entered by the operator, not measured. It is the only field on the record that depends on somebody being honest, and it is marked as unrecorded rather than zero when nobody answered.
  • Agents change weekly. Any result is a measurement of specific versions on a specific date, and the runner refuses to write a record that does not name them.

Reproduce this

Clone projects/copilot-agent-benchmark/, install the agent you want to test and authenticate it, then run `python3 harness/verify_tasks.py` to confirm the tasks still fail before and pass after, and `python3 harness/runner.py --agent <profile> --task all --out results/run-$(date +%F)`. Aggregate with `python3 harness/score.py results/run-<date> --format markdown`. Adding an agent means adding a command template to harness/agents.yaml, never editing the runner.

Sources

Every version-sensitive claim on this page was checked against first-party documentation. Only sources actually used are listed.

Primary sources

Cite this research

Free to use, republish and build on. A link back is appreciated and is not a condition. The one thing we ask is that theverification date travels with the numbers — several of these figures change monthly, and a table with no date on it becomes misinformation within a quarter however accurate it was when copied.

Plain citation

The Copilot Stack. "A Reproducible Benchmark Harness for Coding Agents." Version 2026-09-07. https://thecopilotstack.com/research/agent-benchmark/ (verified 2026-09-07).

Markdown

[The Copilot Stack — A Reproducible Benchmark Harness for Coding Agents](https://thecopilotstack.com/research/agent-benchmark/) (verified 2026-09-07)
BibTeX
@misc{copilotstack_research-agent-benchmark_2026,
  title        = {A Reproducible Benchmark Harness for Coding Agents},
  author       = {{The Copilot Stack}},
  year         = {2026},
  note         = {Version 2026-09-07. Maintained research, not peer reviewed},
  howpublished = {\url{https://thecopilotstack.com/research/agent-benchmark/}},
  urldate      = {2026-09-07}
}

@misc rather than @article or@dataset, and the note records that this is a maintained research that has not been peer reviewed. Formatting it as a journal reference would imply a review process that did not happen.

Licence. Free to reuse for any purpose, including commercially. This is original work by The Copilot Stack — the method, the harness and the results — and it has not been peer reviewed.

All research