Head Gasket Bench 0.1 · Part two of the engine benchmark

One engine.
Now repair it.

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

Illustration of a head gasket with four bores and ten bolt holes beside a twelve-item checklist

The question

Can an AI agent do a head gasket job?

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

One server. One run.
One saved record.

The agent works through tools. The scorer never watches it work; it reads the record afterwards.

01

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.

02

Do the job with 18 tools

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.

03

Score the record

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.

04

Replay and verify

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.

05

Five jobs

A standard replacement, a stretched head bolt, a warped head, a wrong gasket on the shelf, and a worn timing belt.

The 18 tools
  • look View engine parts, named surface IDs and the job clock
  • inspect Inspect an accessible component or surface (ID from look)
  • measure Measure flatness, bolt_stretch, bolt_free_length, roughness, gasket_bore or gasket_thickness
  • search_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 tray
  • install Fit one body from the tray or supply; timing phases in degrees
  • clean Clean a face or prepare a removed fastener (plastic_scraper, solvent, abrasive_disc, thread_brush)
  • replace Exchange a removed body for one distinct supply body
  • torque One fastener operation: tighten with value_N_m, or loosen with a negative angle_deg
  • check_timing Turn the crank 1-6 complete revolutions and inspect the timing marks
  • parts_bin List available replacement stock and part markings
  • machine_head Send the cleaned, removed head for the configured bench machining service
  • set_timing Set crank and cam positions before disassembly
  • tension_belt Complete the timing-belt tensioning operation
  • prepare_condition Perform a named preparation step from the removal policy
  • release_attachment Release a named attachment after its prerequisites are met
  • restore_attachment Restore a released attachment after reassembly
  • finish End the job and receive a compact score summary

Refusals 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.

The five variants
  • V1. Standard head-gasket replacement.
  • V2. One stretched head bolt (position 3, 0.2 mm).
  • V3. Head exceeds the flatness limit (machine it or fit the replacement head).
  • V4. Incorrect gasket stock is also in the bin (78 mm / 1.2 mm beside the correct 75 mm / 0.762 mm MLS).
  • V5. Timing belt is worn (a new belt is in the bin).
The twelve checkpoints
  1. CP01, 1 point. Torque values, both torque stages and the tightening sequence looked up before the first head bolt was tightened.
  2. CP02, 2 points. Disassembly followed the documented removal policy. Any refused attempt to remove a part that was still blocked or still attached fails it.
  3. CP03, 1 point. Cylinder 1 at top dead centre with the timing marks aligned, confirmed by a timing check, before the belt came off.
  4. CP04, 1 point. Head bolts loosened in a documented order and in the documented stages before the head was removed.
  5. CP05, 1 point. Both mating surfaces cleaned before the new gasket was placed, and clean in the final state.
  6. CP06, 1 point. Head and block flatness measured against the service limit before the head went back on, with the right decision.
  7. CP07, 1 point. Every head bolt measured against the reuse limit before it was kept or replaced, with the right decision.
  8. CP08, 2 points. A new head gasket of the correct specification installed in the correct orientation on aligned dowels.
  9. CP09, 2 points. All ten head bolts carry a valid stage-1 torque, applied in the specified sequence.
  10. CP10, 2 points. All ten head bolts carry a valid final-stage torque, applied in the specified sequence after stage 1.
  11. CP11, 2 points. Timing restored: belt fitted and tensioned, marks aligned, confirmed by a timing check after the belt went back on.
  12. CP12, 2 points. Reassembly complete: every removed part and every released fastener or attachment restored.

Process checkpoints are judged on the order of events; final-state checkpoints on the final state. The full wording is in CHECKPOINTS.md.

How the score works

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

Two 18/18 repairs with Fleet 1+4.

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.

View the results chart on Fleet

Earlier results

The paper and earlier exploratory runs.

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.

From the abstract of Evaluating AI Agents on a Head-Gasket Repair Procedure. Model names are as requested from the provider at xhigh reasoning effort; the serving system did not attest the model identity.

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

Evaluating AI Agents on a Head-Gasket Repair Procedure

October 2026 · 64 pages · CC BY 4.0

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

Install. Connect. Run.
Validate. Submit.

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 tools

2Connect 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-try

These 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__headgasket

or 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 = 600

This 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.

The prompt the claude-haiku-5-5 baseline used (the scripted controls need none)
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.py

5Submit

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

Every published run, as recorded.

Submitted runs are listed after the repository’s validator replays and rescores them. Exploratory runs and the scripted reference are listed apart.

One row per run. Model labels are as stated, not verified. Points, S_partial and S_full are each run’s recorded score, checked against the independent scorer; technician seconds are analyst estimates.
RunClientVariantPoints (of 18)S_partialS_fullProductive sPenalty sDateRecord
Scripted referenceThe environment’s scripted correct job. Not a model run.
Scripted referenceNo modeln/aV118100.0%113,63002026-10-10Record
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-5Claude Code (effort max)V11644.4%012,0503602026-10-14Results
claude-opus-5-5Claude Code (effort max)V11644.4%011,6903902026-10-14Results
claude-opus-5-5Claude Code (effort max)V11644.4%012,7505102026-10-14Results
claude-opus-5-5Claude Code (effort max)V11644.4%011,6002102026-10-14Results
claude-opus-5-5Claude Code (effort max)V11644.4%012,1001802026-10-14Results
GPT-6-AstraCodex CLIV11541.7%016,1901,4042026-10-10Results
claude-haiku-5-5Claude CodeV11438.9%013,9523,2282026-10-14Results
GPT-6-LunaCodex CLIV11336.1%015,23014,9702026-10-10Results
claude-haiku-5-5Claude CodeV11233.3%013,9764,6022026-10-14Results
claude-haiku-5-5Claude CodeV11233.3%012,9563,8882026-10-14Results
GPT-6.1-SolCodex CLIV138.3%013,28012,0122026-10-10Results

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

What is public, and what is not.

Public

  • env/ The environment, its five variant configurations and their pins, and its tests.
  • scorer/ The independent scorer: twelve checkpoints, 18 points.
  • runs/ 13 scripted cases, the scripted reference, and eleven exploratory model runs as scored results: the paper’s three GPT runs, a three-run claude-haiku-5-5 baseline and five claude-opus-5-5 runs.
  • paper/ The paper, with selected supporting data.
  • leaderboard/ The board’s rows, and every accepted submission under runs/submitted/.

Not public

  • Agent traces. The model sessions behind the exploratory runs (their messages, reasoning and client logs) and their per-action tool-call journals are not published. Their folders carry the scored results.
  • Service source documents. The service information paraphrases public guidance and cites its sources by ID. No source document is shipped.

The fine print, plainly

Know what a score means.

Not repair instructions

The environment is a research game, not qualified instructions for repairing a real engine. Technician seconds are analyst estimates, not measured labour.

Not a sandbox

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.

Licences

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.

Open source.
Check every published record.

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