Commit commit-4E74 changed one line on March 28, 2026, and that line stopped a legacy deployment script from deleting its own memory.
The script had survived long enough that we no longer remembered the assumptions baked into it. It extracts a release archive, copies the application into place, and uses robocopy purge behavior to remove files no longer present in the release. That last part is normally the point. A deploy should remove stale application files so old code doesn’t pile up forever.
The bug was that the destination held more than application code. It also held environment-specific configuration that never belonged in a release archive. Each deploy treated the archive as the whole truth, saw those local files were absent, and removed them.
The fallback defaults happened to keep the app working, and that is why the fault lasted. The purge succeeded exactly as instructed, deployment after deployment, while the configuration that distinguished one environment from another disappeared.
Deploys ran this way for a long time. The risk was never the deploys where a default happened to match reality. It was every deploy where connection settings or a site setting could have differed from the default. Nobody would have found out until something downstream broke in a way that looked unrelated to a deploy that had, on paper, succeeded.
The boundary the script did not have
The fix was small: exclude the three environment files from the robocopy purge.
The names in the live script were substituted for public release, but the shape matters. One was a local environment file. Two were legacy server-side include files, one holding connection settings and one holding the public site setting. The corrected policy fits in two lines:
Purge release files.Preserve environment-local configuration files.Those are different ownership classes. The archive owns the release payload. The environment owns its local configuration. The deploy script had encoded the first rule and left out the second.
That omission is easy to miss because /MIR and related purge modes feel safe when you are thinking about code. The source tree is authoritative, the archive is authoritative, so make the destination match it. But an environment is not a checkout. It holds a mixture of files with different lifecycles, and a blind mirror operation can’t infer those lifecycles from a directory listing.
The change excluded the three files from the purge, not from deployment entirely, and that distinction matters. We didn’t add a second configuration system, copy secrets into the release artifact, or stop deleting stale code. We put the boundary inside the cleanup step, where it belonged.
How the audit found it
An AI agent surfaced this while reviewing a build-script update. We hadn’t asked it to redesign the deployment pipeline. We asked it to read an old script closely enough to state the behavior its short commands implied.
The first clue was not an outage or an error. It was a purge operation sitting next to configuration files that were known to exist only on the destination. Once those two facts were side by side, the diagnostic path was mechanical:
- Identify what the archive contains.
- Identify what the destination contains that the archive intentionally does not.
- Trace what the purge step does to that difference.
- Check whether a fallback hides the result.
The answer was uncomfortable because it was so ordinary. The script wasn’t failing. robocopy was deleting files it had been told to treat as stale, and the application was filling the gap with defaults often enough that nobody had a reason to suspect the deploy step.
We usually look for commands that crash, return a nonzero status, or leave a visible partial state. The more durable bugs often do the reverse. They complete cleanly and make the filesystem look tidier. They leave an application state that is plausible and wrong in a way the defaults can conceal.
The other ways to fix it
The most obvious alternative was removing the purge behavior. That would have stopped the configuration deletion, but it would also have left stale release files in place, trading a hidden config failure for a different kind of deployment drift.
Putting all environment configuration in the release archive would make the mirror operation internally consistent. It would also put environment-owned settings, including connection configuration, into a distributable artifact, which is the wrong ownership model.
A post-deploy restore step would accept the destructive operation and repair it afterwards. That adds another dependency, another ordering requirement, and another place for a release to succeed without restoring the right files.
The exclusion list was narrower. The purge can delete application files it owns, and it can’t touch the three files it doesn’t.
Why an agent was the right reader
We wrote this script, and we wrote much of this infrastructure long enough ago that familiar code had become invisible to us. Reading it ourselves, we saw a deploy rather than the assumptions embedded in every file operation. That is why it took a reader without our familiarity to catch what we couldn’t. The agent could hold the archive, destination, exclusions, and fallback behavior in one review pass, then ask the unglamorous question: what happens to the file that exists only here?
That doesn’t mean handing a production deploy to an agent without review. The safe use was narrow. The agent audited a bounded script, identified an ownership mismatch, and produced a change we could inspect as a three-line diff: two additions and one deletion in deploy/extract-archive.ps1. That is the entire size of the boundary the script needed, and the work forced an old command to state its rules plainly.
What to ask about your own deploys
Ask whoever runs your deployments what the deploy does to files that exist only on the destination, such as connection settings and site settings. Check that any purge or mirror step excludes environment-local configuration. A working application is no proof the configuration survived, because fallback defaults can hide the deletion.