"The draft is ready" is not a status: what an agentic routine owes you when it reports on its own work

A daily scheduled routine carried its own claim that two replies were drafted. Neither draft existed. Why an agent must verify the assertions it holds in state - and the API trap that nearly confirmed the bug.

"The draft is ready" is not a status: what an agentic routine owes you when it reports on its own work

Two tasks in my list carried the same line in their description: draft is ready in Gmail. One had been sitting there since the second of the month, the other since the seventh. Both were marked priority one. Both were replies people were actually waiting for.

Neither draft existed. Nothing had been sent. One thread had been silent for thirteen days, the other for ten.

The routine was quoting itself

I run a daily reconcile between my task list and my inbox. It is a scheduled routine — it wakes up, reads what is open, checks the mail, writes drafts where a reply is owed, and comments on the tasks so that tomorrow's run knows what yesterday's did. It has been useful for months. It is also the thing that wrote those two lines.

So every morning for two weeks, the routine read "draft is ready in Gmail" out of a task description, took it as the current state of the world, and moved on. It never occurred to it to look. The sentence had been true once, which is how it got written. It stopped being true at some point nobody logged, and after that the only evidence that a reply was handled was a claim the system had made about itself.

I checked it properly in the end: a list of every draft in the account (there were two, neither of them these), plus a forty-five-day search of sent mail against both recipient addresses. Nothing. The most recent real message in both threads was still the other person's.

Lack of proof neither confirms nor denies a proposition.

David McRaney puts that in You Are Not So Smart as the argument from ignorance, and it is precisely the shape of the bug — just pointing the other way. The routine did look at the draft list. It looked at it in order to avoid creating a duplicate. It asked "does one already exist?", got back "no", and read that answer as then create one. It never read it as the one you announced is gone, and the task has been claiming otherwise for a fortnight.

Same query. Same empty result. Two completely different meanings depending on which question you thought you were asking.

A count is not a confirmation

There was a smaller trap underneath it, and this one is worth knowing if you build anything on top of Gmail. Fetching either thread reported a message count of two, but returned a single message. The tempting inference is that the missing one is the draft — count minus returned equals draft present.

It isn't. The threads resource does not hand you drafts the way it hands you messages; drafts live in their own resource, with their own list call. A gap between the count and the array is a property of how the API models a mailbox, not evidence about what is in yours. I nearly used it as proof, which would have meant confirming the bug instead of finding it.

If you want to know whether a draft exists, list the drafts. There is no clever shortcut, and the clever shortcut that looks available is wrong.

What I am changing, and the general rule under it

The fix is narrow. When a task description or an earlier comment asserts that a draft is ready, that assertion gets verified against the live draft list on every run. If the draft is missing and nothing was sent, that is not a cue to quietly make a new one — it is a finding, and it goes under "needs your call" with the shape of the problem spelled out: announced draft is gone, nothing sent, thirteen days of silence.

Note what the existing safety net did not catch. I already had stale-draft flagging: anything sitting unsent for more than forty-eight hours gets raised. But that check presupposes a draft to be stale. The expensive case was the one where the draft had vanished, and the alarm I had built could not see it, because it was watching the wrong object.

The general rule I would put on any routine that reports on its own work: a claim the system carries in its own state is not evidence, it is a memory. Memories need re-checking against the world on the cadence at which the world changes. For a mailbox, that is every run. Anthropic's guidance on building effective agents keeps returning to the same point from the other side: the thing that makes an agent loop work is a real signal from the environment at each step, not an internal note about what happened last time.

This is the third variation of the same failure I have hit this quarter. A freshness check that could not see staleness arriving from outside the folder it watched. A scheduled routine that failed three weeks running because of what it was not given. And now a routine trusting a sentence it wrote itself. Every one of them is the same mistake: the check was aimed at the artefact rather than at the claim.

The uncomfortable part

Two weeks of daily runs, each one producing a clean report, and the report was wrong in the same way every morning. The routine was not broken. It did exactly what it was told, including the part where it believed itself.

That is the real cost of handing work to something that never gets bored. A human assistant who wrote "draft is ready" on the second would, by the ninth, have had a vague itch about it. The routine has no itch. It has a field, and the field said yes.

Which means the itch has to be written down as a check, the way a suggestion has to become a guarantee before you can rely on it. That is the whole job, really — turning the things an experienced person would notice into things a machine has to look at.

Two fetches would have caught it. I just never asked the question in a form that let the answer be no.

Sources & further reading

External
Gmail API — Users.threads reference
Gmail API — Users.drafts reference
Anthropic Engineering — Building effective agents
David McRaney, You Are Not So Smart

Related posts
File timestamps can't see staleness that comes from outside the folder
My scheduled Claude Code routine failed three weeks running
Claude Code mods vs skills: a skill is a suggestion, a mod is a guarantee
The same task, three days running

Subscribe to Remco Livain

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe
Work with me →×