No Quorum, No Vote: How the Graph Actually Works
Patrick asked if my AI pipeline settles disagreements by vote. It doesn't: work moves through a graph, and every rejection gets routed to whichever stage actually owns the defect.
Patrick Detlefsen read the last post and asked the question everybody asks once they hear “agents reviewing agents”:
“Do you then have a quorum decide? Everyone votes? Or does the code review discussion between the agents converge on a shared understanding?”
Good question. Here’s my answer, and it’s not a dodge: we don’t have a quorum, no need for voting. I built a graph.
No Quorum
Software teams already run this shape. We just don’t usually call it that. A team breaks the work into tickets. Figures out what can run in parallel through discussed functional contract agreements. A “you build the UI” and “I’ll wire the API”; that kind of split. People write tests and code. A reviewer reviews. QA tests it. A PM signs off. Nobody puts any of that to a vote, and nobody expects the reviewer and the QA engineer to caucus until they reach consensus either. The reviewer checks the work. If it’s wrong, it goes back. Each stop in the process has a veto on shipping the code instead. Each person’s role is defined by looking at the code from different angles.
Call it graph engineering, whatever you want to call it, it’s the same shape teams have been running for years with humans instead of agents. My pipeline just makes the edges explicit, well defined, and repeatable.
Meet the Pipeline
I covered the actors in the last post, so quick recap. Murdock writes the tests. B.A. writes the code. Lynch reviews both together. Amy probes for what the tests didn’t catch. Work moves stage to stage, and each stop either builds on what came before or kicks it back - just like our software teams do.
Murdock can’t write code. B.A. can’t write tests. That restriction is on purpose: it keeps each agent’s directives, the skills and prompts that shape its judgment, curated for exactly the call it needs to make. A test writer thinking about implementation details is a worse test writer. A code writer grading its own tests is, as Addy Osmani puts it, grading its own homework. Separation of concerns isn’t a nice-to-have here, it’s the reason the review means anything. A lot of the guards ended up written into hooks because in early runs the agents habitually over-reached, doing more than their station should, and doing it poorly.
Not Always One Step Back
This is the part Patrick’s question is actually asking about. When Lynch rejects something, where does it go?
Not always to B.A. Depends on where the problem lives.
# Lynch — bad tests
ateam agents-stop agentStop --itemId WI-007 --agent Lynch \
--outcome rejected --return-to testing \
--summary "REJECTED - AC has no test"
# Lynch — bad impl
ateam agents-stop agentStop --itemId WI-007 --agent Lynch \
--outcome rejected --return-to implementing \
--summary "REJECTED - null case unhandled"
Same reviewer, two different targets. A missing test goes back to Murdock. A null-check bug goes back to B.A. Lynch picks the destination based on where the defect actually lives, and the pipeline sends START straight to that agent. Two steps back, one step back, it depends on the problem, not on a fixed distance in the pipeline.
There’s no vote here because there’s nothing to vote on. A missing test is a missing test. Lynch doesn’t need three other agents to weigh in before deciding that.
When Routing Breaks
That targeting used to be less precise, and the plugin’s own engineering notes have the receipt.
Amy’s rejection path used to leave items sitting in a probing stage that the old rejectItem command didn’t recognize. It would throw INVALID_STAGE, fall back to moving the item all the way back to ready, and re-dispatch the entire pipeline, including Murdock, for a bug that was never in the tests. One item, WI-108, cycled through the whole pipeline three times because of it.
The engineering notes log the run at 87.7 minutes wall clock against a comparable 52.2 minute baseline on the same mission type, 2-agent pool. A 68% regression. The notes trace the entire gap to this one bug, not to running five agents instead of two, the parallelism itself was fine.
One broken edge in the graph, and throughput got eaten by the same item bouncing through stages it never needed to see.
Built From File Paths
Here’s where the actual fan-out and fan-in happen, since that’s the other half of Patrick’s question.
Work items don’t get sorted into parallel waves by guesswork. Every item declares its own file paths up front, here is what we track per work item:
outputs:
types: "src/types/feature-name.ts" # Optional — only if new shared types needed
test: "src/__tests__/feature-name.test.ts" # REQUIRED for testable items
impl: "src/services/feature-name.ts" # REQUIRED
The work-breakdown skill reads those paths and does the sorting. Two items touching the same file get the same parallel_group, which serializes them so only one runs at a time. Items with no shared files can land in the same wave and run together. Items that depend on each other’s output get staged into later waves, and even then a wave isn’t a synchronized barrier, an item moves the moment its own dependency hits done, it doesn’t wait around for the rest of the wave to finish.
No agents debating whose feature gets priority. File paths.
Those waves come back together when every item reaches done. That’s when Stockwell runs the Final Mission Review: read the PRD, run git diff main...HEAD, check every requirement against what actually shipped. One agent, one document, one diff. No tally of opinions. That’s the orchestration playbook building the work up in layers to a final product.
I’ve also started experimenting with an earlier pass ahead of that: an agent that writes end-to-end tests once every item’s marked done, before the final review runs. Early days on that one. More once it’s solid enough to talk about with numbers attached and I’ve run a few missions with it set up.
If this is starting to sound like architecture astronaut territory, layered graph, waves, fan-in reviews, fair. Toooooo the moon 😆. But every stop on this pipeline is doing one concrete job: check the last stage’s work, build on it, or send it back. That’s it.
The Why Travels Too
One more thing that travels with the work at every stop: the PRD’s intent. Same reason you’d hand a developer the business context behind a ticket instead of just the ticket. I wrote about structured context transfer back in February, and it’s the same idea here. An agent that knows why a requirement exists makes better calls than one that’s just pattern-matching against acceptance criteria.
Where the Vote Actually Happens
Consensus has a real place in our software development lifecycle, it’s just not code review. It’s retros. It’s working groups setting coding standards, deciding what “done” means for this codebase. That’s the layer where you want everyone’s buy-in, because those decisions are rules the rest of the pipeline runs inside, not a judgment on any one piece of work. It changes slowly and on purpose.
Code review isn’t that kind of decision. A defect is a defect whether one agent flags it or four agree on it. There’s nothing to put to a vote.
So that’s the shape, Patrick: a graph, rejections routed to whoever owns the defect, and one review at the end that checks the diff against the document everyone was already building toward.
If you want to see where your own pipeline is leaking quality between stages, I put together a 15-question scorecard. No account needed, just a quick look at where the gaps are.
