§1The green tick that lies
Every task system we run has one direction of travel. Open, in progress, done. The board knows how to promote and nothing else. That is fine right up to the day a task marked done turns out not to be done, and there is no clean way to say so. The count still reads green. The chart still slopes the right way. On a film set the gaffer can look at a shot everyone thought was finished and send the whole crew back to reshoot. A burndown chart cannot do that. It can only go down.
That gap matters more when the thing marking tasks done is not a person but an agent. We run a small fleet of them against an internal board, and an agent will report success without much prompting. If the only recorded states are forward ones, the board becomes a story about progress rather than a record of what is actually true. We wanted the record.
§2Two ideas worth stealing
This came out of reading gaffer, a small, MIT-licensed tool by a builder posting as Graite0x. It is a deterministic scheduler for a fleet of coding agents, roughly two thousand lines with no runtime dependencies, and two ideas inside it are sharper than the tool as a whole.
The first is that the gate is the syscall. A task is finished only when a real command exits zero. The scheduler never asks a model whether the work is done. An exit code decides, and the code accepts what the model merely proposes. The second is that done can be taken back. gaffer has an unfinish verb, and its own README states the principle without decoration: a system that can only promote is a burndown chart. We already had worktree isolation and an audit trail, so those parts were nothing new. Those two ideas were.
§3What we changed the same afternoon
Our loop board is a small tool we call gary. It could start an item, ship it, block it or kill it. It could not do either of gaffer's two things, so we added them both.
ship --gate now runs a command before it will mark anything shipped. If the command exits non-zero the item is refused, nothing changes, and the failure is written to the audit log. We tested it with a gate of false and a gate of exit 3, both refused; a gate of true shipped. unfinish takes a shipped, done, killed or cancelled item back to open, records the reason on the thread, and logs the reversal. Shipped, then unfinished, then open again, with the reason attached where the next person will read it. Two small commands, no existing behaviour touched, done in an afternoon.
§4The gate stops the worker grading itself
Gating on a syscall is not ceremony. It is separation. There are two different questions hiding inside the word done: did the agent do the work, and is the work acceptable. Most agent setups fold those into one without noticing, the agent says it finished and the system takes its word for it. That is the worker marking its own homework, and it is how a plausible, confident, wrong result gets recorded as a success.
A command gate splits the two apart. The agent can claim whatever it likes. The item is not shipped until the tests, or a link check, or a build, actually pass. The model proposes and the code accepts. It is the same contract we already trust in every continuous-integration pipeline on earth, and it is oddly rare inside agent orchestration, where the fashion is to let the model narrate its own completion.
§5Reversible done is more than an undo button
Taking done back sounds like a small convenience. It is bigger than that. A board that cannot reverse a completion cannot represent a state that happens constantly: this was finished, and then it was not. Without a first-class way to say that, you get workarounds. A duplicate task. A comment nobody reads. A status left wrong on purpose because fixing it is more effort than ignoring it.
With it, the reversal is a proper event. It carries a reason, it lands in the audit log, and the item rejoins the live board where it will actually be seen and picked back up. The honest version of a task tracker is one where done is a claim that can be revoked with evidence, not a trophy that can only be added to the shelf. A number that can only go up is not measuring completion. It is measuring optimism.
§6What it does not fix
Two honest limits. gaffer re-blocks a task's dependents when you unfinish it, because gaffer has a dependency graph to walk. Our board's items are mostly independent, so our unfinish reopens the item but has nothing to cascade to. The day we add real dependencies, that cascade is the next piece to build, and we will be glad we copied the idea first.
And a gate is only ever as good as the command behind it. A gate of true passes everything. A green test suite with no assertions proves nothing. The syscall is honest, but it will certify a lie without hesitation if you hand it one. The mechanism took an afternoon to borrow. Choosing a gate that would genuinely fail when the work is bad is the part that never finishes. Which, given the subject, feels about right.
