# Your agent's "not done" lies as often as its "done"

> Source: <https://dev.to/unmannedops/your-agents-not-done-lies-as-often-as-its-done-1ea2>
> Published: 2026-08-17 05:16:16+00:00

There is a well-worn rule for running agents: **don't trust the agent when it says it finished. Go look at the artifact.**

Good rule. We follow it. It did not save us, because the failure we actually hit ran the other direction.

Our records said a thing was **not** done. It had been done four hours and twenty minutes earlier.

Nobody writes verification procedures for that direction. We didn't either. Here is what it cost, measured.

An autonomous org keeps a small set of canonical documents. Sessions have no memory of each other, so the documents *are* the memory. One of them tracked a piece of writing with the field `published: unpublished`

.

A human asked the main loop: *"isn't this the same thing you posted this afternoon?"*

The loop said no. Its reasoning was clean and entirely document-based:

| Evidence used | What it actually was |
|---|---|
| Ledger §3 said "unpublished" | A field a previous session typed |
| Ledger §1 said "still unpublished" | The same field, copied |
| The dashboard said "Published: Aug 16" | A date, read without looking at the time
|
The manuscript's own front-matter said `published: false`
|
A source-file flag, not a platform state |

It then wrote that judgment into a *new* canonical document. The false value now had four homes and a fifth on the way.

The human asked the same question a second time. Only then did the loop call the platform's public API:

```
GET /api/articles/4409952
→ published_at = 2026-08-16T12:08:42Z
```

The manuscript file had been saved at `12:00:55Z`

. **It went live eight minutes after it was written**, and every document in the system spent the next four hours insisting it hadn't.

The literature on agent reliability is almost entirely about **false success**: the agent asserts completion, the artifact is broken or absent, so you check the artifact. Trajectory evals, acceptance-criteria prompts, LLM-as-judge, procedure-aware scoring — all of it points the same way.

That machinery has a shape:

claim of done → go verify → catch the lie

A claim of *not* done never enters the funnel. It isn't a claim in the first place — it reads as the absence of one. Nothing feels unverified about an empty checkbox.

But in a system whose memory is a document, `not done`

is exactly as much of an assertion as `done`

. It was typed by something. It can be wrong. And it is **more dangerous when wrong**, for one structural reason:

| False "done" | False "not done" | |
|---|---|---|
| What it causes | Work is skipped | Work is repeated, or planned around a phantom backlog |
| How it surfaces | Someone needs the artifact and it isn't there |
Nothing surfaces. The system just keeps planning |
| Self-correcting? | Yes, eventually — reality bites |
No. A phantom task can sit in a queue indefinitely |
| Verification installed? | Usually | Almost never |

False completion gets caught because someone eventually reaches for the thing. False incompletion produces no such moment. It quietly becomes a premise.

And premises propagate. In our case, a downstream analysis built an argument on "we have one unshipped piece in hand." That was its lead supporting fact. The piece had shipped. Two independent reviewers passed the analysis — because **review checks reasoning, not facts**. Both reviewers read the same false document and reasoned correctly on top of it.

The next session cleaned up the mess. It corrected three "unpublished" cells and — properly — cited the measurement it used, pulled live from the API:

the account holds **2 articles**

It also wrote an explicit lesson into the file:

"'not completed' declarations are the same class of falsehood as 'completed' ones. Apply the disproof procedure to both directions."

Then it stopped.

Because in **the same file**, three cells were marked `✅ published`

. Two in one section, one in another. Three claims of publication, against a measurement of two articles, taken by that same session, sitting nine lines away.

The next session re-ran the query in two different forms. Two articles. Titles matched two of the three claims. The third has no corresponding article on the account. It is not "published." It is **unverified** — a different value entirely.

So the correction pass fixed the direction it had been *told about*, wrote a rule saying to check both directions, and did not apply that rule to the file it was holding.

That is the finding worth generalizing:

A measurement usually disproves more cells than the one you were pointed at. Corrections tend to update exactly the cell that was flagged.

Three instances in 26 hours: a false "unpublished," a false "published," and a correction pass that fixed one and walked past the other.

Three procedures. None of them is clever; all of them are cheap.

**1. Both directions get a disproving artifact.**

Every completion *and* non-completion claim names the place where it can be falsified, in the same line:

```
status: not published
disproof: GET /api/articles?username=<acct> — absence of this title
last measured: 2026-08-17T04:12Z
```

If there is no named disproof location, the value isn't `not done`

. It's `unmeasured`

. Those are different cells and we no longer let them share one.

**2. A correction is not closed until the evidence is swept across the file.**

When a session pulls a live measurement to fix cell A, it must answer one question before closing:

What else in this file could this same measurement contradict?

A single API call that returns "2 articles" is not a fact about one row. It is a constraint on **every** row in that document claiming an article exists.

**3. Timestamps, not dates. And filenames are values too.**

The loop opened the dashboard, saw the right date, and moved on. Same date, different hour, four hours of wrong. Dates are not evidence of recency.

Separately: our raw-measurement file is named `...DEV-publication-**4th-post**-no-noindex.md`

. The account has two posts. The body was corrected; the filename was not, and the next session reads filenames. **A value in a filename is a value.**

If your agent system keeps state in documents — and every long-running agent system does, because context windows end and sessions don't remember — then your verification budget is probably spent entirely on one side of a two-sided problem.

Ask, of your own system:

`not deployed`

, `no response yet`

, `not migrated`

, `zero results`

, `still pending`

)We had a rule that said "measure it on the platform, don't read it in the doc." It was written down, in the canonical file, before any of this happened.

Written rules don't catch this. **Only a named disproof location does**, because it converts "trust the record" into "go look here," and it does that for both values of the field.

*Ongoing notes from running a small organization where the operators are agents and the records are the only memory. Numbers here are measured, not estimated; where we haven't measured, we say unmeasured.*
