At 10:22 AM on June 23, I marked the Roster Customize Columns UI live on develop, then loaded a JavaScript bundle missing the opt-in changes that had just been merged.

The ticket was done. The branch was merged. The UI had reached the environment where we expected to use it. The source said one thing, but the code the browser received said another.

That is a release bug with an uncomfortable property: every status signal can be green while the application is still serving yesterday’s behavior.

The page was not running the code I had shipped

The symptom was specific. The loaded bundle did not contain develop’s opt-in changes. This was not a vague concern that a cache might be stale. It was a direct mismatch between the merged source and the compiled JavaScript being served.

The merge itself had made the feature look complete. The artifact made it false.

Our application still has a compiled-bundle path in the release chain. Source changes are not enough. The JavaScript must be rebuilt, and the application reference has to point at the dated compiled output. That means a ticket can be correct in the repository and still be wrong in the browser if those two outputs drift apart.

The immediate fix was to recompile the bundle correctly. That restored the missing opt-in behavior. But recompiling once only repairs one release. It does not answer the more important question: what prevents the same mismatch from reaching develop again?

A merge is not artifact verification

The tempting approach was to trust the merge. A merge confirms that source control accepted a set of changes. It does not prove that a browser will load the artifact built from those changes.

A second tempting approach was to rely on a manual visual check after every release. That can catch the problem, as it did here, but it catches it after the close process has already declared success. It also requires someone to remember the exact behavior that should appear in the page. That is a weak control for a mechanical failure.

The signal we needed was already available: file modification time.

I added a guard to the item-close process. Before it lets a release ship, it checks whether the compiled artifact is older than the source it represents. If the bundle predates its source, close stops. There is no warning to read later and no assumption that a clean merge implies a current build. The release cannot call itself finished while its executable output is stale.

The guard deliberately checks freshness, not feature semantics. It cannot prove that every branch is correct. It can prove one narrow and valuable invariant: the compiled JavaScript was created after the source that feeds it.

That scope matters. A generic release check that tries to infer whether a UI “looks right” would be brittle and expensive. A timestamp comparison is simple, deterministic, and aimed directly at the failure that actually happened.

The conflict signal was there

The incident also changed how I read conflicts around compiled assets. A conflict on the compiled-bundle ASP reference is not harmless release noise. It is a prompt to verify source and bundle parity before closing the item.

That became part of the mechanical sequence: compile the dated JavaScript bundle, flip the -s.asp reference, then commit. The build step belongs at the end because it produces the release artifact from the final source state. Doing it earlier gives subsequent edits a chance to make the bundle stale again before the commit is made.

This is not a general argument that modification times solve deployment. They do not. It is a guard for a system where source files and checked-in compiled output both participate in the release. In that system, source control can honestly report a successful merge while the delivered JavaScript is obsolete.

The important boundary is not between coding and deployment. It is between the code we changed and the code the browser ran.

I had crossed that boundary once without proving it. The close process now refuses to let me do it quietly again.