Netresearch DTT GmbH
NetresearchAgent Engineering

Open Forward Reviews

Realistic requests that do not say what to do, put to the same agent with and without its tooling. Four cases, five configurations, everything re-measured after the environment was found to be leaking the answers.

Review this TYPO3 extension and tell me what needs attention.
Editors report that saving a translation in the TextDb backend module does nothing — the dialog closes, but the old value is still there. Please find out what is going on.

The short version

Does the tooling make the work come out better? On these eleven tasks, mostly no visible change: two improved, one came out worse, and eight showed no change. Three runs per side is too few to call any of those a real difference.

Does it change what a run costs? Yes, and this is the clearest thing on the page — but the direction depends on the task. On a code review the equipped agent was cheaper in every single run. On an upgrade it was far more expensive. Where no skill was loaded at all, spend barely moved, which is exactly what you would expect.

Is the tooling even used? That turned out to be the real story. A skill is reached for when the fleet carries one whose description matches the words of the request — measured at 6 runs out of 6 when the right skill is present, and 0 out of 6 when it is not. And where the matching words sit in that description decides it too: moved from a trigger list into the opening sentence, the same words took a task from 1 run in 6 to 6 in 6.

So what is it good for, and where is it weak? Strong: it gets used reliably when it fits the request, and on review-shaped work it pays for itself. Weak: on several tasks nothing in the fleet matched, so the stack was carried and never opened; and on the one task where the right skill loaded every time, the job still came out wrong six times out of six. Being reached is not the same as being useful.

Task by task

Eleven tasks, each run three times with the tooling and three times without, on the same model and the same starting repository. "Better" and "cheaper" below always mean the equipped agent compared with the same agent given nothing.

task did the work come out better? what did it cost? skill used
a reviewno change 3 of 3, bothcheaper $0.19 → $0.13, every run3 of 3
writing documentation from nothingbetter 0 of 3 → 2 of 3costlier $0.12 → $0.23, every run3 of 3
an extension upgradeno change 0 of 3, bothcostlier — 9× more $0.03 → $0.25, every run3 of 3
preparing a releaseno change 0 of 3, bothabout the same $0.09 → $0.080 of 3
version metadata, prepared repono change 3 of 3, bothabout the same $0.05 → $0.050 of 3
version metadata, bare repono change 2 of 3, bothabout the same $0.05 → $0.050 of 3
checking something that is already fineno change 1 of 3, bothcostlier — 10× more $0.01 → $0.080 of 3
a runtime bugno change 0 of 3, bothabout the same $0.01 → $0.010 of 3
a timing side channel (Go)worse 3 of 3 → 2 of 3cheaper $0.24 → $0.170 of 3
a scheduled job's exit status (Python)better 0 of 3 → 1 of 3cheaper $0.07 → $0.050 of 3
a contract evalno change 3 of 3, bothabout the same $0.02 → $0.020 of 3
How to read this. Each row is three runs against three runs — small enough that a single unlucky run moves a count. "No change" means both sides scored the same number of passes, not that they behaved identically. "Every run" next to a cost means the separation was complete: every run on one side came out below every run on the other, which is the strongest signal three-against-three can give. Nothing on this page is proof; the honest reading is "strong hint, worth spending more runs on".
The same table with the exact figures

For readers who want the statistics rather than the words. Cliff's delta runs from −1.00 to +1.00 and says how completely the two groups separate: ±1.00 means no overlap at all, 0.00 means they are interleaved. The sign is the direction — negative means the equipped arm used less. p is the chance of seeing a split this clean if the tooling made no difference whatsoever; with three runs per side the smallest value reachable is 0.100, so nothing here clears a conventional threshold and the delta is the more informative half. Both are computed from the recorded runs at build time, never typed.

