◉ANTIGRAVITY LABJP
Articles/App Development
▣ App Development/2026-07-14Advanced

When the Deploy Was Green but Users Still Saw the Old Build: Field Notes on a Gate That Verifies Your Shipped Commit Reached the Edge

When a deploy reports success but production keeps serving the old build, a post-deploy gate that verifies your shipped commit actually reached the edge via a build stamp closes the gap. Field notes with real operational numbers.

Antigravity382DeploymentCI/CD17ObservabilityOperations12

✦ Premium Article

Support kept coming in about a bug I was sure I'd fixed. CI was green. The deploy screen said "success." The commit was on main. Yet opening production, the behavior I'd fixed was still there, unchanged.

The cause was almost anticlimactic. The build had run, but the edge cache kept serving the previous bundle. That green checkmark meant only "the pipeline ran to the end" — not "users are seeing the new code."

Those two are alike in appearance and completely different as facts. This note — using an app I run as an indie developer — records how to build a post-deploy gate that verifies your shipped commit actually reached the edge, via a stamp baked into the build, along with the numbers from running it. I landed on this shape only after shipping two quiet incidents to this exact mix-up.

Why you can't trust "deploy succeeded"

A green deploy guarantees only one of several independent stages: that CI finished without an exception. Between there and the new code reaching a user's screen, several traps remain.

The edge or CDN cache keeps serving the old bundle. The atomic swap lags, so some regions stay on the previous version. Or you shipped not to the intended production project but to a preview environment or a different branch. From CI's vantage, all of these are "success"; from the user's, nothing changed.

The scary part in production is that this kind of mismatch never surfaces as an error. No 500, nothing in the logs — just old behavior served quietly. That's exactly why, instead of believing it succeeded, you need a step that goes and checks whether it actually arrived.

Bake a verifiable stamp into the build

First, make it mechanically readable from production which commit's build is currently live. Embed the commit SHA into the artifact at build time and serve it from a fixed location like /version.json.

// What this does:
// At build time, write out the commit SHA and build time as version.json
// so it can be served as a production static asset.
// scripts/stamp-version.mjs
import { execSync } from "node:child_process";
import { writeFileSync, mkdirSync } from "node:fs";
 
const sha = execSync("git rev-parse --short HEAD").toString().trim();
const builtAt = new Date().toISOString();
 
mkdirSync("public", { recursive: true });
writeFileSync(
  "public/version.json",
  JSON.stringify({ sha, builtAt }, null, 2)
);
 
console.log(`stamped version.json: ${sha} @ ${builtAt}`);

Run this just before your build script, wired as "build": "node scripts/stamp-version.mjs && next build". Now hitting https://your-app.example/version.json tells you at a glance which commit the edge is currently serving. The stamp need not be a SHA — a monotonically increasing build number works too. What matters is being able to reconcile the value the shipping side expects against the value production returns.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Why a green deploy doesn't mean production reflects your change, and a post-deploy gate that verifies the commit at the edge
✦Code that stamps the commit into the build and fetches the live marker while bypassing caches
✦Measured reduction in stale-build incidents reaching users after adding the gate
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

▣ App Dev2026-10-15
I Swapped an Immutable Asset at the Same URL, and Only the Browser Kept the Old Copy
Overwrite a long-cached JS file at the same URL and neither a CDN purge nor a refresh will make the browser fetch it again. Four delivery styles — same URL, no-cache, query string, content-hash filename — compared in Chromium, plus a complete build-step script that adds the hash.
▣ App Dev2026-09-29
Purging Cloudflare Didn't Bring Back the New Wallpapers: the Stale JPEGs Were Held by XPageSpeed on the Origin
An operations record from a wallpaper app: images overwritten at the same URL kept arriving stale even after a Cloudflare purge. Covers the three-way local/origin/edge probe that names the guilty layer, when prefix purge beats per-URL purge, and how to switch off Xserver's XPageSpeed in .htaccess without a 500.
▣ App Dev2026-09-19
I Asked for the Whole Quote PDF Tool at Once, Then Split It Into Four Requests
How I moved my client quote paperwork from copy-and-edit to a small PDF tool built with Antigravity, why the first single-shot request had to be thrown away, and the four smaller requests that replaced it, with working code.