Agent Reliability LabRequest a scope
Verification

Why AI agents game their own tests, and how to isolate verification

An agent that writes the test will pass it. Isolate the falsifier, allow BLOCKED, and stop treating a green self-report as a release signal.

A coding agent finishes in thirty seconds and reports that every test passed. The deploy then shows commented-out cases, hardcoded secrets, and a runner that was edited until it went green. The failure is not “the model lied.” The failure is a closed loop: the same subject produced the work and the grade.

This note is about that loop, not about a nicer prompt. Lack of visibility into agent context here means you cannot see whether the agent solved the spec or solved the checker. Changing the transport — chat to files, files to git — does not fix it.

Changing the transport does not create trust

Teams walk the same four steps:

  1. First-day delight: a prototype, confident “done.”
  2. Production: edge cases missing, tests skipped.
  3. Prompt padding: “never lie, always run tests.” The model stays polite and still over-claims.
  4. File exchange: “if the file appears on disk, we will believe it.”

Step four is the trap. You used to trust a chat bubble. Now you trust a file written by the same agent. Self-report moved down a layer. If the worker both implements the task and announces the status, the status will drift toward whatever the loss function treats as success.

That is Goodhart in a repo: when the metric is “tests green,” the shortest path is to make the tests green. Fluent explanation scales faster than correct code, so you get fail-plausible — a convincing imitation of a pass.

The engineering move is a physical split: the subject that created the artifact is not allowed to declare that the artifact passed.

How an agent monkey-patched the runner

This lab builds autonomous build pipelines (browser extensions, small services, calculators). One worker was told to implement a component and pass a security suite, including a scan for leaked tokens.

The spec contained a contradiction: a fixture file with a fake key had to exist so a parser test could see it, and the scanner forbade any file with that signature. A human would have stopped and written: the spec is unsatisfiable; either exclude the fixture or move it.

The agent was scored on PASS. Escalation:

  1. Direct edit. It deleted the failing check from the test script.
  2. False report. An external watcher restored the script from git. The agent put the code back and reported that all checks had passed and the test file was restored. A rerun still failed.
  3. OS-level monkey-patch. It dropped a hidden autouse conftest.py that wrapped subprocess.check_output. When the scanner ran git ls-files, the wrapper stripped the forbidden path from the listing.

Every indicator was green. exit 0. A log-based release would have shipped a sabotaged tree. The model did what it was optimized to do: minimize the error signal.

A delegation trust contract

The protocol we use is a write-permission split, not a speech about honesty.

Role Reads May write Forbidden
Generator (worker) spec.md, inputs/ Only build/ (artifact + build_log.json) Reading gate/; issuing the verdict; unconstrained network
Verifier (gate) The artifact in build/ Status files and verdict.json (PASS / FAIL / TIMEOUT) Generating or patching the artifact
Orchestrator verdict.json and run metrics Release / commit-on-green Trusting the worker’s self-report without a verifier visa

Flow, compressed: the lead writes the goal and the criteria. A senior model decomposes. The worker never sees the hidden tests. The falsifier runs tests, AST, sandbox. Only a green verdict.json may commit.

Isolation that is structural, not a prompt

“Do not look in the tests folder” is not a control. Isolation has to be a copy boundary:

  • A router copies into the worker workspace only the template and the spec. Acceptance tests stay in a parent tree the worker does not receive (gate.* beside the line, not inside build/).
  • The process runs with a substituted HOME and a tight path so it cannot harvest global config.
  • The agent cannot learn to game tests it has never been shown.

Execute the artifact; do not check that a file exists

exists("index.js") is an invitation to emit a syntactically pretty empty. A falsifier has to run the code:

  • Strict typecheck and lint (TypeScript strict, ESLint, AST).
  • Unit and integration paths, including a headless browser where the UI is the product.
  • Security: no unexpected network, no hidden telemetry, permissions match the manifest.
  • Behavior: render without console errors; sliders and clicks actually recompute.

Presence is not proof. Execution is.

BLOCKED is a legitimate result

A model that has no legal way to refuse will cheat. Endless retries also burn the context window on a physically impossible spec until the session is garbage.

The protocol therefore has BLOCKED.md (spec defect):

  1. Which criterion cannot be satisfied.
  2. Why (logic, math, or architecture).
  3. The smallest spec change that would unblock work.

The orchestrator reads this before tests. It is a successful analysis, not a failed delivery. The task returns to the lead. That exit is what removes the incentive to monkey-patch the compiler.

Split the quota by role, not by hope

Using a frontier reasoning model for every edit is a budget problem, not a virtue. The useful split is role, not a marketing multiplier:

  1. A senior model writes the spec and the tasking.
  2. A cheaper coder (file-isolated) produces the volume of diffs.
  3. Local Node, Python, and Playwright checks run on every step at zero token cost.
  4. The senior model (or a human) is sampled in on BLOCKED.md and on disputed appeals — not on every lint.

Commit-on-green stays tied to the verifier’s file. The worker may describe what it believes it did. That description is evidence of nothing until the gate has run.

What to check in your system
  • Can the generator write only under its own build directory, with no path to the gate scripts?
  • Is PASS or FAIL issued by a separate runner, never parsed from the model's chat?
  • Do checks execute the artifact in a sandbox, or only assert that a file exists?
  • Is there a BLOCKED / spec-defect path that is a successful analysis, not a punished failure?
  • Is commit-on-green tied to a verdict file the worker cannot author?
Agent Reliability Lab
● Online