cd /news/ai-agents/how-i-trust-ai-agents-to-ship-enterp… · home topics ai-agents article
[ARTICLE · art-86183] src=pub.towardsai.net ↗ pub= topic=ai-agents verified=true sentiment=· neutral

How I Trust AI Agents to Ship Enterprise-Grade Code Without Reviewing Every Line

A founder who relies on an AI agent to write most of the code for a multi-tenant B2B product describes a nine-phase pipeline with guardrail hooks, three AI reviewer personas, and a seven-check gate wall that physically prevents the agent from merging its own pull requests. The system enforces process through machinery, not intentions, and includes a routing decision that tiers changes by danger level, with safety-critical milestones as hard blocking edges in a graph-based issue tracker.

read14 min views1 publishedAug 4, 2026

The setup that earned that trust: nine phases, guardrail hooks, three AI reviewers, a seven-check gate wall, and the one button an agent can never press.

I ship a multi-tenant B2B product with row-level security, database migrations, background workers, and a security posture I have to defend to partners. Most of the code is written by an AI agent. Not autocomplete. An agent that claims its own tasks, opens an isolated worktree, writes the failing test before the implementation, gets its diff torn apart by three reviewer personas, opens the pull request, and is then physically incapable of merging it.

That last clause is the whole post.

I should be honest about who’s writing this. I’m not a career infrastructure engineer. I’m a founder who was, until recently, closer to the “vibe coder” end of the spectrum than I’d have admitted at a dinner party. My unfair advantage was never typing speed or a decade of Postgres scars. It’s that when I started letting an agent do the work, I got obsessive about the system around the work. The counterintuitive lesson of the last year:** the less code you write yourself, the more process you need, and that process only counts if it’s enforced by machinery rather than intentions.**

Addy Osmani drew the line well: vibe coding is not the same as AI-assisted engineering. But most of what I read on this topic stops at advice. Review the code. Write tests. Add guardrails. Sure. A rule that lives in a blog post protects nobody. A rule that lives in a pre-execution hook protects you at 2 a.m., on the day you’re tired, which is the only day that matters.

So instead of advice, here is the pipeline itself. Every node in the images below was verified against my live repository on the day I captured them: the hook source files, the CI workflow definitions, the lint rules, the agent briefs, the deploy runbooks. Nothing aspirational. Nine phases, one always-on guardrail band, and exactly one button an agent can never press.

If nine phases sounds like enterprise theater, skip to the five moves at the end, steal those, and come back when one of them saves you. A session doesn’t start with a prompt. It starts with a contract: if a skill exists for the thing you’re about to do, you invoke it before doing anything, including asking clarifying questions. Then context loads in layers: global rules, repository rules, per-package rules, a domain glossary, and long-term memory from previous sessions.

Then the task itself. Work lives in a graph-based issue tracker (bd, from the beads project), not in a plan document. bd ready lists only tasks whose dependencies are genuinely cleared. My eight safety-critical milestones are hard blocking edges in that graph, so the agent cannot pull dangerous work early even if it wants to. It claims the task so a second session can’t race it.

The last step is the one I’d tattoo somewhere visible: facts over memory. The agent must git fetch and inspect the remote before asserting anything about branch state. I learned this the way everyone learns things. A stale local checkout once made a fully merged piece of work look unmerged, and an afternoon went into “fixing” a problem that did not exist. Memory, the agent’s and mine, is a map of what was true when it was written. The repo is the territory.

Before any code, one routing decision: what does this change touch? Not “how big is it.” How dangerous is it. A one-line diff that touches row-level security gets the full ceremony: brainstorm, spec, adversarial review, plan, then build. A four-hundred-line refactor of a leaf module doesn’t need a spec at all. A typo fix takes the fast path, though even the fast path keeps two non-negotiables: tests, and a branch.

This tiering is what keeps the pipeline alive. If every change costs the full ceremony, you start skipping ceremony, and you will skip it on precisely the day it mattered. Process guilt kills pipelines faster than process gaps do. The router also has a promotion rule: the moment a “fast” change starts growing opinions, it stops and gets promoted a tier.

For the risky tier, the agent is not allowed to open an editor yet. A brainstorming skill owns the conversation: intent, requirements, alternatives, edge cases. Two vocabulary layers run inside it, one for module and interface design, one that maintains the project’s shared language, because agents will happily invent four synonyms for the same concept and a glossary is much cheaper than the refactor that follows. Two rules make this phase more than a ritual. First: a design decision without a source is a vibe. Non-obvious choices get checked against primary sources (RFCs, OWASP, vendor docs, how mature products solved the same problem) while designing, and the spec records what was checked against what. The verification outlives the chat window.

Second, high-risk specs get red-teamed before planning. Ahead of my authentication unit, I had a domain-expert persona attack the draft spec as an adversary. It found eleven real issues, including a session-fixation angle, a fail-open reconciliation path, and an IDOR in an admin flow. All eleven were folded into the spec before a single line of code existed. Cost: one evening. The alternative cost: eleven code reviews, or eleven incidents.