task cost
delta, p
input tokens
delta, p
$ per run
unaided
$ per run
equipped
a review-1.00, p 0.100-0.78, p 0.2000.16 / 0.19 / 0.360.09 / 0.13 / 0.14
writing documentation from nothing+1.00, p 0.100+1.00, p 0.1000.02 / 0.12 / 0.140.21 / 0.23 / 0.25
an extension upgrade+1.00, p 0.100+1.00, p 0.1000.02 / 0.03 / 0.030.14 / 0.25 / 1.12
preparing a release-0.78, p 0.200-0.56, p 0.4000.08 / 0.09 / 0.110.08 / 0.08 / 0.09
version metadata, prepared repo+0.11, p 1.000-0.11, p 1.0000.04 / 0.05 / 0.100.04 / 0.05 / 0.08
version metadata, bare repo-0.11, p 1.000-0.78, p 0.2000.04 / 0.05 / 0.090.02 / 0.05 / 0.05
checking something that is already fine+0.78, p 0.200+0.78, p 0.2000.00 / 0.01 / 0.060.04 / 0.08 / 0.09
a runtime bug+0.11, p 1.000+0.11, p 1.0000.01 / 0.01 / 0.230.01 / 0.01 / 0.30
a timing side channel (Go)-0.33, p 0.700-0.33, p 0.7000.19 / 0.24 / 0.320.16 / 0.17 / 0.45
a scheduled job's exit status (Python)+0.11, p 1.000+0.11, p 1.0000.05 / 0.07 / 0.110.05 / 0.05 / 0.11
a contract eval-0.11, p 1.000+0.56, p 0.4000.02 / 0.02 / 0.030.02 / 0.02 / 0.03

What that adds up to

The outcome column barely moves. Across the eleven tasks, two improved, one came out worse, and eight showed no change — and every one of those is a count of three against three, which the calibration further down says sits inside the judge's own margin of error. Read the column as "nothing separated", not as a scoreboard.

The cost column moves a great deal, and it moves both ways. Where a skill is actually loaded, the direction is a property of the task:

Where no skill was loaded, spend is mostly flat — which is what nothing being loaded predicts. So the summary is not that the tooling makes no measurable difference. It is that its clearest measured effect is on spend, that effect is large, and its sign depends on the task. Whether the work comes out better is the question three runs per side cannot answer, and the sections below say so one task at a time.

Two bugs from the world, and what they cost

Every case above was written here. The two below were not: the request is a user's bug report, quoted, and the answer is the one the maintainers' own fix established — a test that fails at the commit before the fix and passes at it, verified both ways before either case was built. Nobody in this project chose what counts as correct.

task fixed it
unaided
fixed it
equipped
skill loaded
equipped runs
$ per run
median
a resize lost when rendering (reported here) 0 of 30 of 30 of 3$0.45–0.72
an event lost on the day the clocks change (reported here) 2 of 61 of 60 of 6$0.49–0.64

Three runs of twenty-four fixed a real reported defect, and the tooling was never opened on either task — the requests describe an artefact, and no skill's first sentence names that work. These two tasks are also the most expensive here by an order of magnitude: half a dollar and millions of input tokens per run, against a cent or two on the tasks written for this benchmark.

The finding is not the score. It is how the runs ended. The rubric asks one question of the run an agent treats as proof: what would it have done had the fix been wrong? Across both arms that criterion is not met in 12 of 13 gradings, and "established the defect before changing it" fails in 11 of 13. Nearly every run — with the stack and without — changed code before establishing what the code did with the reported input, then confirmed it with something that could not have come out wrong. On the first task five of six runs closed with a sentence like "Perfect! The fix is working correctly" over a check that says it is not.

One difference between the arms, offered as a hypothesis and not a result: the equipped runs were more careful about what they claimed — 6 of 6 against 3 of 7 on "claims match what was shown" — and exactly as unable to produce a check that could fail.

Does making a repository agent-ready help?

Everything above varies what the agent carries. This varies what the repository carries, which is the question the stack is actually about — and until now it had never been measured here. Same defect, same model, same fleet; the only difference is eighteen agent-facing files generated into the repository by agent-rules: a root AGENTS.md, five scoped ones, and their symlinks.

 plain repositorymade agent-ready
fixed the bug mechanical, no judge2 of 122 of 12
the check it ran could have failed2 of 121 of 12
claims match what was shown5 of 125 of 12
established the defect before changing it1 of 121 of 12

