← Blog

'Done' Is a Claim, Not a Fact

An agent can report "done" with total confidence and no necessary relationship to whether the work is finished. That claim needs a structural check, not a reminder.

I run a set of AI agents that build and maintain software projects, and every defect, bug, or new requirement they find becomes a row in a SQLite ledger, not a bullet in a document. That distinction turned out to matter because of one closure record: P39-0005.

What shipped green

P39-0005 was a defect in the installer for another one of my agents. A commit had added two new placeholder tokens — one inside a launchd plist file, one inside a deploy script — without adding a substitution site for either. The rendering function that was supposed to fill in real values only knew about two older, different placeholders; it had never been taught about the new ones.

The bug shipped green, because the existing test asserted that one specific, already-known placeholder was gone from the rendered output. It never checked for the two new ones. A passing test suite and a broken installer coexisted for as long as nobody looked for the right thing.

What closing actually requires

Why shouldn't the agent that wrote the fix be the one to verify it?

Because the agent that just finished writing the fix already believes the fix is correct — that belief formed while writing it, so there's no independent frame left to check it against. A verifier who is also the author isn't verifying anything; they're re-reading their own conclusion and agreeing with it.

The fix was written by a Sonnet subagent. Before that report becomes a closed row, the tracker requires a different verifier and an evidence row of kind TEST or REPRO. Both rules are database constraints.

RESOLVED CLOSE GATEverifier ≠ fixerevidence row existsboth required CLOSED REFUSED: return for review
The close is a gated transition: missing independence or evidence sends the issue back instead of recording a false completion.

The record

Here is what the row for P39-0005 actually holds, read from the live ledger:

resolved_by          p39-plist-fix-sonnet-subagent
verified_by          claude-opus-5-orchestrator
closed_by            claude-opus-5-orchestrator
root_cause  commit ddccdae added two placeholders
  (__DEPLOY_DIR__ in the plist ProgramArguments,
  __FACTORY_ROOT__ in deploy/run_daemon.sh) with no
  substitution site for either; _render_plist only
  handled the two older placeholders. It shipped
  green because the test asserted just one named
  placeholder was absent.
evidence (TEST)  RED/GREEN probe: git checkout HEAD --
  src/pehredaar/cli.py -> pytest
  tests/test_launchd_integration.py = 2 failed
  (leaked placeholders); cli.py restored -> 3 passed

The verifier is a different identity than the fixer. verification_method records what was run: a diff stat, a real pytest execution, and a RED/GREEN probe.

Why the RED/GREEN probe is the point

What evidence should be required before closing an issue?

Not a sentence claiming testing happened, but a record of what was actually run — ideally a RED/GREEN probe: revert the fix, confirm the suite fails in exactly the way the bug predicts, then restore the fix and confirm it passes. A single passing run after a fix only tells you today's code works; a RED/GREEN probe tells you the test would have caught the original bug.

A single passing run only says today's code works. The RED/GREEN probe is falsifiable: it records the predicted failure before the fix and the passing result after it, showing that the test would have caught the original bug.

SUMMARY“I tested it.”gate cannot inspect the claim EVIDENCE ROWcommand + expected failureobserved failure + restored passgate can inspect the record
A summary is a claim; an evidence row is a falsifiable record the closure gate can evaluate.

The aggregate, and the gap I won't hide

P39-0005 isn't an outlier I picked because it's flattering.

557closed issues in the ledger
544name a verifier different from the fixer
0self-verified

That ratio isn't a norm the agents choose; it's what the schema permits. An agent that wants to self-close cannot make the insert succeed.

The honest part: 13 of those 557 closures carry a verified_by with no resolved_by recorded at all — a data-completeness gap, not a self-verification one, but a gap. It's tracked as its own row, P0-0002, opened during work adjacent to P39-0005 itself. Its root cause is specific: the check that blocks an unresolved issue from closing was added at the close step, but the equivalent guard was never added at the verify step — so a row can reach a verified state attesting to work with no recorded fixer, the same failure the rule exists to prevent, just with the fixer field left empty instead of matching the verifier. That defect is still open. I'm naming it here rather than fixing it first, because a ledger that only reports the numbers making it look complete isn't a ledger, it's marketing.

What to copy

How do I stop an AI agent claiming work is done when it isn't?

Don't rely on the agent remembering to check its own work — make a false "done" claim structurally impossible to record. A closure should require a different verifying identity than the one that did the fix, plus an actual evidence row from a real test run rather than a sentence saying it was tested, enforced by the database rejecting any write that skips either.

"Done" is what someone says before the work is checked. Whether it's true is a separate fact, and the only way I've found to keep those two things from collapsing into each other is to make the system refuse to record the second without evidence for it.

Meharban Singh

Meharban Singh

AI systems / delivery architect. I build software with AI agents governed by rules, hooks, gates and independent review — and watch it in production after go-live.