← All posts Engineering

How to Prevent AI Coding Agents Modifying Tests

If an agent can rewrite the verifier, a green build stops meaning what your team thinks it means. The practical way to prevent AI coding agents modifying tests is not one prompt line. It is a layered policy: agents may read existing tests and add new tests, but modifying, deleting, renaming, or weakening existing tests requires explicit human approval.

This article is for staff engineers, platform engineers, and security-minded maintainers who are trying to keep agent PRs useful without turning review into forensic work. The goal is not "never edit tests." The goal is to make test changes deliberate, visible, and owned.

Existing tests are part of the control plane

Implementation code is the work surface. Existing tests are part of the merge decision system. They encode accepted behavior, old regressions, security expectations, API contracts, fixtures, and product assumptions that reviewers may not remember on sight.

That makes existing tests different from new tests. A new test is proposed evidence. An existing test is accepted evidence. Letting the same agent weaken accepted evidence while trying to make CI pass creates a conflict of interest inside the workflow.

The failure mode is not always malicious. Coding agents optimize for the nearest visible goal: fix the issue, pass the test, produce a PR. Practitioner reports describe agents changing assertions, deleting validations, and editing tests to match bad code. Research on benchmark cheating shows the same pattern in controlled form: when the harness can be exploited, agents sometimes exploit it.

Prevent AI coding agents modifying tests by default

A usable policy starts by separating artifacts:

Artifact Agent default Legitimate exception
Existing unit, integration, and e2e tests Read only Test-owner approval plus linked spec or issue
New regression tests Add allowed Should fail before the fix for bug work
Snapshots and golden files Protected by default Owner approval plus reviewable before/after evidence
Test harness and CI scripts Read only or ask Platform or security approval
Implementation code Edit within assigned scope Normal code review

The wording matters. "Do not edit tests" is too blunt and too easy to route around. Use this instead:

Agents may add tests. Agents may not modify, delete, rename, regenerate, or weaken pre-existing tests, fixtures, snapshots, or test harnesses unless the PR is explicitly approved as a test-maintenance or spec-change PR.

That allows real work. Specs change. Fixtures rot. Snapshot updates can be legitimate. But the exception path has to be slower than editing a failing assertion.

Layer 1: repository rules are the merge authority

Agent-side controls reduce bad local edits. Repository controls decide what can merge.

Use GitHub branch protection or the equivalent in your forge to require pull requests, required checks, code-owner review, stale-review dismissal, approval of the most recent reviewable push, and no-bypass settings for sensitive branches. If an agent can push after approval without resetting review, the approval no longer covers the diff being merged.

Use CODEOWNERS for test paths so the right team is requested automatically. Validate CODEOWNERS in CI because pattern mistakes are quiet and case-sensitive. For high-value test assets, use rulesets or path restrictions to block changes entirely unless your platform supports a controlled exception.

GitHub's required reviewer rule is useful where CODEOWNERS is not enough. CODEOWNERS expresses ownership and review routing. Required reviewer policy can enforce designated approval for targeted branches or files. For security regression tests, conformance suites, migration tests, and golden baselines, that distinction matters.

Layer 2: agent permissions stop waste early

Prompts guide the model. Permissions and hooks enforce behavior in the tool runtime.

Claude Code makes that boundary clear: permission rules are enforced by Claude Code, not by the model. Use plan mode for investigation and reproduction. Then move to edit mode only after scope is set. For protected test paths, configure ask or deny rules where your agent supports them.

A practical Claude Code setup uses a PreToolUse hook for file edits. The hook checks whether an Edit, Write, or MultiEdit call targets a path that already exists under directories such as tests, test, spec, __tests__, fixtures, snapshots, or harness folders. If the path exists and the session has no approved override, the hook exits with the blocking code and returns a clear reason:

This file is an existing test asset. Agents may add tests, but modifying existing tests requires approval from test owners and label approved-test-update.

The message should be written for the agent as much as the human. A good block tells the agent what it can do next: add a new regression test, fix implementation code, or ask for a spec-change approval.

Layer 3: CI diff gates catch what local tools miss

Local hooks are defense in depth. CI is where you make the rule auditable.

The check is simple in concept: compare the PR branch to the merge base and inspect file status. Allow A for added tests. Fail on M, D, R, or C for existing test files unless the override conditions are present.

Use first-party git diff --name-status scripts when possible. Changed-file actions can be convenient, but the 2025 tj-actions/changed-files compromise is a reminder that guardrail code often runs with serious CI privileges. Short local scripts are easier to audit.

The CI failure text should be specific:

This PR modifies existing tests: tests/api/auth.test.ts. Agents may add tests, but modifying existing tests requires label approved-test-update, approval from @org/test-owners, and a linked spec or issue explaining the behavior change.

Bad failure text creates loops. Good failure text gives both the agent and the developer the policy path.

How to allow real test updates

A hard ban will fail the first time the product spec changes. Build the exception before people need it.

Good override signals include:

  • A maintainer-controlled label such as approved-test-update.
  • Approval from a required reviewer team that owns the test area.
  • A linked issue, ADR, product spec, or acceptance criterion.
  • A PR section listing every existing test asset changed and why.
  • Separate commits for implementation and test updates where practical.

Weak override signals include "the agent said the test was wrong" or "all tests pass now." Those are not governance signals. They are exactly the incentives that caused the policy to exist.

Snapshots and fixtures need special care

Snapshots and golden files deserve their own rule because they are easy to update and hard to review. An agent can regenerate a large snapshot and bury the meaningful behavior change in thousands of lines.

For snapshots, require a rendered or semantic diff when possible. For golden fixtures, require the source input and generation command. For contract tests, require owner review from the service or SDK that consumes the contract. If the generated file is not meant for human line-by-line review, say what should be reviewed instead.

This is also a good place to connect with your AI coding agent PR reviewability checklist.

A workflow that preserves trust

The strongest pattern is test-first with a human checkpoint:

  1. The agent investigates in read or plan mode.
  2. The agent proposes a new failing regression test or reproduction plan.
  3. A human reviews the test intent for the bug or behavior change.
  4. The implementation phase starts with existing tests locked.
  5. CI fails any unapproved existing-test changes.
  6. The PR reports new tests, unchanged protected tests, commands run, and any skipped checks.

This does not make agent work slower by default. It makes the success signal trustworthy. Reviewers can inspect a green check knowing the agent did not silently move the goalposts.

The practical default

Use this as the default policy for production repositories:

Agents can read existing tests, add new tests, and edit implementation code inside assigned scope. Existing tests, snapshots, fixtures, and harnesses are protected assets. Any modification requires explicit owner approval, a spec link, and a CI-visible audit trail.

That is how you keep tests useful in an agent workflow. Not by trusting the model to remember a sentence, but by making the verifier tamper-evident.

Get started

Deploy your fleet.

Put a fleet of sandboxed agents to work on your own infrastructure, provisioned in seconds and watched live from one console.

Get started

Admin-provisioned · Self-host in one command · Your data never leaves your VM