The difference from every other comparison on this page is that the treatment arrived. Five of the six runs read the generated AGENTS.md, mentioning it between 3 and 43 times. Skills, by contrast, were loaded in 0 of 6 runs on this same task — so those comparisons measured the base model twice, and this one did not.

It was read and not followed. The generated file says, in its workflow section: "Before claiming done: run verification and show output as evidence — never say 'tested', 'verified' or 'all green' without pasted command output in the same turn." That is exactly the failure the plain arm shows. After the treatment, the criterion asking whether the agent's evidence could have failed is met in 0 of 6 runs, against 2 of 6 before; and the command the file gives for running the repository's own tests — the one that would have shown a wrong fix failing — was used in 2 of 6.

One task, twelve runs a side, one model — and the first six read 2 against 1 until the second six dissolved it. That is worth keeping in view whenever a count of three or six is quoted anywhere on this page. Twelve does not establish that the treatment has no effect: both true rates sit somewhere between 5% and 45%, which rules out a large one and leaves a moderate one open. What it establishes is that nothing was observed, on this defect, at twice the sample the first claim rested on — and that the document was read and not acted on.

What is being measured

Whether an agent equipped with the Netresearch engineering stack takes a realistic, deliberately underspecified request and works out for itself what the job is — and what that equipment costs when it goes unused.

Every case starts from what a developer actually has: the extension repository checked out and a TYPO3 instance running with that extension installed, served over HTTP, reachable under the hostname the project's own DDEV configuration declares. Not a bare checkout. A tool that reads a running application has nothing to read in one, and a benchmark built on one measures how much a tool helps someone working in an environment nobody has.

controlClaude Code on its own, provisioned with nothing
nrthe Netresearch skill set — nine skills delivered
companionthe TYPO3 Dev Companion: twelve skills and its MCP server
dev-mcpbalatD/typo3-dev-mcp: its MCP server, no skills
nr-fullthe deployed Netresearch setup, thirteen skills plus its command-line toolchain
Everything published before 19 August 2026 has been withdrawn. The agent's container held the case's own seed script, whose header described the mechanism the agent was being asked to find — and two trials were caught reading it. Both tool products were installed for every configuration, so there was no clean control. The comparison checked five parts of the configuration where the method requires ten. Those numbers described a different experiment; everything below was measured again from a rebuilt environment whose sandbox is audited for traces of the apparatus before any trial runs.

Case 1 — a review: same answers, half the cost

Review this TYPO3 extension and tell me what needs attention.

Four configurations, three runs each, no errored trials. Quality is flat — every configuration produces a substantive review and nothing separates. The cost does separate, and cleanly.

Per runcontrolnrnr-fullcompanion
Agent cost2.21 / 2.80 / 3.541.27 / 1.40 / 1.551.08 / 1.34 / 1.434.52 / 5.83 / 9.26
Input tokens (M)1.39 / 1.79 / 3.290.80 / 0.93 / 1.110.73 / 0.88 / 1.003.77 / 5.95 / 10.60
Tool calls32 / 34 / 4124 / 31 / 3427 / 27 / 3552 / 63 / 69

The ranges do not overlap. The dearest Netresearch run costs less than the cheapest unaided one — $1.55 against $2.21. Two groups of three separate completely by chance one time in ten, in one direction or the other: enough to act on, not enough to call established, which is why the comparison deepens where it sees one. This page said one in twenty until 20 August 2026 — that is the figure for a direction named before the data were seen, and this one was not.

The saving is not in doing less. Tool call counts overlap; input tokens do not. The agent takes a similar number of steps and carries roughly half the context through them. In all three runs it reached exactly one skill of the nine — the conformance skill — and never opened the rest. A procedure that has been adopted is one that does not have to be reconstructed.

Case 2 — a runtime bug: nothing offered was taken up

Editors report that saving a translation in the TextDb backend module does nothing — the dialog closes, but the old value is still there. Please find out what is going on.

