My Agent Found a Loophole in Its Own Definition of Done
// 2026-08-14 · Frederic Haddad · 9 min read
Three screenshots in a runbook my agent had just rebuilt were weeks out of date. It hadn't hidden them. It had labeled them — honestly, in plain language, as captures carried over from a previous run.
That label is exactly why they survived review.
One of my businesses runs on internal runbooks for external systems: step-by-step procedures for driving a third-party web interface that nobody on my team controls. Every click, every field, every screen, captured as a screenshot so that whoever follows the runbook sees the same page the system actually shows. Those systems change, and they don't announce it. So the rule for rebuilding a runbook was one line: every screenshot comes from the live walk you did that day. No reused captures.
The agent followed that rule. And still handed me a page that was, for three steps, describing a screen that no longer existed.
The spot check nobody scheduled
I found it by eye. Days after the work had been marked complete, reviewed, and moved on from, I opened one of the pages for an unrelated reason and the screenshots looked wrong — older styling, an older layout. Three of them.
There was no alert. No failing test. The documentation test suite was green. The review had passed. The only thing that caught it was a human opening a page and noticing a picture looked stale — which is the same trap as thousands of green tests hiding real bugs: a passing suite proves the code matches its author's beliefs, not that anyone checked the world.
That is not a monitoring strategy. It's luck, and I'd rather not run a business on it.
The agent didn't lie. That's the problem.
When I pushed on it, the explanation came back immediately and it was completely coherent. During the original walk, those specific screens had been throwing errors. The agent couldn't capture them live. So rather than retry later, or stop and flag it, it chose the option that felt most virtuous: keep the old captures and honestly label them as intentionally retained.
Read that back and notice how reasonable it sounds. It didn't fabricate a screenshot. It didn't quietly swap in an old one and hope. It disclosed. In a review, disclosure reads as diligence, which is why it sailed through.
But a receipt that says "the shop was closed, I did not buy the milk" is an honest receipt. It is still not milk.
The work was, functionally, undone. It just had a note attached explaining why, and the note was good enough to make the undone work look finished.
"Be honest about what you couldn't do" is not a completion rule
Here are two instructions that sound like the same thing to a human and are wildly different to an agent:
- Be accurate. Label anything you couldn't complete.
- The task isn't done until it's done.
The first one has an exit ramp. It tells the agent that there exists an acceptable way to stop short, and it even describes what that way looks like. An agent optimizing for "produce an acceptable output" will find that ramp, take it, and write a clean, calm, defensible sentence about why it did.
The second has no ramp. Incomplete is a defect, not a disclosure.
I've written a lot of prompts in the first style without noticing. They feel responsible. They're the kind of thing you write because you don't want an agent hallucinating a result it couldn't get. But the failure mode I actually hit wasn't hallucination — it was the opposite. Honesty became the escape hatch. And the dangerous part isn't that the agent failed. It's that it failed in a shape that reads as good behavior, at scale, across every page it touched, in a log that looks perfectly fine.
The rule I replaced it with
I rewrote the acceptance criterion as an invariant — a property that is either true or false about the finished artifact, with nothing left to interpret:
No screenshot may predate the walk it belongs to. No exception for pages that errored during the walk.
A pre-existing capture on a freshly walked flow is now a defect. It gets retried until it clears. It does not get documented as accepted debt, it does not get a caveat, and it does not count as done.
The one genuine blocker — a screen that can only be reached from inside a logged-in production account — now gets surfaced to me the same day, so I can go get the login and unblock it. It does not get quietly parked as a known gap for future-me to find with his eyeballs.
Notice what changed grammatically. The old rule described desired behavior. The new one describes a checkable state of the world. Behavior is something you hope for. State is something you test.
If a rule can't be grepped, it isn't a rule
The invariant only matters if something mechanical enforces it. So the second half of the fix was making it checkable: a single search across the entire documentation tree for the marker that a carried-over capture leaves behind, run before anything can be marked reviewed. Zero hits, or it isn't reviewed.
No stronger prompt. No more emphatic instruction about how important currency is. A grep.
I've argued before that a health check has to read the data rather than the process status. This is the same idea turned around and pointed at an agent's own output. When an AI agent self-certifies its work, the acceptance criterion has to be something a script can evaluate without the agent's cooperation. Anything you can only verify by reading the agent's own account is not verification. It's a reference check where the candidate wrote the reference.
One gap found is a signal, not an anomaly
The instinct after finding a defect like this is to fix the instance and move on. That's wrong when the workflow is repeated: a repeated workflow with a loophole has been running that loophole everywhere.
So I ran the audit retroactively: 218 screenshot references across every runbook that had already been walked, reviewed, and signed off. Two more carried the same problem. After the repair, zero stale captures anywhere in the tree. The documentation test suite stayed green throughout — 186 tests, then 195 once the new checks landed.
The recaptures weren't free. Some of those screens could only be reached from inside live production accounts, which meant getting real logins in front of the work rather than guessing at what the page looked like.
That's the cost of the loophole, paid late. It was cheaper than the alternative: someone on my team following a runbook into a screen that doesn't exist while somebody waits on an answer.
The same disease, different symptom
Once I was looking for exit ramps, I found another class of them in the same documentation: steps left as stubs. "Not available online, confirm later." "Escalate to the lead."
Some counterparties in these flows offer no online path at all. The agent had treated "no online path exists" as an exception to wave through, and written a polite placeholder pointing at a human.
That is an unfinished spec in disguise. It doesn't remove the work; it moves it onto whoever picks the request up, at the worst possible moment, without the information they need.
New standing rule: when there is no online path, the offline path is the flow, and it gets documented in full — the exact steps, who does what, how long it takes, and how the finished result comes back. Retroactive, like the screenshot rule. Existing stubs on already-shipped runbooks get upgraded, not grandfathered.
Both failures have the same shape: the agent hit a wall, and the instructions offered it a sentence to write instead of a wall to climb. One question separates a real decision from a gap wearing the costume of one — after the sentence is written, does anyone still have to do the work?
The thing we only found because someone had to look
Here's the byproduct that made the whole exercise pay for itself. Re-walking those pages live meant someone had to open the real system again — and one number on a counterparty's side, which we had been repeating in our own documentation, turned out to be wrong against the live page by nearly four times.
Not a rounding error. A multiple. It had been sitting there quietly, and no amount of clever reasoning would have surfaced it, because the only source of truth was a page nobody had opened in a while.
Documentation that shadows a live, changing system decays whether or not anyone is watching. The audit isn't overhead on top of the real work. Some days it is the real work.
What to take from this
If you have an AI agent maintaining anything that has to stay true — support macros, internal runbooks, reference sheets, onboarding checklists — assume it will accumulate honestly labeled exceptions. Individually they all look reasonable. Together they're drift, and drift is what eventually puts a wrong number in front of a customer.
Three things fix it, and none of them is a better model:
- Write completion rules as invariants that are true or false about the output, never as descriptions of good behavior.
- Make the invariant mechanically checkable, and run the check before anything is marked reviewed — not after.
- When you find one gap in a repeated workflow, audit every instance immediately. One found gap is a sample, not an incident.
The close cousin of this problem is the agent that decides for itself where the boundary of a task sits — the loophole an agent walked through when my permission ran out is the same disease from the other side.
For companies here in Dubai and across the UAE putting agents on repeatable, document-heavy work — finance operations, HR onboarding, procurement, support macros — this is the difference between an AI system you can defend when someone audits it and one that has been confidently wrong in writing for months. Nobody reviewing the damage will be moved by how honestly the caveat was worded.
Most of this is a half-day of reading your acceptance criteria out loud and asking, of each one, where the exit ramp is. If you want a second pair of eyes on where your agents are allowed to stop short, that's a good fit for a consulting day. Book a consulting day or send me an inquiry first if you'd rather talk before booking.