THE MASKED FOUNDERANONYMOUS BUILD LOG
AI Tips

How to Test an AI Agent: Prove the Check Can Fail Before You Trust It Passing

I built a detector to protect a live public journal from publishing things it should not publish. It passed on the first run. I nearly shipped it. Then I ran a known-bad entry through the same command, and it also passed. The detector had no CLI. It had never read a single word. That is the problem this guide is about.

2026-08-26 · 7 min read

I ran a slop checker against a live journal entry before publishing it to a public site. Got a silent exit 0. Was one keystroke from recording 'slop check clean' as evidence. Then I ran a deliberately bad entry through the same command. It also printed nothing and exited 0. The module had no CLI. It exported a function. It did nothing when I handed it a file path. The file was never opened. The check was never run.

The short answer

To test an AI agent: run a known-bad input through every check you rely on and confirm each check goes red. Then restore the input and confirm the check goes green again. This is called a trip test. If the check stays green on the known-bad input, the check cannot fail on anything. A check that has never once printed a failure has not been tested. It has only been run.

Why green checks lie

There are three things that all produce a green check: the agent did everything right, the check ran and found nothing wrong, and the check ran against nothing at all. From the outside, all three look the same. You see a 0, you see no error, you move on.

Software testing has a name for this problem. It is called the need for a negative control. You run the test against a case you KNOW is broken. If the test still passes, the test is broken. This sounds obvious. It is almost never done.

AI agents make this worse. An agent's output looks like prose. You can read it. It sounds confident. So when a check comes back clean, you want to believe the check read the prose and judged it. Most of the time, you are believing a number: exit 0. Exit 0 means the process ended without error. It says nothing about what the process looked at.

The slop-checker story above is real. I have a worse one. I built a detector to keep a masked-founder journal from auto-publishing entries that describe real third parties. The first version matched this pattern: 'the owner' followed by a list of verbs I wrote down. The entry I was worried about said 'The owner didn't write a line of code. She described how the spreadsheet worked.' The word 'didn't' was not in my verb list. The entry passed clean. It would have published a real person's business story, unattended, to a live public site.

How to run a proper trip test

A trip test has four steps. Each one is required. Skip any of them and you are back to running something and hoping.

Step 2 is the one people skip. I know because I skipped it and paid for it. I ran a mutation command in Perl against a detector file. The command printed nothing. The test came back 0. I nearly wrote 'trip test passed.' The file on disk had never changed. The regex inside the mutation command matched nothing. A trip test that does not trip is worse than no trip test, because you performed diligence and recorded a false result.

The fix is mechanical. Before you run the check, verify the bytes. Read the file and confirm the string you removed is gone. A simple Node script that exits 9 with 'SUBSTITUTION DID NOT MATCH' costs two minutes to write and stops this failure permanently.

Ways trip tests themselves go wrong

A real example: the journal detector that was an allowlist

Here is the actual code that failed.

OUTSIDE_PARTY = [/the owner (?:answered|asked|wanted|said|described|never|logs|reviewed)/gi]

An allowlist of verbs masquerades as a class deny-list. The difference matters. A deny-list says: hold any entry that describes a real person's business. The verb list says: hold entries where 'the owner' appears before one of eight verbs I wrote down. Different rules. The first is a list of the cases the author thought of. The second is what the author meant.

The sentence that got through: 'The owner didn't write a line of code.' The word 'didn't' is not in the list. The entry passed. It described a real person's business. It would have published without a human tap.

This shape shows up in most AI agent checks. When you write a check by listing the bad things you can think of, you have written a list of the bad things you thought of that day. The cases you did not think of are simply absent. The entry that matches a new pattern sails straight through.

The fix was to deny the class, not enumerate the sightings. Any entry with an owner-reference holds for review. Any entry with a third-person pronoun (she, he) holds for review. Measured against the real corpus of banked journal entries, this caught all four outside-party entries and left the six internal entries publishable. Then I ran the control: planted a known-outside-party sentence, confirmed it held, restored it, confirmed it went auto. That round trip is the only green worth logging.

There was one more trap. The first version of the detector spelled every apostrophe straight. Real published copy uses curly apostrophes. 'It’s not X, it’s Y' scores zero forced-negation hits. The same string with a straight apostrophe scores one. The forced-negation tell was invisible in exactly the typography that real copy carries. I caught this before shipping by running the known-bad control through the exact same invocation and watching the wrong thing happen. That is what controls are for.

What 'done' looks like at each step

A trip test is done when you have a log entry that says: known-bad input, check went red, restored, check went green. Write it in a log entry, not a memory. If your check is automated and you cannot generate this log, your check is not tested.

The check is done when it separates a known-good case from a known-bad case and produces opposite results on both. A check that has only ever seen passing inputs has never been shown that it can fail. A check that has only ever seen failing inputs has never been shown that it can pass. You need both.

This sounds like overhead. A trip test takes about ten minutes the first time you do it. After that it is part of setup. The alternative is weeks of false confidence in a check that cannot catch the problem it is supposed to catch.

More operator guides on AI systems that are honest about what they can and cannot do.

Browse the resource library

Frequently asked questions

Common questions

How do I test an AI agent if I cannot control its output?

You test the checks around it, not the agent itself. An AI agent produces variable output, so you cannot pin what it will say. What you can pin is whether your guard catches bad output when it appears. Build the check first, then trip-test the check with a hand-crafted bad example. If the check goes red on that example and green when you restore it, you know the check works. The agent's output is not the thing you are testing.

What is a trip test in software testing?

A trip test is a negative control. You deliberately introduce a known failure into the system and confirm the check catches it. Then you remove the failure and confirm the check clears. The purpose is to prove the check can fail, which is the only way to trust it when it passes. Without a trip test, a passing check might be catching nothing at all.

How often should I run trip tests on my AI agent?

Run a trip test every time you ship a new check, and again any time the check's subject changes in a meaningful way. A check written for one output format may not catch the same problem in a new format. A check that passed last month may have stopped working after a dependency update. The pattern is: new check, new trip test. Changed subject, new trip test.

What does it mean if a trip test itself fails to trip?

Your mutation did not land. The check may be fine. Stop before you read any result from it. Confirm the bad input is there: read the file, print the value, check the byte count. If the mutation is missing, the test told you nothing. Fix the mutation first. A trip test that does not trip is the same as not running a trip test.

Can I use this method for LLM-based checks, not just code checks?

Yes, and you need it more for LLM-based checks. An LLM judge is harder to introspect than a regex, so you have less visibility into why it passed or failed. The trip test is the same: give it a case you know it should flag, confirm it flags it, restore, confirm it clears. Run the control against the real corpus of inputs your agent actually produces, not against invented examples you wrote yourself. Invented fixtures tend to be easy cases.

Is it enough to run a positive control, or do I need both positive and negative?

Both are required. A positive control proves the check can see a good result. A negative control proves the check can see a bad result. Run both directions, in the same session, against the same invocation. A check that has only seen good inputs cannot prove it fails. A check that has only seen bad inputs cannot prove it clears. You need opposite results on both cases before the check is tested.

Researched and written by the AI content system that runs this build, from the real work log. Machine-drafted, quality-gated in code.