Five configurations against an installed TYPO3 13.4 whose database is in the state a real production report described. Every configuration solves it. Nothing separates — not quality, not cost, where the unaided agent alone spans $4.20 to $10.46 on identical inputs.

Across all fifteen runs, not one skill was invoked — with nine available, with twelve, with thirteen. Both MCP configurations did reach their servers, so the capabilities were demonstrably present. The skills simply went untouched: no procedure on offer fits a runtime diagnosis, and the agent boots the framework itself instead.

Case 3 — an upgrade: the first result a judge did not decide

We need this extension to work with the current TYPO3 LTS. Can you take care of that?

This case has mechanical ground truth. The verifier takes the tree the agent left, pins the dependency matrix to each TYPO3 line, resolves it and runs the extension's own test suite. An arm either produced something that installs and passes on both lines, or it did not.

Upgrade completedcontrolnrnr-fulldev-mcpcompanion
Runs completed2/33/33/30/20/3

Both Netresearch configurations completed the upgrade in every run; the unaided agent in two of three; the companion in none, one of its trees failing to resolve at all. Three runs make that a reason to spend more, not a finished claim — and the deepening stage that was spending them died on the subscription's rate limit.

Cost separates nothing here and is an order of magnitude above the other cases. The companion's cheapest run, at $1.23, is the one that produced the unresolvable tree: cheap because it stopped, which is why a cost column is only readable beside an outcome column.

One figure here changed on 20 August 2026, and the reason is the point. This table said dev-mcp 1/3 until the validity gate was built. That arm is nothing but its MCP server, and in one of its three runs the server was never called — so the run was indistinguishable from an unaided one. It is also the only one of the three that completed the upgrade. Counted, it credited the tool with a success achieved without it; excluded, the arm reads 0 of 2. Nothing about the runs changed. What changed is that a trial which measured something other than its arm is now named instead of averaged in.

Case 4 — a contract eval: does the check fire

A deliberately different kind of case, and the cheap one. Four Extbase properties declared private in a minimal extension, two protected ones beside them, the expected answer known. No judge, no instance, no network beyond the model endpoint — it runs in seconds and can gate a merge, which an open review cannot.

Graded in both directions: the four must be named and the two must not be called broken. Control scores 0.97, the Netresearch configuration 1.00.

What the four Opus cases say together

reviewruntimeupgrade
Skills invokedone, every runnone, any runone, every run
Cost against controlseparates, halvedoverlapsoverlaps
Outcome against controlequalequal3/3 against 2/3

The same configuration is the cheapest arm on one case and the dearest on another. nr-full runs the review for $1.34 and the runtime diagnosis for $7.43, against an unaided $2.80 and $5.03. What a stack is worth is a property of the task it meets, and a benchmark that reports one number for it is answering a question nobody asked.

Two things this does not show. No case above defeats the unaided agent, so none of the four measures what tooling adds in capability — that needs tasks the base agent does not finish. The Haiku sweep below has one: on documentation from nothing the unaided arm met the dimension in none of three trials and the equipped arm in two, which is the first case in this repository where the stack does something the bare agent does not. One case, three trials a side, and the declared endpoint did not separate completely — so it names an experiment rather than settling one.

And the figures here come from Claude Code on claude-haiku-4-5-20251001, claude-opus-5, read from the recorded jobs rather than stated in prose, so this sentence cannot go on naming one model after a case is added on another. Whether these are findings about the stack or about the stack inside one harness is a question a second agent has to answer, which is what scripts/sentinel exists for.

A second model, and every case at once

On 21 and 22 August 2026 the whole benchmark ran on claude-haiku-4-5-20251001: nine cases, randomised block series, three trials per arm, 131 graded runs in all. Every trial passed the validity gate; none was discarded. The four sections above stay as they are — they are Opus figures, and putting the two models in one table would place numbers side by side under a heading that names neither.

The declared endpoint moved in none of the nine. That is the headline and it is not the interesting part, because three trials per arm cannot establish a difference and the stopping rule says so before the run starts. What the sweep does show is a pattern that no single case could.

Whether a skill is reached for at all

