---
title: "The Deploy Script That Had Been Erasing Its Own Config for a Decade"
canonical: https://dxdev.com/blog/2026-08-22_the-deploy-script-that-erased-its-own-memory/
datePublished: 2026-03-28
---
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 I 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, not accumulate old code forever.

The bug was that the destination was not only 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. That is why the fault lasted. A purge was succeeding exactly as instructed, deployment after deployment, while the configuration that distinguished one environment from another disappeared.

Roughly a decade of deploys ran this way. I do not know how many times, only that it was long enough for the script to outlive my own memory of writing it. The real exposure was never the deploys where a default happened to match reality closely enough. It was every deploy where connection settings or a site setting could have differed from the default and nobody would have found out until something downstream broke in a way that looked unrelated to a deploy that had, on paper, succeeded.

## The one-line 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 is as simple as this:

```text
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 omitted 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. Make the destination match it. But an environment is not a checkout. It contains a mixture of files with different lifecycles, and a blind mirror operation cannot infer those lifecycles from a directory listing.

The actual change excluded the three files from the purge, not from deployment entirely. That distinction matters. We did not add a second configuration system, copy secrets into the release artifact, or stop deleting stale code. We set the boundary where it belonged, inside the cleanup step.

## What the audit found

An AI agent surfaced this while reviewing a build-script update. I did not ask it to redesign the deployment pipeline. I asked it to examine an old script closely enough to identify the behavior its short commands implied.

The first clue was not an outage or a dramatic error. It was the combination of a purge operation and configuration files that were known to exist only on the destination. Once those two facts sat next to each other, the diagnostic path was mechanical:

1. Identify what the archive contains.
2. Identify what the destination contains that the archive intentionally does not.
3. Trace what the purge step does to that difference.
4. Check whether a fallback hides the result.

The answer was uncomfortable because it was so ordinary. The script was not failing. `robocopy` was deleting files it had been instructed to regard as stale. The surrounding application was filling the gap with defaults often enough that nobody had a reason to suspect the deploy step.

This is a useful category of old-code failure. We tend to 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. They make the filesystem look tidier. They produce an application state that is plausible, just wrong in a way the defaults can conceal.

## The alternatives we did not take

The most obvious alternative was to remove the purge behavior. That would have prevented the configuration deletion, but it would also have left stale release files in place. We would have exchanged a hidden config failure for a different class of deployment drift.

We also could have placed all environment configuration in the release archive. That would make the mirror operation internally consistent, but it would put environment-owned settings into a distributable artifact. That is the wrong ownership model, especially for connection configuration.

A third option was a post-deploy restore step. That would work only by accepting the destructive operation and repairing it afterwards. It creates another dependency, another ordering requirement, and another place for a release to succeed without restoring the correct files.

The exclusion list was narrower and more honest. The purge can delete application files it owns. It cannot delete the three files it does not own.

## Why I wanted an agent on this task

I wrote this script. I wrote much of this infrastructure long enough ago that familiar code had become invisible to me. I could read it myself, the author, and see a deploy, not the assumptions embedded in every file operation, which is exactly why it took something without my familiarity to catch what I couldn't. An agent does not have that familiarity. It can hold the archive, destination, exclusions, and fallback behavior in the same review pass, then ask the unglamorous question: what happens to the file that exists only here?

That is not a case for handing a production deploy to an agent without review. The safe use was narrower. 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`.

The value was not that the patch was clever. The value was that it forced a decade-old command to state its rules plainly. The fix itself is two additions and one deletion in `deploy/extract-archive.ps1`. That is the entire size of the boundary that took roughly ten years of deploys to need.
