The agent work cycle in my_architect: start_task โ next โ complete as a single loop
When we opened the MCP interface up to agents, the temptation was obvious: give the model CRUD over the hierarchy โ create nodes, change statuses, write docs โ and let it sort itself out. We didn't do that. Instead of a scatter of low-level commands we built a narrow working loop out of three verbs: start_task, get_next_task, complete_task. Below is why a loop rather than a bag of handles, and what trade-offs come with it.
The problem: an agent without a work cycle degrades
An agent that only has "create a node" and "change a status" behaves predictably badly. It picks up a task and forgets to mark it, leaves a draft status on work that's already done, spawns nodes around the hierarchy, and loses context between steps. And every individual operation returns an honest 200 OK the whole time โ the system isn't lying, but it isn't helping either. The project as a source of truth slowly drifts from reality.
We wanted the agent to have not a list of buttons but a closed loop: ask "what's next" โ take it on โ do it โ close it โ ask "what's next" again. A loop where every transition means something and every step leaves a trace in the source of truth.
How the loop is built
get_next_task answers the agent's one question at the start of a turn: what to work on. It doesn't dump the whole backlog on the model โ it returns the next sensible task given priorities (P1โP2โP3) and what isn't blocked. That's an important shift of responsibility: prioritization is the server's job, not the agent's guess based on recency or file order.
start_task moves the chosen node into progress. This isn't status cosmetics โ it's the point where the agent officially "takes" a unit of work, and where we record that it's underway. From that moment the node stops being a candidate for get_next_task for parallel agents.
complete_task closes the task โ and here's where it gets interesting. Closing one node doesn't stay a local event.
The status cascade: why complete_task isn't just a flag
The hierarchy in my_architect is a single source of truth (RFC-005), and the statuses in it are linked. When a child node is closed, the parent advances its status automatically โ that's rule R7, status cascade: child done โ parent auto-progresses. In parallel, rule R8, progress rollup, works: the completion percentage bubbles up the tree. So the agent closed a story โ and the epic, the initiative and the project root learned about it on their own, without a separate manual sync.
This is a deliberate decision. We could have left updating the parents to the agent ("don't forget to update the epic after closing the story"). In practice those "don't forgets" don't work โ the model forgets, and the tree drifts. So the cascade lives on the server: it fires as a consequence of complete_task, not as a separate agent task. It's cheaper to make the transition automatic than to rely on discipline.
Blockers as part of the loop, not an exception
The loop would be naive if it let you close anything in any order. So a blocker gate is built into the cycle. Requirements have an OQ type (Open Question), and nodes have dependsOn edges. A single helper, getBlockers, treats both unresolved open questions and unfinished dependencies as blockers. The server gates status transitions on unresolved blockers โ "done" is hard-unavailable on a blocked node.
For the agent this means get_next_task won't hand it something it physically can't start, and complete_task won't let it close something that still has an open question hanging under it. block_task, in turn, is a legitimate exit from a turn: if a new dependency or question surfaces mid-work, the agent honestly blocks the task instead of faking progress. An honest blocked is worth more to us than a fake done.
Docs inside the loop: a living source of truth
The work cycle doesn't end with a status change. While the agent holds a task in progress, it keeps the doc on the node as a living source of truth โ not an after-the-fact report but a working document updated as it goes. To keep this from turning into shipping whole walls of text around (and typos on re-transcription), update_doc supports surgical edits: find+replace of a single unique occurrence, appending a block, or replacing the body of a specific section by heading. One line of edit is one line of edit, not a re-send of the whole document.
get_project_context closes the context: at the start of a turn the agent pulls the project structure, in summary mode โ lightweight structural nodes without descriptions, so a large project fits the token budget, and you can drill into get_node for detail. That way every agent turn begins with a current picture rather than shreds of memory.
The trade-offs we made
First โ the narrow interface. Three verbs don't cover everything; sometimes the agent needs low-level operations too. We kept them (bulk_update_nodes, direct edits) but deliberately moved them outside the main loop โ including the bypass of the cascade gate in bulk_update_nodes, which is intentional and documented as a no-guarantees zone. The main path is the loop; the raw handles are for exceptions.
Second โ serialization. As soon as several operations edit one project, a read-modify-write race on the shared YAML files surfaces. We closed that with a per-project lock (a reentrant async chain): operations on one project serialize, operations on different projects run in parallel. The cost is losing some intra-project parallelism; the payoff is that the loop doesn't lose writes out from under itself.
Third โ trusting the server. The cascade, the blocker gate and prioritization are logic we took away from the agent. This makes behavior predictable, but it requires the server's rules to be right: an unreachable branch in routing or a too-strict gate immediately hits every agent at once.
What came out of it
start_task โ next โ complete reads as one loop, not three unrelated commands. The agent asks "what's next", takes it, does it, keeps the doc, closes it โ and the hierarchy recomputes statuses and percentages on its own. The full write-up of how statuses cascade across the project tree and how the execution flow keeps documentation current is in a separate piece on how the agent goes through the work cycle step by step. The main takeaway for us is simple: an agent needs not a rich API but a good work cycle. Richness is a source of drift; the loop is a source of truth.