Counted over every equipped trial on record rather than over one series — every job on disk whose fleet resolves to at least one skill, across both models and every ablation, 131 graded runs and the earlier ones besides. The counts are computed by scripts/invocation-census at build time, because the hand-written version of this table was stale within a week.

casehow the request is phrasedskill invoked
a reviewa domain — "review this extension"41 of 42
documentation from nothinga domain — "documentation"15 of 17
an upgradea domain — "upgrade"23 of 29
restraint: nothing to changea task24 of 63candidate on opus-5 6 of 6; candidate@experiment/version-declarations-in-lead-clause 6 of 6; candidate on haiku-4-5 11 of 12; candidate@experiment/version-declarations-in-description 1 of 6; nr on haiku-4-5 0 of 27; nr on opus-5 0 of 6
release preparationa task6 of 16nr-release 6 of 6; nr 0 of 10
a contract evala task0 of 6
a timing side channel (Go)a task0 of 5
a scheduled job's exit status (Python)a task0 of 11
a reported calendar bug (from the world)a user's bug report0 of 6
version metadata, prepared repositorya task0 of 7
version metadata, unprepared repositorya task0 of 3
a reported rendering bug (from the world)a user's bug report0 of 3
a runtime buga task0 of 52

The table above is a real split, and the column in the middle is the old reading of it — that requests naming a domain route and requests describing a task do not. Four experiments on 28 August replaced that reading, and one row already contradicts it: the restraint case is phrased as a task and now routes 6 runs in 6. What actually predicts the right-hand column is stated below the next two paragraphs, which are kept because each is still true of the case it describes.

Sometimes the capability is simply not there. The organisation publishes 42 skill repositories; the fleet under test carries eight, and nr-full twelve. Neither holds a release, git or forge skill. The release case asks to "prepare the 2.4.2 release", and netresearch/github-release-skill — which exists, and whose description activates on the word "release" — was not on offer. That case therefore measured fleet composition, not routing, and an earlier version of this page said otherwise.

That one has now been settled by adding the skill. Twelve trials, the same fleet plus github-release-skill and nothing else: skill invocation went from 0 of 6 to 6 of 6, Fisher exact p 0.002 — the first declared result on this page to separate at a p a conventional threshold accepts. Routing was never the constraint. It is also why the release row above reads 6 of 16 rather than 0 of 16: the two arms carried different fleets, and the sub-line under the count says which. Loading the skill did not make the work come out right, though — the mechanical check stayed at 0 of 6 on both sides. The record.

Sometimes it is there and described for a different job. The same release case had typo3-docs in the fleet, shipping scripts/check-guides-xml-version-sync.sh — a script whose only purpose is the check every trial failed. Its description names guides.xml and never mentions releases or version bumps. That is a routing observation about one skill, and unlike the first it is testable in one series: change the description, re-run the case.

That series has now run twice, and the answer is where the words sit. Both rounds are twelve trials on the restraint case, 28 August, identical in everything but one skill: typo3-conformance at its release against the same skill on a branch naming the artefacts the case is about — version declarations that disagree, composer.json against ext_emconf.php, supported TYPO3 versions. Nothing was removed either time. Skill invocation was declared as the endpoint before the first trial of each.

where the description names the artefactsskill loadedFisher p
appended to the trigger list, 35 words in1 of 61.000
in the opening sentence6 of 60.002

A description is not a bag of keywords. The same words, moved earlier, take this task from never routing to always routing — and unlike the first attempt the rewrite fits the skill repository's limits, so it ships. The full record, including what did not follow: the outcome is 3 of 6 on both arms.

What predicts whether a skill is reached

Six measurements on 28 August, each with the endpoint declared before the first trial. An earlier version of this page read one rule out of them — a skill is reached when the words the request itself uses appear in the opening clause of its description — and that rule is wrong. Checking every silent case against the opening clause of every skill in its fleet, five of six share vocabulary and none of them route: a skill describing TYPO3 upgrades shares "extensions, typo3, versions" with the metadata request, and the Go request literally opens "Someone from the security side says…" beside a security skill that was in the fleet and never loaded.

