Ten tasks marked done, never on main
By August 5, 2026 we had found ten tasks marked done whose work had never reached main. That day we preserved what was left of it.
For nine of them, the work was a commit that nothing pointed at. No branch contained it, no tag, no remote, and no reflog entry. The objects were still in the repository, so git cat-file -e passed. But the next git gc --prune would have deleted them without a word. The tenth had no commit in the repository at all. Its work lived in a separate repo, on a branch that had never been merged.
How work goes missing
The cause was a defect in how finished work got landed. The landing step committed a fresh, single-parent merge of the content, so a task’s own commit never became an ancestor of the team branch. The tool that was supposed to confirm the landing couldn’t name the landed commit, so every landing was rewound, and each rewind was reported as a conflict. Later, the task branches were pruned as normal housekeeping. That removed the last thing pointing at the work.
Each step was reasonable on its own. Together they made finished work invisible while every task still said done.
What we did
We pinned the nine commits with 18 tags, one per preserved commit, and wrote a bundle:
git tag -l 'rescue/*' # 18 tags across 9 tasks
git bundle verify <backup>/rescue-unlanded-20260805.bundle
The bundle is 31 MB and verifies as a complete history. Pushing the tags to the remote hung, because they carried a large set of objects the remote didn’t have. So the rescue doc says, in bold, that the objects exist only on one machine. Copying the bundle somewhere else is the outstanding risk.
Then we decided what each task actually was. Six, a chain of refactoring tasks, were cancelled: they were 1,277 to 1,288 commits stale, and their conflicts covered 36% to 88% of the file they touched. One more was cancelled for the same reason at 1,407 commits stale. One was superseded by a later task that landed. One still needs a decision. One counts as resolved only in the sense that the work exists somewhere: on an unmerged branch in another repository.
Cancelled didn’t mean thrown away. Every cancelled task names its rescue tag, and one commit carries the content of all six chain tasks. The point the rescue doc makes is that the design is what lasts. The diffs go stale.
Two findings worth keeping
An incomplete list looks exactly like a complete one. The refactoring chain was recorded as five tasks. It was six. The missing one was one gc from gone, and it only turned up because an independent pass re-derived the chain instead of trusting the list. The disposition table had the same problem: its first version described one task in a way the same document’s own evidence contradicted two sections earlier. That was caught during an independent check and corrected.
A recovered check can be empty. One rescued task included a gate that was supposed to confirm three jobs were registered. Against the real tree, the registry it should have read was an empty list, and the jobs sat in a dead array next to it. The gate scanned the file’s text, found three IDs, printed a pass and exited 0. Recovering the work is different from recovering the guarantee. Re-run what a restored check actually binds before you trust it.
The rule
Now I don’t believe an agent’s “done” until git agrees:
git branch --contains <sha> # main has to be in the list
It is the last of the six git rules I wrote up for running several agents in one repository: a task is done when its commit is on main, and not before. Every other signal (the task status, the agent’s summary, a green board) is a claim about the work. This one checks the work itself.
- Oct 2026Rigor has a budget
- Sep 2026The cleanup agent that deleted my model caches
- Aug 2026When an agent does something strange, check what it was refused
- Jul 2026Eight completions, zero real
Building something with agents? Say hello.