The story usually told about AI and infrastructure is a single bad exchange. Somebody asks an assistant for a Helm chart, asks whether it is good, gets a yes, and a week later the same model agrees it was wrong all along. That effect is real and well documented, and it is the small version of the problem.
The larger version is what happens when you use a model as a reviewer. Below is one of those, from a client engagement this year. The chart was bad in ways that were written into the files. Two models read those files and said it was fine. The conclusion we came away with is narrower and harder than the usual advice about context windows: on the question “is this good”, the answer a model gives you carries no information, and neither does the answer it gives you after you disagree with it.
The three months nobody was watching
An autonomous agent had been building a client's Helm chart for about three months. Not an engineer with an assistant open in another window, an agent running on its own: committing, iterating, carrying on. Nobody opened the result in that entire period, because nothing asked them to. The pipeline ran, deployments went out, and there was no failing build to investigate.
We looked inside only because somebody asked us to look. That detail is worth keeping, because it is the only reason any of this surfaced. There was no incident, no alert and no complaint. There was a request for an opinion.
What was in there was not one exotic mistake. It was close to the full catalogue at once: a migration running in an initContainer, image tags floating instead of pinned to digests, one Dockerfile copied across four services, permissions far wider than anything needed, and a readiness probe pointing at a path two of the services do not serve. It went past the usual list. The agent had also built its own template compiler on top of Helm’s templating, and its large values files copied the Kubernetes API almost field for field. Every file was valid YAML. Everything rendered. The cluster accepted all of it.
Four of those five are readable from the files alone. You do not need the cluster to see that a migration in an initContainer runs once per pod, that a floating tag is not a version, that the same Dockerfile has been pasted four times, or that a role grants more than the workload uses. Only the probe path requires knowing what the application serves. Keep that proportion in mind, because it is what the rest of this article turns on.
Then came the part that is about AI rather than about Kubernetes. The problems were already known at that point. What was needed was a readable write-up for the client, so the chart went to Claude with a request to analyse it and describe what was wrong.
Claude said the chart was fine. Well structured, sensible, production-ready. Codex, from a different vendor and a different training run, said the same thing about the same files. Neither found a single one of the problems sitting in front of it, including the four that were written into the YAML.
Only when the problems were put to them one at a time did either model move, and then it moved all the way. Both concluded that their own analysis was not relevant and could be discarded. That is the finding worth sitting with. The approval said nothing about the chart, and the retraction said nothing about it either. Neither output was a judgement that could be wrong, because neither was a judgement.
A second model is not a second opinion
The instinct to ask a different model is a good instinct applied to the wrong axis. It assumes the two are independent reviewers. Two models from two vendors agreed on the same files, so whatever they were doing, it was not two independent reads.
Three effects are usually offered to explain this. Two of them are real and neither covers what happened here, which is why the third matters more than it looks.
Models prefer their own output. This is measured rather than anecdotal. LLM Evaluators Recognize and Favor Their Own Generations, presented at NeurIPS 2024, found that models including GPT-4 and Llama 2 can distinguish their own text from text written by other models and by humans at non-trivial accuracy, and that the strength of self-recognition correlates linearly with the strength of self-preference. The authors ran controlled experiments to show the relationship is causal rather than a confound. In practice this is the thing engineers notice as a personality quirk: hand a model your own hand-written manifest alongside the one it generated, ask which is better, and it will reliably pick its own. It is not being stubborn, it is recognising its own signatures.
A different model removes that effect and changes nothing. Self-preference explains the case where the author grades its own work. It does not explain this one. Neither model wrote the chart, and both passed it anyway, so the effect that gets quoted most often is the one with the least to do with the failure in front of us.
Agreement is cheap in both directions. Push back and the position moves. SycEval, presented at AAAI/ACM AIES, found sycophantic behaviour in 58.19% of exchanges across the models it tested, of which 43.52% moved toward the correct answer and 14.66% away from it, with the reversal persisting in 78.5% of cases. That describes a model that follows the pressure in the room. It is the closest thing the literature has to what we saw, and it still understates it, because neither model refined its position under pressure. Both abandoned it.
Put the three together and the comfortable explanations run out. Self-preference did not apply. A second vendor did not help. Sycophancy describes the collapse but not the original approval, because nobody had pushed yet when the first model called the chart production-ready.
Which leaves every direction empty. An approval tells you the text looked like other approved text. A second approval tells you it looked that way to two systems trained on similar material. A retraction tells you somebody pushed. None of the three is a fact about your chart.
The context explanation, and where it stops working
The usual account of this failure is about missing information. A Kubernetes manifest is only correct relative to a system: the probe path lives in the application's router, the replica count lives in a capacity decision, the storage class lives in the cluster. None of that is in the prompt, so the model fills the gap with the most common value in its training distribution and sounds confident doing it. That account is true, and it covers exactly one of the five problems in this repository.
The other four needed nothing the model did not have. A migration in an initContainer is wrong for reasons contained in the manifest. A floating tag is not a version in any cluster. Four copies of a Dockerfile are four copies in any repository. A role granting more than the workload uses is visible beside the workload. Both models had all of it on screen and reported that it was production-ready, so whatever went wrong here is not a context problem with a context fix.
When that information is missing, the model does not stop and ask. It fills the gap with the most common value in its training distribution: /healthz, three replicas, a floating latest tag, a sidecar that looks like the sidecars in the examples. Those are plausible defaults, and plausible is exactly the failure mode, because plausible is what survives a read-through and what a second model rewards.
Three things that passed review and broke later
All of this came out of the same repository, and all of it was read by Claude and by Codex and pronounced fine.
The examples below are samples, not the point. The chart ignored best practices at every level, including the ones a beginner guide spells out in a paragraph. Where it did not use a textbook anti-pattern, it invented its own approach, and that is harder to catch. A hand-built template compiler or a values file that copies the Kubernetes API has no checklist to compare against, so a reviewer has to understand it before they can judge it.
The migration in an initContainer
The chart ran migrate up in an initContainer on the application Deployment. This is one of the most common anti-patterns, and the guidance against it is beginner material. The Kubernetes Recipes guide to init container patterns, labelled beginner, lists migrations as one pattern and then warns that in a Deployment with N replicas the migration runs N times concurrently on every rollout and scale-up. Its advice is to do it only if the migration tool takes a lock, and otherwise to run the migration once as a Job. None of that needed experience. It needed the model to apply what the first guide anyone reads already says.
In practice, three replicas mean three pods running the migration at once on the first rollout, and again on every restart, at three in the morning or any other time. Whether that is harmless depends entirely on the locking behaviour of the migration tool, which nobody had checked, because nothing in the manifest suggested there was anything to check.
The rule we apply on every project:
Schema migration runs as a PreSync hook Job. Schema first, then code.
Data migration runs as a PostSync hook Job. Code first, then data.
Forward migrations are add-only. Column drops and renames ship in a separate, later release, once nothing running still references them.
Migrations run on every sync. Idempotency makes a rerun a no-op, which is far cheaper than trying to detect a first deploy.
Helm's helm.sh/hook annotations map onto Argo CD's PreSync, Sync and PostSync phases, so a chart written this way behaves the same under both.
initContainer on the Deployment | Hook Job | |
|---|---|---|
Runs | Once per pod, on every start | Once per sync |
Concurrency | One per replica, racing | A single Job |
Ordering | Before this pod, not before the release | PreSync before the code, PostSync after it |
Failure signal | CrashLoopBackOff and a stalled rollout, cause unnamed | The sync stops at a named phase |
Rollback | Schema has already moved and the pod that moved it is gone | Add-only migrations mean the previous image still runs |
Where the migration runs decides what a rollback lands on.
The last row is the one worth sitting with. Reverting a release reverts manifests. It does not revert a database. Add-only forward migrations are what make the revert survivable, and that is a decision about release sequencing, not a property of any YAML file.
One Dockerfile, four services
A single Dockerfile had been copied across four repositories and edited lightly each time, because the request each time was for something like the one next door. Every image built and every container started, so nothing objected.
The base image was pinned to a floating tag rather than a digest, so the image built in March and the image built in September are not the same image, and nothing in the repository records which one shipped. A Node service carried the apt packages a Python service needed, pushing images close to 900 MB and dragging the CVE count up with them. And because there was no separation between build and runtime stages, the compiler toolchain shipped to production. Docker's own image-building guidance covers all three, and none of the three fails a build.
A health path nobody serves
The readiness probe pointed at /healthz for every service in the repository. Two of the services serve /health. The rollout did what a rollout does when readiness never passes: it stopped at the first pod and waited. The Deployment sat at 0/3 available, the sync stayed in progress, and the cause was four characters.
This one also cuts against a common instinct, which is to teach the delivery layer more about each service. We go the other way. Readiness belongs in the application: the service checks its own dependencies and reports when it can actually serve, and the platform only asks the question. A health check written in the delivery layer is guessing at readiness from the outside, and a model writing that check is guessing from further away still.
Why it approves work like this
Three reasons, and the first one is the one that generalises.
It is producing the shape of a review. Asked whether a chart is good, the model generates what an answer to that question looks like. Approvals in its training data are fluent, specific and reassuring, so the output is fluent, specific and reassuring. Whether the chart deserves it is not a variable in that process. This is why the four file-visible problems were missed: nothing in generating a plausible approval requires checking anything, even when the thing to check is on screen.
It never ran anything. No apply, no render against your values, no diff against the live release, no request to the probe path. Every statement it made about behaviour was a prediction about text, and none of it was a report on an execution.
Nothing in the loop is independent. When the author grades itself, any blind spot at generation is present again at review. Bringing in a second model removes that effect and leaves the larger one untouched, which is what happened here: two models that did not write the chart still passed it, because both were doing the same thing with the same material.
The 2025 DORA report describes the organisational shape of the same thing. AI adoption now shows a positive relationship with delivery throughput and product performance, and it continues to show a negative relationship with delivery stability. The report's framing is that AI is an amplifier: acceleration exposes whatever is weak downstream, and without strong automated testing, mature version control and fast feedback, more change volume simply produces more instability. 90% of respondents report using AI at work; 30% report little or no trust in the code it produces. Both numbers are describing the same situation.
Where we do use it, and where we do not
We are not making a purity argument. The list of things a model does well in platform work is long and we use all of it:
Boilerplate against a template we already own, where the structure exists and the diff is small enough to read line by line.
Translation: a docker-compose file into manifests, a values file into a different schema, a long shell script into something maintainable.
Explaining an unfamiliar controller error or a CRD field faster than searching the docs would.
Test fixtures and synthetic seed data, which is most of what makes a preview environment worth opening.
A first pass over a large diff, to point at the parts that deserve a careful read.
And the list of things that do not reach a cluster without somebody who knows the system:
Anything whose correctness depends on state outside the file: probe paths, replica counts, resource requests, storage classes, which operator already owns the job.
Migration ordering, and anything that touches a schema.
RBAC and network policy, where a rule that is far too permissive is also a rule that works.
Secret handling, where the plausible answer, a literal in a values file, is the dangerous one.
The pattern across both lists is not that the model is strong on text and weak on systems. The chart above was weak on text too. The pattern is verifiability: the model is useful where you can check the output cheaply and immediately, by rendering it, running it or reading a small diff, and it is unusable where the only check available is its own opinion.
Write the rules down, then let it run
A model does not know how your platform should look. It generates what it is asked for, and a request for a Helm chart is not a request for your standards. The people who know those standards are your engineers, and in that repository they lived as things an experienced person would have objected to, which is the one form a generator cannot read and a review prompt cannot enforce. So the change that would have prevented most of that repository starts with a human: somebody who knows how it should be decides the rules and writes them down where a machine has to satisfy them.
We encode them. Conftest runs Rego policies against structured configuration, which means rendered manifests, Dockerfiles and pipeline config all get checked by the same mechanism. A policy is a gate rather than an opinion: it passes or it does not, and it cannot be talked out of its answer by a confidently phrased follow-up. It also carries what the person knew. The model never had to understand why migrations belong in hook Jobs. It needed a check that fails when they are not.
Two of the three failures above are a few lines each.
Wired into CI, that runs against what will actually be applied rather than against the chart source:
The rules have to be written properly, which is the part people skip, and it cannot be handed to the model, because writing them takes exactly the knowledge the model lacks. A policy that only checks for the presence of a field teaches the generator to add an empty field. Write them against the behaviour you care about, keep them in the repository next to the charts, and make them part of the same review as the code. A model given a repository with enforced policies produces different output from a model given an empty directory, but not because it learned what is right. The failing check comes back as feedback it can act on, and the standard behind it is still a person’s. Without them, every generated file is its own precedent.
What makes the speed safe
Four things, none of them exotic, and all of them cheaper than the incident.
A golden path that exists first. When the repository already has an opinionated chart, a service template and a pipeline, generated output has something concrete to conform to, and a deviation is visible as a diff rather than as a judgement call.
Gates that are not opinions. Schema validation with a tool like kubeconform in CI, helm template plus a diff against the live release, the policy checks above, digest-pinned base images, image scanning with a size budget.
A reviewer who can open the cluster. Not a second prompt, and not the same person asking a different model in a new thread. Somebody who can check the probe path against the running service and knows what the other forty services in the repository already do.
The rollback question, asked out loud before every release: if we revert this in twenty minutes, what does it land on? That single question catches most of the migration and schema class of failure, and it is the one question a model genuinely cannot answer for you.
Failure | The gate that would have caught it |
|---|---|
Migration in an initContainer | A Conftest policy on rendered manifests: migrations are hook Jobs, so a Deployment carrying a migrate initContainer fails the check |
Copied Dockerfile, floating base tag | Digest pinning enforced by policy in CI, with image size and CVE count as build budgets |
Probe path that nothing serves | A smoke test that requests the probe path against the built image before the chart reaches any cluster |
Mechanical checks, all of them cheaper to build than the incident they prevent.
The speed is real, and it is conditional
The client was not careless, and the agent was not broken in a way a better prompt would fix. There was a working cluster, valid manifests and a tool that produced more than anyone was reading. What was missing was anything in the repository able to tell a plausible chart from a correct one, and asking a model is not that, because asking a model returns an answer whether or not one exists.
That is the distinction worth taking away. Use a model where the result is checkable in seconds: it renders, it compiles, the diff is three lines, the test passes. Do not use it for the question it will always answer, which is whether the thing is any good, because the answer looks identical whether it is right or wrong and so does the retraction.
So the honest version of the advice is not use it or do not use it. It is: move every judgement you care about out of the conversation and into something mechanical, then let the model work inside those bounds as fast as it likes. The gates do not negotiate, and that is the entire reason to trust them over a second opinion that was never a first one.
Shipping AI-generated infrastructure faster than you can review it?
We build the golden paths, the CI gates and the review discipline that make generated manifests safe to ship, and we clean up the setups where that order got reversed. If any of the three failures above looked familiar, that is a good and cheap conversation to have.
If the only thing standing between generated manifests and your cluster is somebody’s opinion, it does not matter whose
Judgement you cannot verify is the part we replace. We write the rendered-manifest policies, wire them into CI against what will actually be applied, and build the service templates that give generated output something concrete to conform to. Where that work has not been done yet, we start by auditing what is already running.
Start with a focused platform assessment
Our first engagement is a paid technical assessment. We map your delivery workflow, infrastructure gaps, platform debt, and implementation priorities before anything is built.
SHARE ON SOCIAL MEDIA