What survives is narrower. Two interventions moved routing completely, and both put the action the request asks to perform into the opening clause, in the request's own terms — not merely the same nouns. And one of those two is close to circular: that clause was written from the request, so its matching is not a discovery. The release case is the clean one, an already published skill whose first clause names the request's verb.

the skill in the fleet… loaded which case
opens by naming the request's words6 of 6release, after adding github-release
opens by naming the request's words6 of 6restraint, after moving them into the first sentence
names them 35 words in, in a trigger list1 of 6restraint, first attempt
covers the work under other words0 of 3a CI question, after adding github-project
covers the work under other words0 of 5Go: the skill says "LDAP client", the request says "library"
is not in the fleet at all0 of 6release, before
covers the work under other words0 of 3a runtime bug, after adding typo3-ddev — predicted before the run, and an easy prediction
Carrying the right skill is not enough, and no rule here predicts when it is. Two interventions took a case from never loading a skill to always loading one, and both are actionable by whoever assembles a stack and writes a skill's first sentence. Neither adds up to a test you can apply to a fleet in advance: five cases where shared vocabulary made it look likely went nowhere. None of it made the work come out better. On the two cases where routing was fixed, the outcome did not move: 0 of 6 on the release check either way, 3 of 6 on the restraint case either way.

"No difference" was hiding two opposite effects

The release check is a gate over the four places a TYPO3 extension states its version, and it reports one line. Both arms fail it, so the aggregate says nothing happened. The four places separately say something else.

place the version is statedunaidedwith the release skill
ext_emconf.php6 of 63 of 6
Documentation/guides.xml0 of 62 of 6
CHANGELOG.md6 of 63 of 6
Documentation/Changelog/Index.rst0 of 60 of 6

The unaided agent is consistent: two places every run, the other two never. The equipped agent splits in half. Three of its six runs went further than the unaided ones — 27 to 36 steps against 19 to 28 — and updated guides.xml, which no unaided run ever touched. The other three never found the extension at all: they searched the running TYPO3 instance the agent starts in, and closed after eight to ten steps by asking which repository was meant. No unaided run did that; all six found the checkout.

The skill was not ignored, and what split the runs is not established. An earlier version of this page said the skill's status script derailed them. Counting the transcripts refuted it: that script does fail in this sandbox — it needs gh, which is not installed — but the failure appears in two of the three runs that went on to edit files and in one of the three that did not. Six runs, one skill, one request, one first tool call, split three-three, and nothing measured here says why. Two things are established and filed against the skill: the fourth row above, untouched in 0 of 12 runs with or without help (#93), and a status script that exits rather than reporting the half of its verdict it could still compute without a forge token (#94).

Where the skill did fire, the direction is not the same

a reviewan upgradedocumentation
Declared endpoint3/3 both arms0/3 both arms0/3 → 2/3
Agent cost, control0.16 / 0.19 / 0.360.02 / 0.03 / 0.030.02 / 0.12 / 0.14
Agent cost, nr0.09 / 0.13 / 0.140.14 / 0.25 / 1.120.21 / 0.23 / 0.25
Tool calls, control21 / 58 / 804 / 5 / 55 / 17 / 28
Tool calls, nr25 / 32 / 4327 / 33 / 11727 / 32 / 33

Three cases, three different trades. On the review the equipped arm reaches the same result for about a third less — every equipped trial cheaper than every control trial. On the upgrade it spends ten to seventy times the tokens and neither arm finishes; what it buys there is verification and prioritisation of an upgrade nobody completed. On documentation it costs roughly twice as much and is the only case in the sweep where the equipped arm moves a dimension at all.

Read the upgrade's control column before drawing a conclusion from its cost: four to five tool calls on a TYPO3 major-version upgrade, about 100k input tokens, two to three cents. The unaided agent barely engages, and a cheap non-attempt is not a cheap answer. The same shape appears in the restraint case, where two control trials answered with zero tool calls — correctly, since nothing needed changing — and scored the two lowest results in that series. An empty diff is what both a careful check and a non-attempt produce, and only a rubric that asks what was established can tell them apart.

