One of 126 posts had an image, and I put it there by hand.
That was the state of the blog yesterday. The heroImage field existed in the content schema. No template ever read it. Every post’s social-preview card pointed to /og-image.jpg, a file not in the repository, which meant link previews had been broken site-wide for however long the blog had been running.
I knew I wanted images. My actual words: “The goal with this is to adjust from being just a wall of text in my posts to having a relevant image. I do not want to have an image just for the sake of having an image in my blog post.” And: “I don’t want to have to use an API key.”
The second constraint fell away first. Workflow illustrations are diagrams, and a diagram can be written as an SVG file by the same model that writes the post. No API key. No image model. The routine runs in an ephemeral clone at 5 AM with only Python standard library. Nothing to install.
The harder problem was the first one.
Three reviewers in parallel, all 126 posts, instructions to be strict. Fifteen candidates. One hundred eleven “no image.” About one post in eight. Most of the rejections were a single story with nowhere for a picture to land. The three I picked from the 15 each needed a different kind of drawing. One of them was the watchdog post, whose punchline depended on seeing which machine watched which. The answer to “would a picture explain something the reader has to hold in their head” is almost always no, and writing the routine to ask that question was the actual work.
The first versions of those three came back as plain boxes and bars. My reaction, also my words: “I think these images look okay, but is there any way to make them a little bit more interesting looking?” The second versions used the site’s own blueprint look, the same dark grid and heading font the rest of the pages use. That took two rounds.
The kit catches text overflow, a class of problem neither I nor the routine can catch by looking. One label “ALERT THROUGH NTFY” overflowed its box. Found that by looking, which the routine can’t do. The kit now refuses to write a figure if text overflows a box, overlaps other text, leaves the frame, or falls under 11 pixels at phone width.
A false connection nearly slipped through. A post from March calls injecting a context block into every conversation “the next step.” The step had not been built. The first diagram drew it connected by a solid arrow, as if it had. It got redrawn dashed, because anything the post describes as not real draws dashed.
The gate has an escape hatch. If the checker rejects only the image but the post itself passes, the routine deletes the image and ships the post. A post without an image beats no post.
This post is the routine’s first unattended attempt at drawing one. The diagram ships with the draft. The first time I’ll see it is when I open this file for review.