Flow
Runtime guard decision order
Each failed condition has its own reason code so health output explains itself.
1Marker is a regular file
A symbolic link or missing marker fails immediately; in production a missing marker never falls back to a cache.
2Contract parses
Unknown contract versions, bad hashes or malformed sizes are rejected as invalid.
3Paths stay inside
Absolute paths, backslashes and parent segments are refused; resolved paths must sit under the release root.
4Executable is right
A regular file with the recorded size and an execute bit, inside a real browser directory.
5Library matches
The installed automation library version must equal the manifest value before the browser is declared ready.
One worker that drives a real browser couldn't tell if it had a browser until a job needed it.
The worker logs into the stores' register in a headless Chromium and posts loyalty points. Its code went through my whole release pipeline. The browser didn't. A deploy script installed it into a cache in the service account's home folder, and the worker just checked that a big enough file sat there. If not, the job failed with a clear error.
What was wrong with that
The error showed up late. Health only checked the database, so a release could go live before any job noticed the browser was gone. A big file isn't proof it's the right version or that it starts. And anything that touched that home folder could change the browser without a deploy.
Putting the browser in the release
I replaced the loose install commands with one installer script that CI and the release plans both use. When the release is built, the browser gets copied into a folder inside it. The installer starts that copy once, then writes a small manifest at the top of the release: which browser, where it is, its size, its hash and the library version it goes with. The manifest is written last, so if it's there, the browser started.
Every pipeline step after that, from staging to promotion, checks the manifest and re-hashes the browser.
Checking at runtime
The app checks the release too. The manifest has to be a real file, its paths have to stay inside the release, and the browser has to have the right size and be runnable. The library version has to match. Each failure gets its own reason. In production a missing manifest is a failure, never a cue to fall back to the cache.
Then health uses the answer.
In production, health now needs both the database and the sealed browser to be fine, and answers unavailable otherwise.
A release without a working browser now fails its smoke check and never goes live. The worker also starts the browser from the manifest's path, not the library's default.
My check looked in the wrong folder
My first check used the folder the app was running from as the release root. The web server actually starts two folders deeper, so it would have called every good release broken. Now it recognizes that one exact layout and steps up to the real root. It doesn't hunt through parent folders, because then a stray file outside the release could pass.
Two more fixes that day
In CI, the temp folder sat deep inside the workspace. Chromium puts a socket file in there, and socket paths have a length limit, so a deep enough path makes the browser crash on start. Each run now gets a short temp folder that gets deleted afterward.
The test that starts a real browser was running beside lots of parallel tests. It now runs alone, first.
The runtime check still skips the hash, so a same-size swap after release would get past it.