Decisions that are hard to reverse and the result of a real trade-off get an architecture decision record. Everything else explicitly does not, because an ADR pile nobody reads helps nobody.

The spec becomes a plan: small steps, each with its own verification, written well enough that a fresh session with no context could execute it. Then the work gets a home. Every unit of work runs in its own git worktree, on its own branch off the remote main. Direct commits to main are forbidden. For me too.

The worktree rule reads like bureaucracy until the first time two agent sessions race a single checkout. Mine collided with one session’s half-finished state sitting under another session’s feet. Since then, isolation is not a preference. It also unlocks something better: independent tasks can fan out to parallel sub-agents, each in its own workspace, with no shared state to trip over.

The build loop is strict red-green-refactor: failing test first, minimal code to green, then clean up. With an agent doing the typing, this is less about coverage and more about steering. The failing test is the prompt. It converts “write something plausible” into “make this specific assertion pass,” and plausible-but-wrong is the agent failure mode that eats days.

Two support beams. When the agent needs to understand code, it queries a code graph (symbol source, call paths, blast radius in one round trip) instead of grepping and guessing. When it needs the database, it goes through a read-only MCP server against a throwaway local Postgres, and a Postgres best-practices skill loads before any schema or RLS change, even a one-column diff.

One hard rule for debugging, learned from watching an agent flail: two failed fixes means stop. Re-enter systematic diagnosis and re-examine the hypothesis. An agent that has failed twice has usually stopped debugging and started guessing.

Everything above is workflow. This band is enforcement, and it runs regardless of phase, mood, or how convincing the agent sounds.

A pre-execution hook screens every shell command and blocks the unrecoverable ones: recursive deletes, force pushes, DROP and TRUNCATE, anything that names the production environment. It also blocks the GitHub verbs an agent should never own: merging a PR, approving a PR, re-running CI. Merging is a human’s job by construction, not by convention. A post-edit hook runs the typechecker, linter, and architecture scanner on every file the moment it’s written. That one is advisory, so it warns without breaking flow.

The architecture rules themselves are eight enforced invariants (things like “no raw database driver outside the db package” and “exactly one Slack signature verifier”), plus hard complexity ceilings: no file over 700 lines, no function over 200.

My favorite part is the least glamorous. Every guardrail that fires appends one line to a local telemetry log, because rules have to earn their keep. An advisory rule that fires constantly is a candidate for promotion to a hard gate. A hard gate that hasn’t fired in months is dead weight and gets deleted. I treat the process itself as a product, and I measure it like one.

Before any PR exists, the diff faces three reviewer agents, each with its own brief and its own model. A paranoid staff-engineer persona hunts correctness bugs, tenant-isolation leaks, and N+1 queries, and must answer in MUST FIX / SHOULD FIX / CONSIDER with file-and-line citations. A security reviewer works against my product’s threat model (cross-tenant leakage, RLS silently not applying, ACL derivation, replay attacks on webhooks) rather than a generic OWASP checklist, because generic checklists produce generic findings about problems you don’t have. A test-writer fills coverage gaps with the integration tests that matter, against a real Postgres.

The subtle half of this phase is receiving the review. Agents love to agree. Ask one to fix a finding and it will implement the fix whether or not the finding was real. So there’s a skill for taking feedback: verify each finding against the code before acting on it. And the exit gate is unconditional. Nothing gets called done without running the build, the linter, the full test suite, and the invariant scan, then reading the output. Evidence before claims, always.

The PR runs a wall of seven checks: build plus tests, secret scanning, static analysis, the same eight architecture invariants as local, SAST on the CI workflows themselves (every action pinned to a commit SHA), an AI code review posted as a comment, and one meta-gate I’ll get to.

Here’s the war story that shaped this phase. My AI reviewer once “passed” with a green check and a job marked successful, while posting a review with no findings in it, because the model had returned nothing usable. I read the checkmark instead of the comment and merged. Weeks later it happened again, differently: the review got cut off mid-word by a token limit. Same green check.

The fix was not “be more careful,” because be-more-careful doesn’t survive contact with a Tuesday. The fix was structural, twice over. A triage command now reads the gates and classifies every failure as FINDINGS (my diff’s problem), TOOLING (the reviewer or CI broke, which gets logged and never blamed on the code), or WAITING. And a new CI gate makes the discipline mechanical: the PR cannot merge until a triage comment postdates the latest review. A re-review resets the clock. The gate doesn’t judge the findings. It verifies that a human disposition exists.

Then the merge itself: squash, to main, performed by me. The agent cannot do it. Not “shouldn’t.” The hook rejects the command.

Merge to main auto-deploys three services from one monorepo to staging in an EU region, config-as-code per service. Database migrations run in exactly one place: the worker’s release phase, under a Postgres advisory lock. The web and API services never migrate. Boring, deliberate, correct.