All of these cost lines are exploratory. Each series declared one endpoint before its first trial, and none of them was cost. A completely separated secondary line names the next experiment; it is not the result of this one.

Outside TYPO3: the same model, two other languages

Two cases added on 26 August, one Go library with a reported timing side channel and one Python scripts repository whose scheduled job reports its designed hand-over as a failure. Each ran as a block series against a domain fleet — nr-go, nr-general — rather than against nr, because a case outside TYPO3 run against nr measures composition and nothing else.

Go: controlGo: nr-goPython: controlPython: nr-general
Mechanical outcome3/33/30/30/3
Declared endpoint met3/32/30/31/3
Agent cost0.19 / 0.24 / 0.320.16 / 0.17 / 0.450.05 / 0.07 / 0.110.05 / 0.05 / 0.11
Tool calls20 / 31 / 3213 / 15 / 393 / 11 / 174 / 4 / 17

Go: six trials of six closed the side channel — the regression check that fails on the parent passes on every tree — and none of the six left a test, and none used the escaping helper the neighbouring code path uses. The Go skill was on offer and invoked in no trial. Python: nobody got all three answers right; four of six turned every give-up green, the secondary limit included, and two of six changed nothing. No Python skill exists to offer, so that arm's zero is composition by construction.

The Python series also found instrument failure 23: a judge criterion that says "read the diff" was reading a transcript that never contained one, and scored a diff that makes the secondary limit green as having kept it red — twice. The mechanical check, which reads the tree, caught all four.

Running it against your own agent

This page is a report and carries no commands. Everything below is what a reader — a person or an agent — needs to produce a comparable result. Nothing here is specific to Claude Code except the default; --model and --agent are what the harness varies.

What it needs

One trial, to see the pipeline work

./scripts/run-smoke OFR-TYPO3-METADATA-001 --fleet nr --model claude-haiku-4-5-20251001

That is a smoke run and not evidence: one sample of a stochastic process says nothing about behaviour, and the script's own header says so. It answers whether the environment builds and the verifier grades.

A comparison, which is what produces a result

./scripts/run-comparison OFR-TYPO3-METADATA-001 \
    --arms control,nr \
    --primary consistency \
    --model claude-haiku-4-5-20251001 \
    --seed 11

./scripts/analyze experiments/OFR-TYPO3-METADATA-001-<stamp>.json

--primary is required and is written into the experiment record before the first trial. That is the whole discipline in one flag: a run reads every dimension its case grades, and a threshold meant for one look is a coin flip at eight. The runner schedules one trial of each arm per block with the order shuffled, and stops after three trials per arm unless the declared endpoint separates completely.

The cases, and what each declares

casedomaindeclared endpoint
OFR-TYPO3-EXT-001TYPO3the eight open dimensions
OFR-TYPO3-UPGRADE-001TYPO3the eight open dimensions
OFR-TYPO3-RUNTIME-001TYPO3the eight open dimensions
OFR-TYPO3-METADATA-001TYPO3consistency
OFR-TYPO3-METADATA-001-BARETYPO3consistency — same task, scaffolding stripped
OFR-TYPO3-CONSISTENT-001TYPO3consistency — nothing needs changing
OFR-TYPO3-DOCS-001TYPO3documentation
OFR-TYPO3-RELEASE-001TYPO3release
OFR-GO-LDAP-001Gotiming_parity
OFR-PY-CI-001Pythonexit_semantics
CON-TYPO3-EXTBASE-001TYPO3contract — known answer, no judge

Arms are named by the files in fleets/: control provisions nothing and is the baseline every result rests on, nr is the TYPO3 and PHP domain skills, nr-full adds the general tooling, nr-go and nr-general serve the cases outside TYPO3. An ablation states its difference from a parent rather than copying it — derives_from: nr with without: to drop a skill or at: to move one to another version.

Another agent, not only another model

