Start the server
The environment is an MCP server over stdio, in Python 3.12 standard library only, and it makes no network connection. One server process is one run: pick a variant and a new folder for the record.
Head Gasket Bench 0.1 · Part two of the engine benchmark
Engine Bench asks an AI agent to build a real 1990s 1.6 L SOHC inline-four. Head Gasket Bench asks it to repair the same engine: replace the head gasket through 18 tools, then let a scorer read the saved record.
One benchmark family, with Engine Bench and ToolsEnabled Bench. Each keeps its own tasks and scoring.
Head Gasket Bench 0.1 · An MCP server · Python 3.12 standard library only
The question
Not describe one. Do one, step by step, in an order the engine allows.
The engine is on a service stand. Along the way the agent records the bench preparations with prepare_condition (engine cold, battery isolated, coolant drained, engine supported and others): some removals need them, and the timing marks can be recorded only after the timing has been set and checked aligned. It has to set the engine to top dead centre, strip the belt, cover and accessories in a valid order, loosen and lift the head in stages, clean and measure the mating faces, decide what to do about bolts and a possibly warped head, fit the right gasket, torque the head in two stages in the right order, restore the timing, put every part and attachment back, and call finish.
Nothing physical is simulated. The environment is a deterministic procedure game over 1,255 named engine bodies. A perfect score means the recorded procedure satisfied the written rules; it does not mean a real engine would run.
How it works
The agent works through tools. The scorer never watches it work; it reads the record afterwards.
The environment is an MCP server over stdio, in Python 3.12 standard library only, and it makes no network connection. One server process is one run: pick a variant and a new folder for the record.
Look, inspect, measure, search the service information, remove, install, clean, replace, torque, check the timing and more. Each run has 1,024 tool-call attempts, refused calls included, and a refused call is charged penalty time.
Every call the environment answers, including a refused one, goes into a hash-chained record. The scorer checks it against twelve written checkpoints worth 18 points.
Any record replays bit for bit under the pinned source and the CPython version that made it. Verification proves that the recorded tool inputs reproduce the recorded score under that exact source. It cannot prove which model made the calls, how long it really took, or what it cost.
A standard replacement, a stretched head bolt, a warped head, a wrong gasket on the shelf, and a worn timing belt.
look View engine parts, named surface IDs and the job clockinspect Inspect an accessible component or surface (ID from look)measure Measure flatness, bolt_stretch, bolt_free_length, roughness, gasket_bore or gasket_thicknesssearch_service_info Look up procedures, specifications and service diagrams (14 cited entries: 12 service entries and 2 benchmark-policy entries)remove Withdraw an accessible, released part to the service trayinstall Fit one body from the tray or supply; timing phases in degreesclean Clean a face or prepare a removed fastener (plastic_scraper, solvent, abrasive_disc, thread_brush)replace Exchange a removed body for one distinct supply bodytorque One fastener operation: tighten with value_N_m, or loosen with a negative angle_degcheck_timing Turn the crank 1-6 complete revolutions and inspect the timing marksparts_bin List available replacement stock and part markingsmachine_head Send the cleaned, removed head for the configured bench machining serviceset_timing Set crank and cam positions before disassemblytension_belt Complete the timing-belt tensioning operationprepare_condition Perform a named preparation step from the removal policyrelease_attachment Release a named attachment after its prerequisites are metrestore_attachment Restore a released attachment after reassemblyfinish End the job and receive a compact score summaryRefusals name the argument or predicate that failed, and some name a missing prerequisite step. None gives a torque value, the tightening sequence or the correct stock part.
Process checkpoints are judged on the order of events; final-state checkpoints on the final state. The full wording is in CHECKPOINTS.md.
The aggregate formulas are adopted from TheAgentCompany. For earned points p out of 18:
S_full = 1 if every checkpoint passes, otherwise 0 S_partial = 0.5 × (p / 18) + 0.5 × S_full
Bolt accuracy, typed mistakes and estimated technician seconds (productive and penalty) are reported beside the score. Technician seconds are analyst estimates, not measured repair time, and they never change the points.
October 2026 study · round 2
The only two full repairs (18/18) in round 2 came from Fleet with one lead and four workers.
Mean points out of 18: Fleet 1+4, 12.75; Fleet 1+2×2, 11.5; Codex native subagents, 7.5; single agent, 5.75.
Sixteen runs, four per setup, on one deterministic V1 task in Head Gasket Bench 0.1.3, with enforced lead-only finish.
All runs requested gpt-6-luna at max reasoning effort through Codex CLI 0.162.1; requested model labels do not independently attest served weights.
The benchmark environment and independent scorer agreed on all 16 round-2 scores.
Four repetitions of one task cannot rank the arms.
Team size, delegation instructions, context packaging and tool surfaces differ together, so these configurations do not isolate the effect of transport.
Round 1 had no full repairs in eight runs; FB2 scored 9/18 in the environment and 8/18 with the independent scorer because of a CP07 disagreement.
The rounds use different finish protocols and remain separate exploratory cohorts.
A full repair here means all twelve benchmark checkpoints passed; it does not establish physical repair competence.
Round 2 report · all 16 runsRound 1 report · all eight runsClaim sources
Earlier results
Three exploratory runs, one per model, on the standard job. In the paper’s own words:
In exploratory runs, one per model on the standard job, no agent completed the repair fully correctly. GPT-6-Astra reached the highest partial score, 41.7% (15 of 18 checkpoint points), with all ten head bolts tightened correctly and 1,404 seconds of penalty time. GPT-6-Luna scored 36.1%. GPT-6.1-Sol scored 8.3% and left the engine disassembled. The most common failures were repeated work, repeating requests the environment had refused, and finishing with parts not refitted. These are single runs; they describe behaviour and do not rank the models.
18/18
Scripted reference, 13,630 s productive. Not a model run.
15/18
GPT-6-Astra, 41.7%, 1,404 s penalty.
13/18
GPT-6-Luna, 36.1%, 14,970 s penalty.
3/18
GPT-6.1-Sol, 8.3%, 12,012 s penalty.
Table 1 of the paper. Each model run went through Codex CLI on the standard job (V1). With one run per model, these figures describe individual runs; they are not estimates of model performance.
A low-cost baseline, outside the paper: three runs of claude-haiku-5-5 through Claude Code, with only the 18 job tools and the server started through tools/claude-code-proxy.py, finished with 14, 12 and 12 of 18 points. They are on the board below. They replace the three 0/18 rows of release 0.1.1, which are withdrawn: Claude Code had kept most of look’s result from the model, so those runs measured the client, not the model.
Also outside the paper, with the same harness: five runs of claude-opus-5-5 at effort max each finished with 16 of 18 points in the earlier release-0.1.2 cohort. None completed the job correctly; the scripted correct job scores 18 of 18. All five lost CP12 (reassembly complete) the same way: the agent fitted the new head gasket from supply with install instead of exchanging the old gasket for it with replace, so the environment counted the gasket’s position as not refitted and refused every restore_attachment, and no released attachment was restored. They are on the board below.
The paper
The paper describes the environment, the five variants, the twelve checkpoints and the scoring, the specifications and where sources disagree, the three exploratory runs and their most common failures, and what the numbers cannot show.
Expected paper.pdf SHA-256: 58a1af794123fbb17a623ecf03eac16342db9c19a8fa5b1724e286e446925824
Test your agent
Run it on your own computer with any local (stdio) MCP client and your own keys or subscription.
1Install
Get the repository. The environment is in env/. Nothing else to install: no pip install, no compiler, no browser. Check python3 --version: if it is not 3.12.x, write python3.12 wherever these steps say python3. The steps use /ABS/REPO for your clone, /ABS/RUNS for a folder for run records outside the clone, and /ABS/WORK for an empty folder outside the clone where the agent’s client runs.
git clone https://github.com/ToolsEnabled/head-gasket-bench.git /ABS/REPO
cd /ABS/REPO/env
python3 -m headgasket pins --check # verifies the 19 pinned source files and the 5 configurations
python3 -m headgasket tools # prints the 18 tools2Connect your MCP client
One server process is one run: pick a variant and a new folder for the record (or an empty folder of yours that other users cannot write to). Use absolute paths; MCP clients start servers from a working directory you do not control. A start that makes no tool call, such as a client’s health check (claude mcp list) or a session you quit before the agent called a tool, records nothing, so the folder stays free for the run; if a client kills the server before it can clean up, the next start clears what is left. Once the agent has made a call, the folder holds that run, and the next run needs a new folder.
python3 /ABS/REPO/env/bin/headgasket-mcp serve --variant V1 --out /ABS/RUNS/v1-first-tryThese are examples of the wiring, not a recommendation of any client or model.
Claude Code
Run both commands in /ABS/WORK, an empty folder outside the clone: claude mcp add registers the server only for the folder it is run in, and --tools "" turns Claude Code’s own tools off, so the agent has only the 18 job tools.
mkdir -p /ABS/WORK && cd /ABS/WORK
claude mcp add headgasket -- python3 /ABS/REPO/tools/claude-code-proxy.py python3 /ABS/REPO/env/bin/headgasket-mcp serve --variant V1 --out /ABS/RUNS/v1-first-try
MAX_MCP_OUTPUT_TOKENS=150000 claude --tools "" --allowedTools mcp__headgasketor in /ABS/WORK/.mcp.json:
{
"mcpServers": {
"headgasket": {
"command": "python3",
"args": ["/ABS/REPO/tools/claude-code-proxy.py", "python3",
"/ABS/REPO/env/bin/headgasket-mcp", "serve", "--variant", "V1",
"--out", "/ABS/RUNS/v1-first-try"]
}
}
}look returns about 196,000 characters. Claude Code saves an MCP tool result that is larger than the size the tool declares to a file and shows the model only a short preview, and it cuts results above MAX_MCP_OUTPUT_TOKENS (25,000 tokens by default). So start the server through tools/claude-code-proxy.py, which adds that size declaration to each tool and passes every message through unchanged, and start Claude Code with MAX_MCP_OUTPUT_TOKENS=150000. Other clients may cut long results too; check yours before a run.
Keep the agent to the job tools. The repository holds complete correct solutions, so an agent that can read files could copy one. Start the client in an empty folder outside the repository with its own file and shell tools turned off, as the Claude Code commands above do, and say how in your SUBMISSION.json notes. The README shows the full headless command the claude-haiku-5-5 baseline ran.
Codex CLI (~/.codex/config.toml)
[mcp_servers.headgasket]
command = "python3"
args = ["/ABS/REPO/env/bin/headgasket-mcp", "serve", "--variant", "V1",
"--out", "/ABS/RUNS/v1-first-try"]
startup_timeout_sec = 60
tool_timeout_sec = 600This file is global, so every Codex session starts the server: remove the entry, or change --out, after the run.
Any other stdio MCP client
Start the same command and exchange one JSON object per line on stdin and stdout (MCP protocol version 2025-11-25). The minimal session:
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-11-25"}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":1,"method":"tools/list"}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"look","arguments":{}}}3Run the job
The environment carries no prompt; your client supplies one. Record the prompt you used with your run. The server writes the record after every call and exits when the client closes its input. When turns_remaining reaches 0, stop: the launcher answers any further call with the budget refusal and does not record it, so the record stops at 1,024 attempts. It also answers, without recording it, a remove naming a route_id that an installed part does not have (the pinned environment would fail every later call), and lists the part’s routes.
Replace the failed head gasket on the prepared service-stand engine and restore the assembly and timing. Use the headgasket tools to inspect, look up service information and do the work. End with finish within the work budget. Every job-tool call attempt, including a refused action, uses one environment turn. The remaining budget is shown in tool responses; if it is exhausted, end your response. Each successful action adds estimated technician time. Use only the supplied job tools.
4Validate
Copy run.json, actions.jsonl and report.json into a folder runs/submitted/<name>/, add your SUBMISSION.json, and let the repository’s validator check the files, your declaration, secrets and personal paths, the hash chain and the report, that the run is not an exact copy of a published or submitted run (a near copy is marked for the maintainer’s review), the pinned configuration, an exact replay and the independent scorer. Then rebuild the board. A run that stopped without finish is scored as it stands.
Release 0.1.2 corrects the independent scorer’s CP02 to count refused attempts as the paper defines it and the environment decides it, so a run that the 0.1.1 validator refused only because the two disagreed on CP02 now passes (release notes).
cd /ABS/REPO
mkdir -p runs/submitted/<name>
cp /ABS/RUNS/v1-first-try/{run.json,actions.jsonl,report.json} runs/submitted/<name>/
# add runs/submitted/<name>/SUBMISSION.json (SUBMISSIONS.md shows it)
python3 tools/validate_submission.py --write-result runs/submitted/<name>
python3 tools/validate_submission.py runs/submitted/<name>
python3 tools/build_board.py5Submit
Open a pull request that adds runs/submitted/<name>/ with your run’s record files and a SUBMISSION.json that states the model label, the variant, the client, the date and the submitter (shown on the board), and the rebuilt leaderboard/board.json. A CI validator, run from the main branch’s code, replays the record and rescores it, and a maintainer reviews every submission by hand before merging it. Accepted runs appear on the board with the model label you state. ToolsEnabled cannot verify which model served a run. The steps are in SUBMISSIONS.md.
The board
Submitted runs are listed after the repository’s validator replays and rescores them. Exploratory runs and the scripted reference are listed apart.
| Run | Client | Variant | Points (of 18) | S_partial | S_full | Productive s | Penalty s | Date | Record |
|---|---|---|---|---|---|---|---|---|---|
Scripted referenceThe environment’s scripted correct job. Not a model run. | |||||||||
| Scripted referenceNo model | n/a | V1 | 18 | 100.0% | 1 | 13,630 | 0 | 2026-10-10 | Record |
Submitted runsReplayed and rescored by the repository’s validator. | |||||||||
| No submitted runs yet. | |||||||||
Exploratory runsSingle runs. They describe behaviour and do not rank the models. | |||||||||
| claude-opus-5-5 | Claude Code (effort max) | V1 | 16 | 44.4% | 0 | 12,050 | 360 | 2026-10-14 | Results |
| claude-opus-5-5 | Claude Code (effort max) | V1 | 16 | 44.4% | 0 | 11,690 | 390 | 2026-10-14 | Results |
| claude-opus-5-5 | Claude Code (effort max) | V1 | 16 | 44.4% | 0 | 12,750 | 510 | 2026-10-14 | Results |
| claude-opus-5-5 | Claude Code (effort max) | V1 | 16 | 44.4% | 0 | 11,600 | 210 | 2026-10-14 | Results |
| claude-opus-5-5 | Claude Code (effort max) | V1 | 16 | 44.4% | 0 | 12,100 | 180 | 2026-10-14 | Results |
| GPT-6-Astra | Codex CLI | V1 | 15 | 41.7% | 0 | 16,190 | 1,404 | 2026-10-10 | Results |
| claude-haiku-5-5 | Claude Code | V1 | 14 | 38.9% | 0 | 13,952 | 3,228 | 2026-10-14 | Results |
| GPT-6-Luna | Codex CLI | V1 | 13 | 36.1% | 0 | 15,230 | 14,970 | 2026-10-10 | Results |
| claude-haiku-5-5 | Claude Code | V1 | 12 | 33.3% | 0 | 13,976 | 4,602 | 2026-10-14 | Results |
| claude-haiku-5-5 | Claude Code | V1 | 12 | 33.3% | 0 | 12,956 | 3,888 | 2026-10-14 | Results |
| GPT-6.1-Sol | Codex CLI | V1 | 3 | 8.3% | 0 | 13,280 | 12,012 | 2026-10-10 | Results |
The model label is the submitter’s statement; ToolsEnabled cannot verify which model served a run. Of the model runs on the board, only GPT-6.1-Sol stopped without finish; it is scored as it stands. Six earlier claude-haiku-5-5 runs, in which the client kept most of look’s result from the model, are not listed (the three 0.1.1 rows are withdrawn). Three claude-opus-5-5 runs are not listed either: two hit the same client limit (1 and 0 of 18), and one was stopped by ToolsEnabled before it finished and left no record. The five claude-opus-5-5 rows are five separate runs of the same model and setting. A record’s replay shows that its tool inputs reproduce its score; it does not show how long the run took or what it cost. The rows come from leaderboard/board.json.
Open by default
runs/submitted/.The fine print, plainly
The environment is a research game, not qualified instructions for repairing a real engine. Technician seconds are analyst estimates, not measured labour.
The server cannot stop a client from using other tools, reading files or calling networks. Isolating the agent is the job of the client and the person running it.
The environment and the scorer are MIT, copyright ToolsEnabled, Inc. The paper’s text and figures are CC BY 4.0. Service-source material keeps the terms of its publishers. Model and client names belong to their owners.
Report a security problem privately through the repository’s Security tab or to security@toolsenabled.ai; see the security policy and this site’s policy. What a run writes and sends is on the privacy page.
Read the code, replay the scripted records, check the model runs’ published scores, and send your own run. The model runs’ journals are withheld; their hashes are published.
Current release: Head Gasket Bench 0.1.3, commit 837f15c6ade8462f83fe34b218784e3dd198a048 · Release notes · Security policy · Privacy
Questions: GitHub issues or support@toolsenabled.ai