The commit that shipped to nowhere
A while back I shipped a post that nobody could read. The pipeline that writes this blog did its whole job: it drafted the piece, passed every gate, moved the file into place, committed it, and reported a clean run. Green the whole way down. The only problem was that the commit landed on a branch my host does not build, so the page it created never went anywhere. It sat in git, perfectly valid and perfectly invisible, until I went looking for the post and found a hole where it should have been.
Nothing had failed. That is the whole trap. A broken build shouts at you. A test that breaks turns something red and blocks the merge, and you go fix it because you are stopped. A commit on the wrong branch does none of that. Git does not care which branch you are standing on. It accepts the commit, advances the branch, and hands you back the same calm exit code it gives you on a good day. The deploy, watching only the one branch it was told to build, simply never hears about the work. Success and silence look identical from where you sit.
This is the kind of failure vibe coding is built to hide from you. The whole appeal of vibe coding is that you move fast on green checks and trust the machinery underneath to watch your back. A vibe coder reads a clean run as a shipped result, and almost always that read is correct, which is exactly why the one time it is wrong slides past unnoticed.
A clean commit that nobody could see
Here is what actually happened. The job that publishes this blog ran while the working tree was parked on a feature branch, left checked out from some unrelated work the night before. The job was not written to care about that. It drafted the day's post, ran its checks, moved the file, and committed. Every step returned success because every step did succeed, in isolation. The commit was real. The file was well formed. The gates genuinely passed.
But the host deploys from one branch, main, and only main. A commit to a feature branch is, as far as the live site is concerned, a commit to nowhere. The build that would have turned that markdown into a page never triggered, because the branch it triggers on never moved. There was no error to read, no log line to grep, no alert to wire up. The post existed and the post was invisible, and those two facts sat there quietly together until a person happened to notice the absence.
Recovery was a one-line cherry-pick onto main, and the moment the right branch moved, the page appeared within minutes. Cheap to fix, once seen. The expensive part was the seeing.
The precondition nobody checks
Every commit carries a precondition you almost never think about: am I on the branch that actually ships? You do not think about it because, ninety-nine times out of a hundred, you are. The branch is right by habit and the deploy just works, so the question never surfaces. Then one day the habit breaks, an unrelated branch is checked out, the scheduled job fires anyway, and the precondition you were never tracking quietly goes false.
The move that fixed this was not a smarter commit message, and it was not a post-deploy sweep that hunts for missing pages after the fact. It was to take that invisible precondition and make it something the pipeline observes out loud, before it does any work. The first step of the job now reads the current branch and refuses to proceed unless it is the deploy branch. If it finds itself somewhere else, it stashes whatever is in the way, switches to main, pulls, does the work, and puts the original branch back when it is done. The precondition went from an assumption nobody could see to a guard nobody can skip.
I think about this as the difference between observability you read and observability you enforce. A dashboard that would have shown the wrong branch is still a thing you have to go look at, and the entire failure here is that nobody was looking, because nothing told them to. Turning the precondition into an invariant the system checks on its own is the version that survives a tired operator and a 5am scheduled run. It is the same instinct behind why a vibe-coded pipeline needs observability before you automate it: the state you cannot see is the state that fails you, so you make the system carry the watching instead of carrying it in your head.
Guard every stage, not just the one that bled
There was a second decision in the fix that mattered more than the check itself. The pipeline is not one job. It is five: research, draft, an SEO pass, a revision pass, and the final publish. The stage that failed that morning was the publish step, because that is the one that commits. The easy fix would have been to bolt the branch check onto the publish step and call it done.
That would have been a guard with an expiry date. Any of those five stages can touch git. The day an earlier stage starts committing, or the day I add a sixth that does, a publish-only guard is right back to trusting a precondition it stopped checking. So the branch check did not go on the stage that bled. It went on all five, as the identical first step, copied across each one. Same template, every leg.
That is the part I would lift into any system, not just this one. When you find a failure mode in one stage of a multi-stage build, the fix belongs everywhere that shares the exposure, not only where it surfaced this time. A guard that lives on the single stage that failed is really a bet that the same stage will be the next to fail, and that bet has no reason to pay out. Codifying the check as one template stamped across every stage costs a few extra minutes and buys a guarantee that does not erode as the pipeline grows. We made a related version of this argument about the enforcement layer that almost got skipped entirely, and the spec-first habit underneath both is the same: write the invariant down once, then make every stage that could break it carry the check.
if you ship from a branch
If you are a vibe coder orchestrating AI to build and ship real software, and the thing that puts it live is a deploy that watches one branch, this failure is sitting in your setup right now, whether or not it has fired yet. It will not announce itself. The commit will be clean, the run will be green, and the page, or the function, or the migration, will simply not be there, and you will learn about it from the absence rather than from any alarm.
The fix is boring and it holds. Make the deploy target a precondition the system checks before it works, not an assumption it inherits from whatever branch you left checked out. Spec the invariant once, enforce it at every stage that can commit, and the wrong-branch ship stops being a thing that can happen quietly.
If you are formalizing an AI orchestration that ships to production on its own, and you want a sparring partner on where it can fail silently, work with VibeKoded. The guard that catches a failure nobody would have seen is exactly the kind of thing worth speccing on purpose, before it bites instead of after.