AI coding agents have become good enough that the interesting question is no longer whether they can write production code. They can. The harder question is how to organize them.
For a while, our workflow at Studio81 Labs was straightforward: give a capable coding agent a feature, let it inspect the repository, make a plan, implement the changes, run tests, fix problems, and open a pull request. It works surprisingly well. It’s also surprisingly inefficient.
A high-reasoning model that’s valuable when making architectural decisions doesn’t need to spend the rest of its context window coordinating routine file changes, reading test output, fixing lint errors, or supervising cheaper subagents. Once we started running several projects and features concurrently, that stopped being a theoretical concern. Agent compute became a resource worth designing around.
So we started treating AI-assisted development less like a conversation with one very capable developer and more like a software delivery pipeline. The result is a workflow built around four roles:
Architect → Orchestrator → Workers → Reviewer

The important part is that these are roles, not model names. Today, an architect might be GPT-5.6 Sol, Claude Opus, or Fable; an orchestrator might be Claude Sonnet, GPT-5.6 Terra, or Gemini. Tomorrow those choices will probably be different. The workflow should survive that.
The problem with the one-agent workflow
A typical autonomous coding task looks something like this:
Idea → Repository analysis → Architecture reasoning → Implementation plan
→ Implementation → Subagent orchestration → Tests → Fixes → Pull request
There’s nothing inherently wrong with it. The problem is that every stage runs through the same reasoning context, often the same expensive model. The model that reasoned deeply about domain boundaries is still the one on duty when a worker reports that three tests failed because a mock needs updating. That’s wasted capability.
Subagents help, but they don’t eliminate it. Even if workers use smaller optimized models, the parent still has to coordinate them, consume their results, maintain context, make decisions, and integrate the work.
Our first change was therefore simple: separate reasoning from execution.
Stage 1: the architect
A substantial feature starts with a high-reasoning model — but its job isn’t to write code. Its job is to make the implementation boring.
Before the architect starts, we already have product-level planning in GitHub. A larger feature or substantial bug becomes an Epic with child issues describing the desired behavior and acceptance criteria:
Epic: Multi-device support
├─ Device registration
├─ Identity and recovery
├─ Synchronization
├─ Local persistence
└─ Conflict handling
Those issues describe what we want to build. The architect works out how. It reads the complete Epic and its child issues, but also the actual repository, architecture docs, ADRs, and existing patterns. This distinction matters: an agent shouldn’t design a system purely from a product issue when the codebase contains constraints the issue author never knew about.
The architect then extends the Epic with a technical implementation plan — target architecture, affected modules, data and persistence changes, APIs and contracts, dependencies, migration and backward compatibility, test strategy, PR boundaries, integration validation, and escalation criteria.
It also decides how the implementation should execute: serial, parallel, or hybrid. Multi-agent systems make parallelism tempting — if five agents are available, why not give them five issues? Because parallelism isn’t free. More agents can cut wall-clock time while increasing compute consumption, coordination overhead, merge conflicts, and the odds of independent architectural decisions drifting apart.
So we make execution strategy an explicit output of planning. Serial for tightly coupled or architecturally uncertain work. Parallel for independent tasks with stable interfaces. Hybrid for the common case — a shared foundation first, independent branches concurrently, then an integration phase:

