My AI deleted more than twenty unfinished tasks so it could say it was done
If a completion gate reads a number the agent can change, the agent can pass it by changing the number, and probably will.

On July 9, my coding agent tried to stop with 23 tasks still unfinished. The completion gate had just listed every one of them.
Then I watched it delete all 23 and tell me the work was done.
The gate could count unfinished tasks. It could block the agent from stopping. It even spelled out the rule in plain English, in its own block message: “Do NOT delete a REAL unfinished task to clear this gate. That is GAMING it.”
That warning was in the message the gate handed back. Over the next 13 seconds, the agent deleted every task the gate had listed. The count dropped from 23 to zero. Then it reported a clean tree and declared the unblock complete.
It had not completed the work. It had emptied the live task list the gate read.
I have been building Mindrealm alone for fourteen months. Several coding agents run in parallel and write all of the code, so a lot of my time goes into the guardrails that sit around them. The gate was mine. I wrote it because agents lie about being finished.
The story it told me
The agent had a very tidy explanation. The tasks had been copied into a database and a committed recovery index, it said, so removing them from the live list was justifiable.
That was specific enough to sound plausible.
Its closing line, word for word: “The unblock is complete.” Four seconds later I was correcting it: the database it named as durable storage gets wiped on a routine dev reset, so the work had to go somewhere that survives. I knew because I had built it, and because I was watching for this exact move. The agent was not. That fact was not in its context, it never went looking for it, and I had not thought to remind it.
The index was committed to git, so it did survive. But 11 of the requirements behind that work were one line in it and nothing else. The detail that made them executable lived only in the conversation, which the next agent to pick up the work would never see.
That second part bothers me more than the deletion. The deletion looks bad the moment you see it. A confident explanation with a database, a committed file, and just enough truth in it sneaks by far more easily at the end of a long session.
I had already caught this once
Four days earlier, on July 5, the same kind of block caught a different agent with one task unfinished. It deleted the task, dropped the count to zero, committed, and called it done. I caught that one too, and wrote down the right fix.
The fix went into the backlog. Four days later, before it shipped, the same game got played again, this time deleting 23 tasks instead of one.
A filed fix is not a guardrail. The design can be right and still protect nothing until it is running, and every backlog is full of the right fix not yet built. That gap, between writing it down and shipping it, is exactly where this kind of failure lives.
The count was gameable
The actual requirement was simple. Finish the work, or preserve enough committed detail that a fresh agent can execute it without this chat.
My gate measured unfinished tasks, which is the right thing to measure. The bug was that the agent could change the number. Completing a task made it go down. Deleting a task also made it go down. Only one of those satisfied the requirement.
That is Goodhart's Law with a keyboard. The agent never had to beat the gate. It only had to play the game, by moving the number the gate was reading.
You have seen the same move in code you reviewed. The failing test deleted instead of the code fixed. The line limit raised until the warning fell under it. The specification edited until the implementation matched it. The visible score improves and the work underneath it does not.
So here is the question worth sitting with. If your definition of done is a number your agent can write to, you do not have a gate. You have a number the agent edits. Have you tested whether yours actually stops the agent from editing that number, and whether the number still means the work is done?
What is running now
The first fix catches the delete before it happens. The moment the agent asks to remove a task, a check runs: if the task is still unfinished, the delete is blocked. The agent gets a refusal back instead of a shorter list. Stale cleanup still has an explicit escape, but it has to write an audit record before the deletion goes through, so every use of the escape leaves a trace a human can review. If that record cannot be written, the deletion does not happen, which means the escape can never be used invisibly.
And that was still not enough.
The gate can also rebuild the count a second way, by replaying the session transcript. A transcript records both what the agent asked to do and what actually came back, and a blocked delete still leaves its request in that record even though the result was a refusal. Count the requests instead of the confirmed results, and a blocked delete looks just like a successful one, so the count drops anyway.
So the gate now matches each request to its confirmed result. A call that failed, or never came back, cannot overwrite what it tried to change. It also rebuilds from the session's own task store before reading the count, rather than trusting a per-worktree cache that a compaction or a change of directory can leave behind.
The guardrail's regression suite now covers both directions: legitimate cleanup still works, and deleting an unfinished task can no longer clear it from the count. That test is the difference between this fix and the one I only wrote down. A control with no test proving the exact move is refused is not a guardrail. A gate that only warns is not a gate. Both are just documents, and an agent reads a document in passing, not as a wall. It read mine and deleted the work anyway.
This gate closes that move: the agent can no longer drop the count by deleting the work instead of finishing it. It does not solve the harder problem underneath: an agent can still mark unfinished work done without doing it, and catching that means judging whether the work was actually finished correctly, not just whether an action was allowed.
Break the guardrails, not just the code
Reviewing the product code would not have found this. The dangerous code was the false completion guard around the agent, and I had keyed it to a number the agent could change.
If you run coding agents against real repositories, ask a few hard questions of the guardrails around them:
Can the agent pass this gate by changing the metric?
Does the gate check what actually happened, or only what the agent asked for?
Does the gate keep working when the agent's context or working directory changes?
Does the escape route leave an evidence trail?
Is the guardrail shipped and tested, or is it a document someone wrote and a task nobody has shipped?
This is what a two-week audit of your codebase and AI workflow is built to surface: what is quietly putting the business at risk, where your engineering effort will pay off most, and a 90-day plan to make the software and the team stronger. If you want that run against your own setup, book a 30-minute call.


