The blog that couldn't publish on a schedule
I have hundreds of blog drafts triaged and scheduled months ahead, and until this week my pipeline could ship about one of them per sitting. This issue was supposed to fix the throughput problem with a batch skill. It mostly did, but the interesting part is what I found before writing a single line of the skill: my blog was structurally incapable of publishing on a schedule, and I didn’t know it.
The bug that wasn’t in the plan
The batch design seemed straightforward: write posts ahead of time with their scheduled publication dates, commit them, and let them go live on their dates. Before planning around that, I checked how the site actually treats a future-dated post. The answer: it doesn’t. The blog routes are prerendered, and the page-generation code filters out any post whose pubDate hasn’t arrived. A future-dated post isn’t hidden - it’s never built. It 404s.
That alone would be fine if something rebuilt the site daily. Nothing did. Deploys run on git push and only on git push. So a post committed today with next Tuesday’s date stays a 404 past next Tuesday, and the Tuesday after, until some unrelated push happens to rebuild the site. The whole write-ahead model I was about to build on was a 404 generator. No amount of skill-writing fixes that; it needed infrastructure: a daily cron that pokes both sites’ build hooks so staged posts flip live on their dates. Twenty lines of workflow YAML, and it’s the load-bearing piece of the entire feature.
The lesson I keep re-learning: verify the substrate before designing on top of it. One five-minute check of the routing code inverted the entire design conversation, and it happened before the plan was approved instead of after the skill shipped.
Small print I’m glad I read
Two details from the rebuild trigger worth recording. Cloudflare’s deploy hooks are dashboard-created only - no API - and the URL itself is the credential, which makes them the minimum-privilege option compared to parking a broader API token in repo secrets. And date-only frontmatter dates parse as midnight UTC, so a noon-UTC rebuild means a post dated Tuesday goes live 6am Tuesday my time. Timezone reasoning on a two-line cron expression, but it decides whether posts appear the morning of their date or the evening before.
The batch pipeline itself
The skill fans out one prep agent per post: each drafts from the issue’s source material, runs the same pre-flight checks and fact-check the single-post pipeline uses, picks hero image candidates, and returns a readiness card. Then one consolidated review: every card in a single view, and one reply ships the batch. The design constraint I cared most about was not weakening any gate - every check that used to run per-post still runs per-post; what changed is that my decisions got batched instead of drip-fed across separate sessions.
The dry run validated the shape and caught two things. First, the review page renders in a sandbox that blocks remote images, so the hero thumbnails showed as captions - the skill now embeds them as data URIs. Second, and more satisfying: one of the two test posts came back flagged with a major overlap. The prep agent noticed the story it was drafting had already been published, months ago, under a different issue. That’s exactly the judgment call the review gate exists to surface, and it fired on the very first batch. A pipeline that only demonstrates happy paths on its first run hasn’t really been tested.
What’s next
First real power session against the actual queue. The imperfectsystems.com side still has one loose end: PRs there need a manual merge until auto-merge lands (tracked separately), so the first batches will be vdaluz.com-only.
Related reading
Debugging a hidden checkbox on Medium's real UI
A zero-sized invisible input behind a styled toggle, a disabled state that silently eats clicks, and verification against the live page instead of my own logs.
Medium kept dropping my hero image, so the bridge now pastes it in
Two theories disproved at one manual import each, a paste that saved dimensions but no src, and the native file-chooser path that finally attached the image.
Stopping the heroless-post gap from coming back (a guard, not a nag)
A one-time cleanup fixed 80 posts and promised nothing. The second fix is twelve lines that fail the build, with zero exceptions.