Engineering

The problem is not where you test. It is that you reset.

A cloud VM is not the problem, and neither is a container. A long lived one would have caught most of what I hit last week. The line runs between environments that persist and environments that start empty every run.

14 September 2026 · André · Part 2 of 3

Part 1 the bug · Part 2 why I test on a real Mac · Part 3 the machine that was told not to believe me

The claim I had to walk back

The first draft of this post said a fresh cloud VM would have missed every bug I found last week. That was wrong, and it was wrong in the direction that flatters me, which is the direction to watch for.

The starvation bug from Part 1 needed a running system, a board with a few tickets on it, and twenty five minutes of patience. All three are available on a machine created ninety seconds ago. If someone had thought to construct that scenario in CI, CI would have caught it.

So I went back and sorted every fault from that week by what it actually required. The sorting is this post, and it is more useful than the claim I started with.

Sorted by what each one needed

Needed months. A fresh environment can never see these.

My agent configuration file was written once in June and has not changed since. The code has since learned about a sixth runtime and gained a flag that the file does not carry, and because the seeding logic only runs when the file is absent, it can never be rewritten. The code says one thing. My machine does another. It has been that way for three months.

Nothing that starts empty can hold this fault, because every fresh start writes a correct file. The bug is not in the code or in the file. It is in the gap between the version that wrote the file and the version now reading it, and that gap only exists on a machine that was here for both.

This is the class nobody tests, and it is where most real complaints come from. Almost every user problem is an upgrade problem rather than an install problem, and almost every test suite tests an install.

Needed a real machine, not an old one.

Two faults needed reality rather than age.

The first was my liveness check. It asks whether an agent process is running by reading each candidate process, and when it could not read one it gave up and answered "alive" for everything. On a machine owned entirely by me that never happens. On a machine with other users' processes on it, a single root owned process named the same as one of my agents pins every answer to "alive", permanently, until that process exits. A container running as root with six processes cannot produce that.

The second was a flag I passed to a terminal multiplexer that the installed version does not accept. Not ignored. Rejected outright, so every session died in the argument parser. My machine had a newer build than the test machine, and the difference was invisible until something real refused it.

Needed only a system that was running and being watched.

The two bugs from Part 1 are here, and this is the honest correction. They needed no history at all. They needed a system doing real work while somebody watched it for long enough, and that is rarer than history.

Nobody writes that scenario into a test suite, because you would have to imagine the bug first to construct it: one ticket already past a failure threshold, a clean one queued behind it, and the patience to watch eight rounds do nothing at all. I found it by watching a live system misbehave, not by querying anything old.

The value was not that the machine remembered. It was that the machine was running, and something was watching it long enough to notice a prediction fail.

The part that is specific to agents

Here is where I think this stops being ordinary advice about test environments.

In a system that runs agents, the agent writes the state its next run reads. The ledger of what was dispatched. The records of what was verified. The sessions still attached. The settings file it updated. That is not environmental noise sitting around the product. It is the product's own memory of what it did.

Look back at last week with that in mind. A counter read 134 records left by an earlier failure and treated them as strikes. A second status row for the same agent replaced the first, and the interface read the dead one. A new version parsed a configuration file an old one had written. Every one of those is the system misreading something an earlier version of itself had written.

The uncomfortable conclusion, and the one to take away, is that an environment that resets erases exactly the memory the system is built around. You can prove the code correct in a clean room and learn very little about whether the product works.

What I actually changed

A second machine that is never reset

A Mac mini on the desk, running the app the way a user would, kept alive between test runs on purpose. It has months of state on it now, and that state is the point rather than a problem to be cleaned up. When I am tempted to wipe it, that is usually a sign it is about to be useful.

The tests keep starting fresh

This is not an argument against clean environments and I have not removed any. Determinism is what makes a failure debuggable, and a bug that only reproduces on one machine with three months of history is a miserable bug to chase. They answer different questions. A fresh run answers "does this code work". A machine that has lived answers "does this work where people are".

Persist and reset are different axes from cloud and local

Worth naming because I had them tangled. A long lived cloud VM persists and would catch most of this. A container that is recreated per run does not, whether it is on my desk or in a data centre. When I pick where an agent runs, that is the axis that matters, not the postcode.