Stop agents from marking half-finished work 'done'

You've been burned by this: an agent closes a ticket, the board turns green, and in review you find the open question was never answered or the dependency was never built. The "done" was optimistic โ€” a status that reads clean and isn't. My Architect fixes it by moving closure out of the agent's judgment and into the domain layer, where "done" is a transition the server refuses to make while the node still has open items.

The problem: "done" is a claim, and agents make it too easily

Agents are optimistic by construction. Hand a model a status field and it will set it to done the moment the work feels finished โ€” before the open question under the story is resolved, before the dependency it relies on is built. Every one of those writes returns a clean 200. The system isn't lying; it just isn't stopping the agent from lying to you. Your green status becomes a claim you have to re-verify by hand, which defeats the point of a tracked backlog.

The fix isn't a better prompt or a sterner reminder in the agent's instructions. Discipline erodes across a long session; the model forgets. The fix is to make the dishonest transition impossible.

Closure is enforced at the domain layer, not asked for

In My Architect, completing a node is not a flag the agent flips โ€” it's a request the server can reject. The task work loop runs get_next_task โ†’ start_task โ†’ complete_task, and the last verb is gated. When an agent calls complete_task on a node that still has open items, the domain layer throws BLOCKED_BY_OPEN_ITEMS instead of quietly recording a done. The status never changes, because the constraint was never met.

Two mechanisms feed that gate, and it runs on the done transition itself โ€” not on a check the agent has to remember to make first:

Requirements hang off the node, so "what's required" is checkable

The other half of premature closure is scope the agent silently drops. My Architect keeps an orthogonal requirements layer โ€” FR, NFR, SAR and CON items attach directly to the node they constrain, with a coverage view over the tree. Before it closes anything, the agent reads the requires list on the node it's holding: the functional requirements, the non-functional ones, the architectural decisions and the constraints it agreed to. Coverage is traceable, so an uncovered requirement is visible rather than assumed-satisfied. The constraint you care about isn't living in a comment thread โ€” it's a first-class item the agent has to reckon with before complete_task will succeed.

The cascade only fires on real completion

Because closure is honest, everything built on top of it stays honest. My Architect runs an automatic status cascade: close a child and the parent advances on its own; a progress and release rollup bubbles the completion percentage up to the epic, the initiative and the project root. That rollup is only trustworthy because the leaf done underneath it is trustworthy. Gate the leaf and the whole tree tells the truth for free โ€” no manual "don't forget to update the epic" that the model was always going to skip.

The aha

Watch it happen once and the change is obvious. The agent decides a story is finished and calls complete_task. The server sees an unresolved open question hanging under the node and answers with BLOCKED_BY_OPEN_ITEMS โ€” the done is rejected. So the agent does the honest thing the gate leaves open to it: it calls block_task, records the real reason, and moves on. The obstacle lands on your board live, described in words. Your status didn't turn green on a lie, because the lie was never a transition the system would accept.

For the full walk-through of the loop these gates live in โ€” pull the next task, work it, close it โ€” see hand your backlog to a coding agent.

How to try it

  1. Create a project and lay out a node or two โ€” start with Getting started.
  2. Attach a requirement of type Open Question to a story, or add a dependsOn edge to an unbuilt node.
  3. Connect your AI agent and let it run the loop. When it reaches for complete_task on that node, it hits the gate โ€” and blocks honestly instead.

Next up: connecting an AI agent so the loop runs end to end.