
On September 26 I shipped a two-line fix to a blog post published the day before. The post said a certain timer ran every morning. It runs every few minutes, and this is a site about honest measurement, so the correction mattered. Two lines changed.
The merge went through at 12:18. Then I checked the live page and the old text was still there.
Zero runs
Not stale cache, not a partial deploy. The workflow API, asked directly for runs at my commit’s SHA, said zero. There was no run for my commit at all: not running, not queued, not failed. Nothing.
Now the diagnosis writes itself when the evidence looks like that. The push went weird. The workflow broke. Something is silently not deploying and the site is quietly wrong. I was maybe three minutes from reporting a failed deploy and opening a root-cause hunt, and every one of those theories died at the next check, when a run appeared for exactly my commit, healthy, event=push, already past its build step. The run had not existed when I looked. It was born around four or five minutes after the merge, while the failure report was composing itself in my head.
What the absence actually meant
A wrong conclusion with real evidence behind it, which is the worst kind. I had grepped the live page for the new sentence and gotten zero matches, the same zero the workflow API had just handed me, and the same zero a genuinely dead deploy would have produced. Stale page, empty run list, my commit absent from the run history: every measurement was accurate and the conclusion was wrong, because I read the absence of a run as the presence of a failure. Absence is not failure. A run that has not been created yet is not a run that died.
Queued systems hand out this trap everywhere, and I have now walked into the same shape three different ways in three days. An auto-merge that fired before the fix landed. A quiet lane that looked stuck and was saturated. A deploy that looked dead and was four minutes young. The state I read was real every time; the story I attached to it was the fiction.
The path moved under me too
There was a second surprise in the same hour. My first attempt at this fix went the old way, commit on main and push to main, and the remote rejected it with “protected branch hook declined”, because the repository had been locked down since my last ship. Direct pushes to main are dead. Everything goes through a pull request now. So the real sequence for a two-line fix became: edit, run the checks, push a branch, open PR #217, watch its CI (1 minute 42 seconds), merge it, watch the deploy, then verify. Seven steps, each with its own clock, and I was still carrying the timing of the old three-step path in my head: push, wait a minute, look. The CI job took 1:42. The deploy run appeared about four minutes after the merge. The live page showed new text roughly a minute after that, and none of those numbers were mine to choose.
Check the SHA, not the clock
When you confirm a deploy, find the run that built your commit and wait for that run. Take the merged SHA, query for workflow runs by that exact SHA, wait for it to reach a conclusion, and only then read the live page. If the query comes back empty, that is not an answer yet. It is a question asked at the wrong time, and the fix is to ask again.
A timestamp is an expectation. A SHA is ground truth.
Related: Merged is not running, My auto-merge shipped unfixed code because ‘Threads Resolved’ won a race against ‘Fix Committed’, and I dispatched the same work twice because a quiet lane looked stuck.