The reason there’s a whole verification step after deploy is a platform gotcha that cost me a real debugging cycle. If one dashboard field is unset, the platform silently ignores your config file, and your migration never runs while the deploy reports success. Green banner, stale schema.

So there’s a read-only command that refuses to take the platform’s word for it. It pins one commit and demands two pieces of evidence: the resolved deploy manifest contains the migration command, and the migrator logged “migrations applied.” Its exit codes encode an epistemology I wish more tooling had: confirmed, failed, and inconclusive, where inconclusive is never treated as failure, because absence of evidence isn’t evidence of absence.

Production, meanwhile, is not in this diagram’s automated path at all. Promoting to prod is a human acceptance step, and the firewall hook rejects any agent-run command that so much as names the production environment.

When the work is verified, the task graph gets closed and exported so it travels with the repo. The executed plan gets a status banner naming what superseded it. The built artifact is now the truth; the plan survives as the record of why. A sweep script cleans up merged worktrees, trusting only the platform’s merge state (squash-merges blind normal git ancestry checks) and reporting any merged worktree that still holds uncommitted work instead of deleting it. That reporting once saved three files I’d have quietly lost.

Then the part that compounds. Three feedback loops run across every unit of work: the guardrail telemetry that promotes and retires rules; the per-PR triage log that turns “the reviewer was flaky once” into a visible pattern with dates; and session memory that carries hard-won gotchas into the next task, so no lesson gets paid for twice. A weekly cron watches the supply chain: dependency updates, audits into a rolling issue, and a seven-day cooldown before any new package release is allowed in.

This is my answer to “isn’t all this process slow?” The pipeline gets faster every week because of the process. The overhead is the investment. The flywheel is the return.

Everything above, mapped: which skill owns which phase, which MCP servers the agent can reach, which models the reviewer agents run on. One deliberate absence worth calling out. GitHub is driven through the ordinary gh CLI, permission-gated and hook-filtered, rather than an MCP server, precisely so the dangerous verbs stay inside the firewall’s blast radius.

For the curious: the stack is Claude Code with the open-source superpowers skill library, beads (bd) for the task graph, ast-grep, Semgrep, gitleaks, and zizmor for the scanning wall, Vitest and Drizzle in a pnpm monorepo, Railway for deploys, and Sentry (behind an allowlist scrubber) for errors. Nothing here is exotic. That’s rather the point. Fairness demands a paragraph the genre usually skips. This pipeline does not make product decisions, and it does not absolve me from reading code: on the risky tier I still read every line of the diff, just with three agent reviews in hand before I start. It did not appear in a weekend either. It accreted over months of evenings, one guardrail at a time, and nearly every node exists because something specific bit me first. And it does not eliminate incidents. It changes their anatomy: when something breaks now, the logs tell me whether the pipeline missed it or I overrode the pipeline. So far the honest answer is usually the second one.

You don’t need the whole pipeline. These five are cheap, and each one removes a category of disaster:

  1. Never let the agent touch main. A branch or worktree, every time, even for a typo. Isolation converts “the agent broke everything” into “the agent broke a branch.”

  2. Make the failing test the prompt. Ask for the test first, then for code that passes it. This one habit converts plausible into correct more reliably than any system prompt.

  3. Write one hook that blocks the scary verbs. Recursive delete, force-push, merge, anything naming prod. Mine is a small Python script. It is the best code I didn’t have to write well.

  4. Read the review body, never the checkmark. Whatever reviews your code, AI or human, verify a review actually happened before you act on its silence.

  5. After a deploy, demand one line of evidence. Not the dashboard’s green banner. A log line proving the thing you feared didn’t happen, happened correctly.

All five fit in a weekend. The rest is compounding.

Speed, yes, but that’s the boring half. The real purchase is that speed stopped costing certainty. Every claim in my repo has a receipt: the spec cites its sources, the review posts its findings, the deploy proves its migration, the merge carries a human’s disposition on every gate.

I don’t review every line of code anymore. I review the system that reviews the lines. For a founder shipping mostly alone, that inversion isn’t a productivity hack. It’s the difference between an AI-assisted engineering org of one and a very fast way to accumulate regret.

One question before you go, because I collect these: what’s the one verb you’d never let an agent run? Mine was merge. The comments are open.

I’m Bram van Gestel, a founder in the Netherlands building an AI-heavy, multi-tenant B2B product as (mostly) a one-person engineering org. I write about shipping with agents without losing the plot: the hooks, the gates, and the war stories behind them. If that’s your kind of thing, follow along. The next post walks through the adversarial spec review that found eleven security issues before a line of code existed.

How I Trust AI Agents to Ship Enterprise-Grade Code Without Reviewing Every Line was originally published in Towards AI on Medium, where people are continuing the conversation by highlighting and responding to this story.

── more in #ai-agents 4 stories · sorted by recency
── more on @addy osmani 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/how-i-trust-ai-agent…] indexed:0 read:14min 2026-08-04 ·