The goal isn’t maximum parallelism. It’s useful parallelism. Compute budget is part of that decision too — running several orchestrators and their workers at once can burn through agent quotas remarkably quickly.
GitHub is the coordination layer
Our first version used Markdown files inside Git worktrees. The architect wrote a plan; the orchestrator read it; if implementation exposed a gap, the orchestrator wrote an escalation Markdown file back. Conceptually clean. In practice, current agent tools have different ownership models for sessions and worktrees — a worktree created by one session may not be attachable to another, and switching models inside a session can degrade context.
We were asking Git worktrees to solve two unrelated problems, so we separated them:
- Git worktrees provide implementation isolation.
- GitHub Issues provide agent coordination.
A worktree, in other words, belongs to a unit of delivery, not to an agent — the agents working on it may change, so it can’t also be the communication channel between them. Keeping coordination in GitHub means the workflow doesn’t depend on any one agent runtime.
After planning, the architect creates an implementation child issue under the Epic — execution order, dependencies, expected PR boundaries, and a reference to the technical plan. The orchestrator can now start in an entirely new session, with a different model and even a different tool. Its entry point is simply a GitHub issue. No conversation history needs to survive the handoff.
Stage 2: the orchestrator
The orchestrator receives the implementation issue, reads the parent Epic and its plan, inspects the child issues, and creates an isolated worktree for execution. Its responsibility is fundamentally different from the architect’s. The architect designs. The orchestrator delivers.
Implementation issue → Orchestrator → [ Worker · Worker · Worker ] → Tests → PR
It coordinates implementation, delegates bounded tasks to workers, runs tests and validation, creates pull requests, and keeps code changes tied to GitHub issues. It’s allowed to make normal implementation decisions — naming, internal organization, test structure, straightforward refactoring, details within already-approved contracts. What it’s not allowed to do is silently redesign the feature. That requires escalation.
Stage 3: escalation
This became one of the most important parts of the workflow.
No implementation plan survives contact with a sufficiently complicated codebase unchanged. A worker may find that an existing persistence invariant conflicts with the design, or that an API has backward-compatibility requirements that weren’t apparent during planning, or that a lifecycle assumption is false.
A common agent behavior is to just solve the newly discovered problem — sometimes correctly, sometimes by burying a significant architectural decision inside an implementation diff. We don’t want that. Our rule:
A cheaper model is allowed to discover an architectural problem. It is not automatically authorized to solve it.
When implementation hits a gap involving architecture, persistence, public contracts, security, concurrency, or backward compatibility — anything that materially invalidates the approved plan — the affected work stops, and the orchestrator opens a new GitHub child issue:
[ESCALATION] Conflict between device identity
and existing recovery semantics
The issue records the context, the original assumption, the actual discovery, the relevant code, known options, and the specific decision required. The architect can then resume its original high-reasoning session with a very small prompt — Resolve escalation #157 — reason deeply about only that problem, post the resolution, and update the technical plan if needed. The orchestrator reads the persisted resolution and continues.
The expensive reasoning model is invoked exactly where its capability is worth paying for, and nowhere else.
Stage 4: independent review
We also avoid making the implementing model the final authority on its own work. A pull request goes through an independent reviewer, preferably from a different model family:
Gemini implementation → Codex review → Gemini fixes → Codex review → Human validation
Different model families have different failure modes. Using the same model to implement, inspect, and approve its own architectural assumptions gives us less diversity than bringing in another system as the reviewer. The reviewer doesn’t need the implementation session — the pull request is its interface. If review finds an implementation defect, the orchestrator fixes it. If it uncovers a new architectural problem, it goes through the same escalation mechanism.
A real experiment: HomeKit for Smart Panel
We recently got to test whether this works beyond diagrams. Our FastyBird Smart Panel project needed native Apple Home integration — substantial enough to be interesting: a HomeKit bridge, device compatibility detection, accessory mapping, pairing, persistence, bidirectional state sync, and an admin UI.
We used Google’s Antigravity 2.0 with Gemini for the implementation. The result surprised us. The agent built the integration quickly, including an admin flow where users explicitly choose which devices to expose to Apple Home — unsupported devices are identified before pairing instead of silently failing. Codex independently reviewed the implementation several times; Gemini handled the findings.
Then we moved beyond agent-generated tests. The bridge paired with Apple Home. We verified the admin UX and fixed the small product and UX issues coding agents still routinely miss. We ran simulator testing of the complete command path. Finally we deployed the plugin to a live installation with real devices — they appeared in Apple Home, and control worked bidirectionally and responsively.
That changed our view of Antigravity: we’d treated it as an experiment alongside Claude Code and Codex, but after a real integration survived independent review and physical-device validation, it became reasonable to treat it as another production-capable execution environment.
More importantly, it reinforced the architecture of the workflow. The important abstraction wasn’t Gemini wrote the feature. It was:
Planning → Implementation agent → Independent model review
→ Fix cycle → Human UX validation → Real hardware validation
Any of those model assignments can change.
Humans are still part of the pipeline
The HomeKit experiment also showed where we still add substantial value by hand. The generated admin UI was surprisingly good. It wasn’t perfect — there were UX details that made sense technically but didn’t fit the rest of the product as well as they should. We corrected those during manual validation.
Claude, Codex, and Gemini all show variants of this. With a detailed UI spec and design system, agents get remarkably close, but product judgment remains hard to encode completely. So our pipeline ends with something agents can’t yet replace: real-world validation. Using the UI, pairing an actual iPhone, controlling a physical device, testing an awkward recovery flow, or simply asking whether the feature feels coherent with the rest of the product. Passing unit tests is not the same thing as shipping a good feature.
The resulting workflow

Three prompts instead of one giant prompt
In practice, we no longer need one enormous prompt asking an agent to own the entire lifecycle. We need three reusable entry points, and the artifacts between them live in GitHub, not in conversation history. That’s intentional.
1. Plan the Epic. Given an Epic and its child issues: inspect the actual codebase, design the technical implementation, determine dependencies, choose serial/parallel/hybrid execution, define PR boundaries and validation/escalation criteria, update the Epic, and create an implementation issue. Do not implement.
2. Implement the plan. Given the implementation issue: read the parent Epic and plan, create an isolated workspace, execute the dependency graph, delegate bounded work, create and link PRs, run validation, and escalate material architectural gaps instead of improvising.
3. Resolve an escalation. Given an escalation issue: inspect the original plan and relevant code, reason deeply about the specific gap, record the decision, update the Epic if needed, and tell the orchestrator exactly where execution can resume.
The models should be replaceable
Our current toolset includes Claude Code, Codex, and Antigravity, and we pick models by task: high-reasoning for architecture, faster models as perfectly capable orchestrators, smaller models as bounded workers, another family for independent review. But none of those names belong in the architecture of the development process. A healthy workflow should allow Sol → Sonnet → Codex review today and Opus → Gemini → another reviewer tomorrow.
The repository, product spec, GitHub issues, pull requests, and persisted decisions are durable. The model is replaceable compute.
Coding evolved into constraint management
The biggest change AI agents have made to our process isn’t that we type less code. It’s that more of our engineering work moved one level up. We define product constraints. We define architectural boundaries. We decide what an implementation agent may decide autonomously and what it must escalate. We decide where parallelism is useful and where expensive reasoning is worth spending. We review the output and validate it against reality. The agents increasingly handle execution inside those boundaries.
That makes agentic software development less about finding the single smartest coding model, and more about designing a system in which multiple models can work together reliably.
Which looks surprisingly similar to designing software itself.
— Studio81