The teams getting real, compounding value from AI coding agents are not the ones with the most capable model. They are the ones who gave the agent the means to check its own work, and then extended autonomy in proportion to what that agent could prove, not what it promised. Verification is not the toll booth on the road to automation. It is the road. An agent that can run your test suite, read your linter’s output, execute code in a sandbox, and iterate on its own failures has earned the right to work with less supervision. An agent that cannot do any of that has not, no matter how good the underlying model is.
This is a practical, technical argument about how to design agentic coding workflows, not a cautionary tale about AI risk. The thesis: autonomy should be a function of verification capability that you deliberately build, not a default setting you turn up because a model feels trustworthy.
The industry has already found the ceiling on “just prompt it”
The data on AI-assisted development in 2025 tells a consistent story: the model is not the bottleneck anymore. The absence of a verification loop is.
The 2025 DORA State of AI-assisted Software Development report, based on survey responses from nearly 5,000 technology professionals, found that 90% of respondents now use AI at work and over 80% believe it has increased their productivity. But the report’s central finding is not about productivity. It is about amplification: “AI doesn’t fix a team; it amplifies what’s already there.” Teams with strong automated testing, mature version control, and fast feedback loops get faster and more stable. Teams without those things get faster and less stable, because AI increases the volume of change moving through a pipeline that was never built to absorb it. Reversing last year’s finding, AI adoption showed a positive relationship with both throughput and product performance, while its relationship with delivery stability remained negative. However, it is worth mentioning that the relationship with delivery stability stayed negative wherever those control systems were missing.
Read that carefully. The instability is not caused by the AI writing worse code than a human would. It is caused by more code, of unknown quality, entering a system that has no automated way to catch problems before they ship. The lever a team has here is not “use a better model.” It is “build the control system that lets you safely absorb more change,” which is precisely what a verification loop is.
Trust data backs this up from the other direction. Sonar’s 2026 State of Code Developer Survey found that 96% of developers do not fully trust AI-generated code, and only 48% always verify it before committing. Stack Overflow’s 2025 survey found trust in AI accuracy has fallen, not risen, over the past year, down to 29% from around 40% previously. 66% cite “AI solutions that are almost right, but not quite”, and teams report spending close to a quarter of their working week checking, fixing, and validating AI output. Lightrun’s 2026 State of AI-Powered Engineering Report, reporting on the same wave of surveys, put a number on the downstream cost: 43% of AI-generated code changes require debugging in production.
None of this means AI-generated code is a bad idea. It means unverified AI-generated code is a bad idea, which is a different and much more solvable problem. The developers who distrust AI output are not wrong to distrust it in the abstract. They are usually describing a workflow where the only verification step is a human reading the diff, which does not scale and was never going to.
The reframe: verification is not a gate on autonomy, it is the mechanism that produces it
Here is the design principle that falls out of the data above: an AI coding agent should get more autonomous scope exactly to the degree that it can independently prove its own output is correct, and no further.
That sounds obvious once stated, but it inverts how most teams actually configure their tools. The common pattern is to grant an agent broad permissions (“edit any file, run any command”) on the theory that the model is good enough to be trusted, and then hope verification catches whatever goes wrong. The better pattern is to treat verification capability as the precondition for autonomy, not the safety net underneath it. Concretely:
- An agent that can only generate a diff needs a human to read every line, because nothing else stands between its output and the codebase. Autonomy here should be minimal: suggestion, not execution.
- An agent that can run the test suite and read the output can be trusted to iterate on its own failures before a human ever sees the code, because it has a feedback loop that does not depend on human attention.
- An agent that can also run the linter, the type-checker, and execute the code in a sandbox can be trusted with a meaningfully larger unit of work (a full feature, not just a function), because more of the ways it could be wrong are things it can detect itself.
- An agent that can additionally check its output against a defined eval or acceptance criterion can be trusted with genuinely autonomous tasks: pick up a ticket, implement it, verify it, open a pull request, and only interrupt a human when something doesn’t pass.
The autonomy is not granted by the model getting smarter between these stages. It is granted by the tooling around the model getting richer. This is the part that is straightforward to build and easy to skip: most teams stop at stage one or two because it’s the fastest way to get a demo working, then wonder why they can’t extend the agent’s scope without incidents climbing.
What “wired into the verification loop” actually means in practice
This is where the argument has to get specific, because “give the AI tools to verify its work” is easy to say and requires real engineering to do properly. Six things need to be built, not asserted in a prompt.
1. Running the test suite, not just generating tests, as a scoped and fast loop
An agent that writes a test and an agent that runs a test suite and reads the result are doing fundamentally different things. The first produces an artifact a human still has to trust. The second produces a pass/fail signal the agent itself can act on: if the suite fails, it has the test runner’s actual output in front of it and can iterate before handing anything to a person. The loop is mechanically simple:
1. Agent edits code.
2. Agent runs the test command (or a hook runs it automatically).
3. Runner exits non-zero: agent parses the failing assertion, the file, the line.
4. Agent edits again, targeting that specific failure.
5. Repeat until exit code 0, or until an iteration budget is exhausted
and a human is pulled in.
The detail that decides whether this loop is actually useful is speed. A full suite that takes forty minutes gives an agent, or a human, roughly one shot per coffee break, which defeats the point of an agent that can otherwise iterate in seconds. This is where test impact analysis matters as much as the tests themselves: tools that map source files to the tests that exercise them, pytest --testmon, Nx (nx affected --target=typecheck), or Turborepo’s --affected flag in JavaScript monorepos, Bazel’s dependency graph in polyglot builds, let an agent run only the tests that could plausibly be affected by its last edit, in seconds rather than minutes, and fall back to the full suite before anything merges. Without that scoping, teams either wait out a slow suite every iteration or, more commonly, stop running it at all and let the agent guess. A verification loop an agent will not actually use because it is too slow is not a verification loop.
2. Reading real compiler, linter, and type-checker output, in a structured, parseable form
A model that has to guess whether its code compiles is guessing. A model that can execute tsc --noEmit, mypy, ruff check, or the project’s actual lint command, and read the literal output, is not. The distinction that matters technically is between prose output and structured output: eslint --format json, ruff check --output-format=json, and mypy --output=json all emit machine-parseable arrays of {file, line, column, rule, message} records rather than a wall of formatted text. An agent working from structured output can go directly to the offending line and rule rather than re-deriving location from a human-oriented report, which matters once a single run produces dozens of findings across a large diff. This is a small implementation choice, pointing the agent’s tooling at --format json instead of the default human-readable output, but it is the difference between an agent that reliably fixes what a linter flags and one that periodically misreads which line a warning was about.
None of this requires the model to be smarter. It requires the agent to have a shell, permission to run the same commands a developer would run, and output in a shape it can act on precisely.
3. Permissions scoped to verification, not just execution generally
The instinct when giving an agent the ability to verify its own work is to grant it a shell and hope for the best. The better approach treats the permission model itself as a verification control. Claude Code’s settings support explicit allow, deny, and ask lists, scoped to specific commands:
{
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run typecheck)",
"Bash(npm test *)"
],
"deny": [
"Bash(curl *)",
"Bash(rm -rf *)",
"Read(./.env)",
"Read(./secrets/**)"
],
"ask": [
"Bash(git push *)",
"Bash(npm publish *)"
]
}
}
This is not a blanket “run any command” grant. It is a narrow one: the agent can run the lint command, the type-checker, and the test suite without asking, because those commands only read the codebase and report on it, they cannot ship anything or exfiltrate anything. Deploying, force-pushing, or touching secrets stays behind an explicit ask or a deny. The permission boundary is doing the same job as the tiered-autonomy argument earlier in this piece, applied at the level of individual commands rather than whole tasks: verification actions get a wide grant because their blast radius is near zero, and state-changing actions keep a human or a stricter policy in the loop regardless of how much the agent has already proven.
4. Sandboxed execution as the precondition for a wide verification grant, not a substitute for one
Sandboxing solves a related but distinct problem: how do you let an agent run arbitrary commands, install dependencies, and execute code to verify its work, including commands you have not enumerated in advance, without that becoming a security or blast-radius problem? Claude by Anthropic is a useful concrete example here. Anthropic’s engineering write-up on Claude Code’s sandboxing describes filesystem isolation (the agent can only touch defined directories) and network isolation (traffic goes through a proxy to an approved allowlist), enforced at the OS level using Linux bubblewrap and macOS Seatbelt. The result, per Anthropic’s own numbers, is that sandboxing cut permission prompts by 84%, because the boundary is set once, structurally, rather than negotiated action by action.
This matters for the verification argument specifically because a sandbox is what makes it safe to let an agent actually run things: install a package, execute a test suite, spin up a local server and hit an endpoint, without a human first confirming that each individual command is fine. Anthropic has also published on why per-action approval breaks down at scale: internal data showed users approve 93% of permission prompts, which is the classic shape of approval fatigue, a rubber stamp that provides the feeling of oversight without much of the substance. The fix was not more prompts. It was defining a boundary an agent could operate freely inside, a verification and containment problem, not a model-capability problem. Filesystem isolation, network allowlisting, and the permission scoping above are complementary controls, not alternatives: the sandbox bounds what a command can reach, the permission list bounds which commands run without a human, and together they are what makes a wide verification grant safe to hand out by default.
5. Hooks: making verification a structural property of the workflow, not a habit
Claude Code’s hooks system lets a team wire verification steps directly into the agent’s execution loop, as a shell command that fires on a defined event rather than something the model has to remember to do. A PostToolUse hook that runs after every file edit looks roughly like this:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npm run lint && npm run typecheck"
}
]
}
]
}
}
The behaviour that makes this a verification mechanism rather than a convenience feature is the exit code contract. The hook receives the tool call as JSON on stdin (tool_name, tool_input, and, for PostToolUse, the tool’s own response) and its exit code decides what happens next: exit 0 and the check passed silently; exit 2 is a blocking failure, and Claude Code feeds the hook’s stderr straight back to the model as an error it has to address before doing anything else; any other exit code, 1, 3, 10, whatever, is a non-blocking error, logged and visible to a human reviewing the transcript, but the agent carries on regardless. That blocking case, exit 2, is the one that matters: it converts “the linter found a problem” from information the agent might notice into an instruction it cannot proceed past. A PreToolUse hook can use the same exit code, or return structured JSON with a permissionDecision of deny, to block a dangerous command before it runs at all rather than reacting after the fact.
This is the mechanism that turns “the agent should verify its work” from a prompting instruction, which is unreliable, into a property of the system, which is not. An instruction can be ignored under context pressure or a long session. A hook fires every time, because it is not the model choosing to comply, it is the harness enforcing it.
6. CI as the second, independent gate, not a rerun of what already happened locally
Everything above runs inside a single agent session, which means it is only as trustworthy as that session: a hook can be misconfigured, a permission list can be too permissive, a local environment can drift from what actually ships. The structural fix is the same one teams already apply to human-written code: nothing merges without passing an independent check in CI, run in a clean environment, regardless of what happened on the author’s machine.
# .github/workflows/verify.yml
on: pull_request
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm test -- --coverage
This is not redundant with the local loop, it is what makes the local loop trustworthy. The local hooks and permission scoping give an agent a fast, cheap feedback signal to iterate against, in seconds, inside a session. The CI gate gives the organisation a slow, expensive, but structurally independent check that does not care what the agent claimed to have verified locally, because it reruns the checks itself, from a clean checkout, before anything reaches a branch a human or another service depends on. An agent that can only produce a diff still needs this gate. An agent wired into a full local verification loop needs it just as much, for the same reason a senior engineer’s locally-passing tests still get rerun in CI: local success is a strong signal, not a substitute for an independent one.
The practical implication of building all six of these: if verification lives only in the prompt (“please run the tests before finishing”), you are trusting the model’s judgement about when to comply. If verification lives in the tooling, scoped permissions, hooks that fire on file writes, a sandbox that bounds the blast radius, and a CI gate that reruns everything independently, you are trusting the system, which is a much sounder basis for extending autonomy.
Why this is the correct order of operations, not just a safer one
There is a version of this argument that sounds purely defensive: verify more so that things go wrong less often. That is true, but it undersells the case. The teams that build a real verification loop do not just avoid incidents. They unlock throughput that ungated teams cannot safely reach, because they can extend the size and independence of the work they hand to an agent without a proportional increase in review burden.
Consider the trade-off. A team that reviews every AI-generated line manually has a review bottleneck that scales linearly with how much code the agent produces: more output, more human review time, full stop. A team that has the agent run its own tests, type-checker, and linter before a human sees the diff has moved most of that burden into a loop the agent runs itself, in seconds. The human review that remains is qualitatively different: judgement calls and architectural fit, not “does this compile.”
This is also why the DORA report’s finding about instability is not really an argument against giving agents more autonomy. It is an argument against giving agents more autonomy without the control systems that make instability visible and self-correcting before it reaches production. A team with strong automated testing and fast feedback loops can absorb a much higher volume of AI-generated change than a team without those things, at the same or lower incident rate, because the verification loop is doing the work that a slower, purely human review process used to do.
Where the stakes make this non-negotiable
This calculus gets sharper wherever the cost of a bad deployment is not “revert and retry” but “regulatory exposure, patient harm, or a grid outage.” In financial services, a coding agent operating against a payments system needs verification that includes compliance and audit checks, not just unit tests. In healthcare, an agent touching anything adjacent to clinical data or decision support needs verification gated against the same standards a human-written change would face. Energy, telecoms, and logistics systems carry similar weight: the acceptable failure mode for an unsupervised change is usually much narrower than “the deploy pipeline catches it.”
None of this means AI agents are unsuitable for regulated environments. It means the verification bar, the permission scoping, the hook policies, the CI gates, has to be built to match the domain before autonomy is extended, not after an incident reveals it wasn’t there. The principle does not change: autonomy tracks verification capability. Regulated industries simply need a sharper, more specific verification bar before they extend the same scope a less regulated team might grant sooner.
How Zartis helps
Most organisations do not lack access to AI coding agents. They lack the verification architecture, scoped permissions, hooks, sandboxing, and CI gates wired together, that would let them extend those agents more autonomy without increasing risk. That gap is an engineering and process problem, not a model problem.
Zartis is a Preferred Services Partner in the Claude Partner Network, and works with engineering teams on both sides of that gap: advising on where an AI-enabled SDLC needs test coverage, permission and hook design, sandboxing, and eval work before autonomy is extended, and then building that infrastructure directly, wiring Claude, powered by Anthropic, into a team’s actual verification loop, from the permission lists and hooks that make it safe to run rather than just suggest, through to the CI gates that check its work independently. This spans AI Development, AI-Enabled SDLC, and AI Governance work across regulated and non-regulated environments alike.
The recurring failure mode we see is teams treating agent autonomy as a setting to increase once the model feels good enough, without first building the tests, permission scoping, hooks, and sandboxes that let that autonomy be exercised safely. We help teams do the harder, more durable version: design the verification loop first, then extend autonomous scope in step with what the agent can actually prove, so the throughput gains in the DORA data show up without the instability that comes from skipping the control systems that make them safe.
Sources
- DORA, “2025 State of AI-assisted Software Development Report”
- Google Cloud Blog, “Announcing the 2025 DORA Report”
- Sonar, “State of Code Developer Survey report: The current reality of AI coding”
- Lightrun, The State of AI-Powered Engineering 2026
- VentureBeat, “43% of AI-generated code changes need debugging in production, survey finds”
- Anthropic, “Making Claude Code more secure and autonomous with sandboxing”
- Anthropic, “Enabling Claude Code to work more autonomously”
- Claude Code Docs, “Automate actions with hooks”
- Claude Code Docs, “Hooks reference”
- Claude Code Docs, “Identity and access management”