Three posts went out without my custom share image.
Not broken posts: the writing was there, the page loaded, the feed picked them up; the face, the preview, the part that makes a page feel alive looked abandoned, a gray rectangle with a URL sitting in it.
I found out the way you always find out. I went to share one.
So I added a check. My publishing agent already waits for a post to go live before it does anything else; now it also asks whether the share image that post claims to have is actually there. Small. Obvious in hindsight, the way most of these are.
Then the real question showed up, and it wasn’t a technical one.
Should that check stop the publish?
What I almost built
My initial instinct said yes. I’d just been burned by this exact gap, three times, in public. A check that notices a problem and then shrugs isn’t a check; it’s a comment with extra steps. If it matters enough to look, it matters enough to stop.
Wrong.
I almost built it that way. What stopped me was noticing what the check is actually looking at.
The image isn’t rendered by the publishing agent. It’s rendered by a separate CI job that starts when the push lands, which means it finishes after the publish, by design, every time. So the agent isn’t asking whether the image exists. It’s asking whether a different process has caught up yet.
Those are not the same question. Only one of them is the agent’s to answer.
Whose failure is it
That turned into the rule I’ve been using since: gate a defect, warn about a queue.
A missing <article> block is the post’s defect. A missing publish date is the post’s defect. Those stop the chain cold, and they should; the post is genuinely not ready, and that would mean shipping something incomplete. A share image that hasn’t rendered yet is nobody’s defect. It’s a queue.
The distinction isn’t about how much you care. I care about the share card; that’s why I wrote the check after getting burned. It’s about who’s at fault when the check fires, because that determines what happens next and who has to answer.
What a gate costs when it’s wrong
Picture the gate.
It’s eleven at night. The post is finished, the writing is good, the build queue is slow because someone else’s workflow is hogging a runner. The gate fires. The publish fails. Nothing is wrong with the post; a machine somewhere is just busy.
What do you actually do in that moment?
You don’t fix the build queue at eleven at night. You loosen the gate. Just a little; just this once, and that’s the last night anyone trusts it.
The false alarm isn’t the price of a gate that punishes the innocent. It’s the erosion. A gate that fails good work teaches you to route around it, and once you’ve learned to route around it, it has stopped protecting anything at all. It’s just a thing you turn off, sitting in the codebase looking like safety.
A check that misses a problem costs you a post; a check that teaches you to skip it costs you the check.
Loud, and out of the way
So the image check warns. It polls on its own clock after the page is live, and if the image still isn’t there it puts the failure in the result where I can’t miss it: the URL it tried, the status it got, and the plain sentence that the render job may still be running or may have failed outright.
The post publishes. The retrieval corpus ingests it. Both of those are correct without the image, so blocking them would punish the post for something the post didn’t do.
I’d rather this be a decision than a habit, so there’s a test that pins it. One test asserts the chain still succeeds when the image is missing and still carries the warning; another asserts that when the render job finishes in time, the poll catches it and there’s no warning at all. That second one matters more than it looks. A warning that never goes quiet stops being a warning.
The part that transfers
Every system that automates something consequential ends up with a pile of checks, and the pile always grows faster than the thinking behind it. The temptation is to make each new one a gate, since a gate feels like rigor and a warning feels like a shrug.
But rigor isn’t how many things you can stop. It’s whether you can say, out loud, why this one stops and that one doesn’t.
If you can’t answer that for a check you wrote, you haven’t built a safeguard. You’ve built something that’s going to get disabled at eleven at night by someone with a deadline, and neither of you will remember why it was there.
Gate what you’re judging — warn about what you’re waiting on.