Why a line, and not a chat window
Most of what is sold as AI-assisted development is a developer with a chat assistant. That makes one person faster and leaves everything else as it was: the backlog is still unsorted, the review still depends on who has time, and nobody can say afterwards what the assistant did or did not check. It also runs on that one developer's laptop, with their settings, their login and their rights. When the laptop is off, nothing happens.
We wanted the opposite: a production line with roles, evidence, isolation and an audit trail, running in the cloud on a schedule or on request, under identities that belong to the platform rather than to a person. Built for the .NET and Azure applications we already build and run for clients, on the same stack.
The idea underneath is simple. Agents take over the routine steps: reading the item, finding the code, writing a test, making the change, running every suite. People keep what only people should do: decide what gets built, answer questions, and approve every merge. The line itself is fixed code with guardrails. Models judge; they do not route.
From ready to merged
On any doubt, failure, disagreement or exhausted budget the line stops and the item goes to a person. Never a retry loop, never a merge.
a person actsThe roles, and what each role may do
Every step in the line is a separate role with its own task, its own model (or no model) and its own identity with exactly the rights that step needs. That last part is not a detail: what the identity may do is what the agent may do, so every role gets its own, narrowly scoped account.
The sorter. A fast, cheap model reads the work item, the comments and the relevant code, with read-only access. Its verdict: ready to build, probably already fixed, duplicate, missing information, or needs a human decision.
The checker. A stronger model re-checks every verdict that would lead to an action. Only when both agree does the item get the label that says it is ready. In practice the fast model is too optimistic about a share of its "ready" and "already done" calls; the checker catches them before anything happens.
The builder. Claude Code, in its own sandbox, under an identity that may push branches, open pull requests and update the work item, and nothing else. No right to bypass a policy, no right to complete a pull request.
The verifier. A job with no model and no credentials. It rebuilds the change from a clean checkout and checks that every new test fails without the change and passes with it.
The reviewer. Codex, a model from another vendor, in its own short-lived sandbox with read-only access. It reviews the change against the written definition of done and hands its findings back to the builder.
The gate. Code plus a small model, which decides from the change and the evidence how heavy the human review needs to be. What the gate never decides: whether to merge. A person always does.
The engineer. One of our engineers reviews the pull request, with the verifier's evidence and the reviewer's findings attached, and completes the merge. Or not.
Sorting before building
An item enters the line from the backlog: through an automatic notification when someone marks it as suitable, through an hourly scan that catches missed notifications, on a fixed schedule, or on request. Every route produces the same message on a queue, and a small controller picks it up. The controller prevents two runs on the same item, creates the sandbox, starts the agent with the right instruction set, stores the result and cleans up.
Items that miss information get one question, posted once, and then wait for a person. No reminders; a weekly digest shows which items have been waiting. Items that are duplicates or probably already fixed are flagged for a person to confirm. The agent never closes anything, and never picks which duplicate survives. The useful side effect is a cleaner backlog, which most teams never find the time for.
Labels as the steering wheel
Everyone on a team knows the labels in their backlog tool, so labels are the control surface. The platform keeps the real state in its own database and mirrors it as one agent label per item. A person steers by changing labels, and a person's change always wins. A "skip" label means: never touch this item; running work is cancelled and its results rejected. Removing a waiting label means: sort it again. Assigning the item to someone means: cancel the run. Even adding the "ready" label by hand is not a verdict but a request to sort again.
Behind that wheel sit generation counters and locks, so a slow or restarted agent can never write over a newer human decision. Questions are posted once, never repeated.
Building, with the test first
Only items with a clear scope and a verified verdict are picked up, one at a time. The builder gets a fresh sandbox: a small virtual machine per run in Azure Container Apps, in our Azure environment in West Europe, with Docker inside. That last part matters because our integration tests start a database in a container; ordinary container jobs cannot do that.
The sandbox boots from an image we build and pin ourselves: Microsoft's Azure development image with the .NET SDK, Node and Docker, plus Claude Code at a fixed version, verified against Anthropic's signed release manifest, with self-updates and telemetry switched off. A new Claude Code release means a new image with a new name, so we always know exactly what ran.
Inside that sandbox the agent reads the work item and its parent, sets it to active, creates a branch following the repository's own conventions (they live in the repository, in a file the agent reads), and writes the failing test before the change. Then it builds, runs the unit tests, the integration tests and the front-end suite, pushes the branch and opens a pull request linked to the work item. The agent runs in the background and the controller polls for status, so a long run is not tied to one connection. At the end the result is kept and the sandbox deleted.
The same runner code runs on a developer's laptop under .NET Aspire for development, in a Linux container, so it can never pick up a person's own settings or login. What works locally goes to Azure with the same code.
Evidence, not agreement
A change does not count because the builder says it is done, nor because two models agree. The verifier, with no model and no credentials, rebuilds the change from scratch and checks per acceptance test that it fails before and passes after the change. That evidence belongs to that exact commit; if the code changes, the evidence has to be produced again.
Then the second review. We have a model from another vendor look because two models from the same vendor share the same blind spots. Codex runs in its own short sandbox with read-only access, so the reviewer and the builder never share credentials, and hands its findings to the builder's still-running sandbox. If the review finds something, the builder gets at most two repair rounds. If they still disagree, the item goes to a person, not into another loop.
So the pull request an engineer finally sees carries three things: the verifier's green evidence for that exact commit, the reviewer's findings, and the link to the work item. The repository enforces the policies that apply to people too: at least one reviewer who is not the author, a linked work item, and a passing build.
What the agent cannot do
The question every management team asks: what if the agent does something stupid, or someone plants an instruction in a work item? The answer is layers that do not depend on the agent behaving. Every layer is enforced outside the agent, so if one fails, the next still holds.
Identity. The builder's identity cannot bypass a policy and cannot complete a pull request. Each role has its own sandbox group with its own identity, because code inside a sandbox can request its own group's token; so the identity's rights are the agent's rights, and we keep them as small as possible per role.
Branch policies. A direct push to main is refused. On main a minimum number of reviewers applies, the author's vote does not count, a linked work item is required and the build must pass.
Network. Closed, except for a list per role: the model API, the repository, the package registries. Everything else is refused and logged. The agent never holds an API key: it sends its requests with a placeholder and the egress proxy swaps in the real key on the way out. Code in the sandbox cannot read that key.
Procedure. The text of a work item is data, not instructions. A planted instruction must come back flagged as suspicious content, not executed. Pull requests follow a fixed form, and the "skip" label always wins.
Budgets. Every run has a ceiling in money, turns and time, every stage a daily budget, and the model vendors' own spend limits are the hard backstop.
Switches. The whole platform, one stage, one kind of write or one item can be stopped with one switch. The build stage switches itself off after a run of handoffs to people or after several abandoned pull requests in a short time. The last resort is disabling the identities themselves.
The default on any doubt is to stop and hand over. Errors, missing evidence, disagreement and exhausted budgets go to a person, never to a retry loop and never to a merge.
Every run measured
Every run writes a row in a ledger: the item, the role, the tokens per model, the price from a versioned price table, and the tool's own estimate. Those rows roll up per item, per merged pull request and per handoff to a person, and are reconciled monthly against the vendors' invoices. So we know per work item what it cost, which the model vendors' bills cannot tell on their own. Model tokens are the cost; the sandboxes' compute is a fraction of that.
Why in our own Azure environment
We run the line on our own Azure rather than on a hosted agent service, for four reasons. Our repositories live in Azure DevOps and the line has to push and open pull requests there under its own identity. Our test suites need Docker. We want a reviewer from another vendor, which is only possible on a platform we assemble ourselves. And the work stays in West Europe, with no session transcripts stored at a third party. It costs us more to operate: our own image, proxy rules, a controller. We think that is the right price.
What you see, and how to start
From the outside the line is invisible. What you see is a pull request in your own repository: a tested change, linked to the work item, with the evidence attached and an Enigmatry engineer's approval on it. What you do not see is just as important: no code on anyone's laptop, no secrets in the agent's hands, no transcripts at a third party, and nobody merging because two models agreed.
The line is one half of how we build; the other half is the foundation the agents build on, Entry. Together they are what makes the SaaS Factory fast without making it careless. What our agents may touch and what they never see is also on the security page, in your security officer's language.
Questions we get about this
Does an agent ever merge on its own? No. An engineer completes every merge, and the agent's identity could not do it even if it wanted to.
Does an agent see production data? No. Agents work in the same anonymised environments as our developers. Production is for operations only, on a need-to-know basis.
What if someone puts an instruction in a work item? The text of an item is data. An instruction inside it is flagged as suspicious content and the item goes to a person. And even if that failed, the identity, the branch policy and the network still hold.
Can I stop the line? Yes, per item with one label, and on our side per stage, per kind of write or for the whole platform. A person's change always wins.
Which models do you use? A fast model to sort, a stronger model to check and build, and a model from another vendor to review. Which exact versions changes with the months; the roles and the limits do not.