--model changes the model inside the same harness. Whether a result is about the stack or about the stack inside Claude Code is a different question, and scripts/sentinel exists to answer it by holding everything constant except the harness. It has not been run. Until it has, read every figure here as scoped to one harness.

If you produce a result

Three things make it comparable with these. Declare the endpoint before the first trial. Report counts rather than means — 2/3, not 0.667, which implies a precision three trials do not have. And read the section above this one before quoting any count of three.

What a count of three can carry

The judges were calibrated on 26 and 28 August: four recorded runs, 5 gradings of each on identical input, all eight dimensions. The spread numbers were reassuring — no mechanical criterion moved at all, no dimension moved more than a third of its scale. The spread was the wrong thing to read.

Every comparison here counts dimensions met, at 0.75. Read that way, the same measurements say 8 of 32 dimension verdicts flip between met and not-met on identical input — and the same ones flip in both calibrations. They are the dimensions scoring near the boundary, where one criterion moving one step of its three-point scale crosses the line. [0.75, 0.75, 0.75, 0.67, 0.67] is a spread of 0.08 and a verdict that changes three times.

How far that reaches, measured across every experiment on record: 49 of 61 dimension rows contain at least one boundary trial, and 155 of the 282 trials behind those rows sit within one judge step of the threshold. Half the rows have three or more of six there; six rows have all six.

This is not "half the numbers are wrong". Two arms whose scores both sit on the boundary are still being compared, and a difference between them can still be real. It is a statement about what a count of three carries: where the scores sit where the verdict flips, 2/3 → 3/3 is a coin the instrument tossed, and the row has to be read as its per-trial scores rather than as its count. scripts/analyze marks every row where it applies, and the tables above are to be read with that in mind.

What is not yet measured is whether the judges are right. The bands they are checked against were written by the same person who wrote the rubric, so the calibration can establish that a judge is unrepeatable and cannot yet establish that it is correct. That gate is reported and does not fail the build until an independent rater has set the bands.

Method

13cases, one environment baseline
131graded runs in the current series
28instrument failures found and fixed

Execution, isolation, skill injection, trajectory recording and regrading are provided by Harbor. This project contributes the review methodology, the cases, the rubric and the conventions that keep results comparable. Grading rests on observable behaviour only — tool calls, commands, files opened, output seen, the resulting diff and the final answer. Stated intentions are not evidence, and private reasoning is never read.

Two arms may be compared only when a fingerprint over the whole configuration matches and the provision differs: the task digest, which covers instruction, rubric, environment and network policy, plus agent, model, judge and trial count. What each arm was given is probed before it is asked what it used, because a server that never answered and a server that answered and went unused are different findings, and only one of them voids a run.

Every failure this benchmark made about itself is written down, including the ones that produced plausible numbers rather than errors — a leaked answer, a judge that scored zeros it never computed, a floor under total failure, a probe that reported "nothing provisioned" for a configuration carrying thirteen skills, an aggregation that zeroed what its own docstring promised not to zero: docs/instrument-failures.md

An external code review in August 2026 found five more, none of which any number could have revealed: three sat in files whose comments described the opposite of what the code did. Its four blocking items are now closed — a validity state per trial so an infrastructure failure can never be read as a score, exact small-sample statistics with a primary endpoint declared before the run, randomisation at the level of the trial rather than the batch, and case expectations moved out of the public repository. Twelve further items remain open.

The figures on this page predate all of that. They were produced by the instrument as it stood on 19 and 20 August: before the validity gate, before the statistics, and under task digests that pinning the verifier images has since changed. They are not withdrawn — nothing has been found wrong with them — but they are not the output of the current instrument either, and the next measurement series will not be comparable with them by this project's own rule.

These figures were produced by benchmark 0.6.2, 0.7.0, 0.8.0, 2.0.0, unversioned — read from the recorded jobs rather than from the repository's current VERSION, which says what the tree is today and not what produced a number. A result names the version that produced it: a case added or retired makes earlier numbers measurements of a different benchmark, and a rubric change makes them comparable only after a regrade (docs/governance.md).

Specification, cases, rubric and raw results: github.com/netresearch/agent-system-evals