From the day I started Datris, I built it with one agent in one terminal. One model planned each feature, wrote it, tested it, reviewed it, and wrote the release notes, all in one long conversation.
It worked, mostly. But the failures had a pattern. Code got reviewed by the agent that wrote it, and the review came back “looks good.” Tests got written by the agent that had just made the design calls, so they checked what it built instead of what I asked for. Nobody was ever surprised, because there was only one mind in the room.
A few months ago I broke it into a team.
Five roles that don’t trust each other
The planner turns an idea or a GitHub issue into one story: goal, files, steps, acceptance criteria, verification commands, and an out-of-scope section. It has to read the actual code, cite real paths, and grep for the workarounds the story would replace. It can’t implement anything. If a story won’t fit in one session, it’s two stories.
The tester runs before any code exists. For every acceptance bullet it writes a test that fails today for the right reason and will pass once the bullet is true. Anything that needs the running stack gets flagged as end-to-end, and the tester comes back at the end to rebuild the Docker stack, run those by hand, and try to break it: empty input, an old record, the same action twice, and one thing the story said not to touch.
The implementer gets the story and the failing tests, with the instruction “make these pass, do not delete or weaken them.” If a step is wrong it stops and says so. It commits when the verification commands are green.
The reviewer did not write the code and is told as much. It works from the diff against main, checks scope, failure scenarios, and the repo’s rules, then runs every verification command itself. I added that last rule after getting burned: do not trust the implementer’s paste. It can’t edit files, and a pass means zero blocking findings.
The releaser bumps versions and writes the release notes under my usual rules (user-facing outcomes only, no filenames). It has no permission to tag, push images, or publish to the registry. I do the shipping.
The lead can’t write code
A lead sits over all of them and is forbidden from implementing or reviewing anything. It spawns the tester, then the implementer, then the reviewer. If the review fails, the lead passes the findings back to the same implementer word for word, with the instruction “fix only these,” and spawns a fresh reviewer. After three rounds it stops and I decide.
The word-for-word part matters. My old single-agent setup would paraphrase its own review findings back to itself, and the sharp edges got lost. A lead that can only relay has no way to soften anything.
The fresh reviewer matters too. The implementer keeps its context because it needs to remember what it built, but a reviewer that remembers approving round one is inclined to approve round two.
What shipped through it
The first real work through the team was Iceberg table output for the object-store destination, with S3 credential rotation alongside it. Six stories, each one planned, built, reviewed, and end-to-end tested against a local MinIO and a real S3 bucket, with a security review on top. The lead opened the pull request with the reviewer’s verdict and the test results pasted in, and I read those before I looked at the diff.
Next came three hardening stories, including a bug where editing a secret in the UI could blank it. In one of them the reviewer caught a scope widening I would have waved through.
It isn’t faster. Five roles and a three-round review cap burn a lot of tokens and wall clock. What I got was a pull request argued over by something other than its author.
What surprised me
Writing tests first mattered more than I expected. Handed tests it didn’t write, the implementer works toward someone else’s definition of done. Writing its own afterward, it just confirms whatever it already did.
“Stop and report” worked better than I expected too. My single-agent setup would route around a step that made no sense, and I’d find out three files later. Now the implementer stops, and when the story itself is wrong it goes back to the planner.
What’s still bad
The planner is the weak link. A vague story gives the tester nothing to test, and the lead kicks it back. That’s correct, but it means my ideas have to be sharper going in. Maybe that’s a feature. It doesn’t feel like one at 9pm.
Three review rounds is a guess. I had one story burn all three on a formatting check the implementer kept fixing slightly wrong. A human would have said “just run the formatter” in round one.
The bigger worry is that the roles aren’t really different minds. Same model, different instructions, which is closer to role-play than independence. I don’t yet know how much of the review quality comes from simply being told “you did not write this,” and I suspect it’s more than I’d like.
Why bother
The whole pitch for Datris is that agents can do real operational work if the boundaries are right: scoped keys, approval gates, audit trails, a recovery loop that hands off to a human when it should. It would be strange to build that with a single unbounded agent and a prayer.
So the org chart is the same argument applied to my own shop. No role reviews its own work, and none of them can ship. I hold the tag.
The role files are in the Datris repo. They’re short, and most of each one is a list of things the role isn’t allowed to do.
Todd Fearn is the founder of Datris.ai, an open-source, agent-native data platform built on the Model Context Protocol, and he runs IData Corporation, a data engineering consultancy for financial services firms. He has spent about thirty years building production data infrastructure inside institutions like Goldman Sachs, Bridgewater Associates, Deutsche Bank, and Freddie Mac.