Six Posts a Day, and the Scheduler That Learned to Say When
A social pipeline that published one Reel a day and nothing else, because per-format quotas were ceilings and nothing was asking for the other formats.
My short-form pipeline publishes six pieces a day to one account, in four different formats. Getting from "it renders video" to "it fills a day" took longer than building the generator, and almost none of that work was creative.
Four formats, not one
The account posts a verse card to the feed, a story that is the twin of that card, two standalone stories, a carousel, and one Reel. Six items, four formats, each with its own generator path and its own idea of what "good" means.
The twin is the fiddly one. It is the same verse and the same art as the morning feed card, but the platform's API will not reuse the feed media for a story — so it has to be published as separate media that happens to look identical. The artwork is reused rather than regenerated, so the twin costs no GPU time. That distinction between regenerating and reusing is the whole reason it is cheap.
Quotas were ceilings, and nothing was a floor
For weeks the account published exactly one Reel a day and nothing else. Every quota was correct. The scheduler was working.
The per-format quotas were only ever ceilings — they stopped formats from stealing each other's slots. But the only worker that produced anything was the article worker, and it only makes Reels. Nothing in the system was responsible for asking for a card or a carousel. A limit is not a request, and I had built five limits and one request.
The timer's frequency is not the cadence
Three things that look like the same number and are not:
- The timer decides how often the scheduler wakes up (three times a day).
- The plan decides how many of each format the day should contain.
- The window decides what time of day the item is actually posted.
The scheduler produces at most one item per format per run. So the timer silently bounds the plan: asking for four stories a day when the scheduler runs three times means the fourth never happens, and nothing errors. Raising a number in one place required raising a number in a second place, and the failure mode of forgetting was an under-delivered day that looked completely healthy in the logs.
Windows, and why a fixed hour plus jitter still looks like a robot
Each format declares a window, and the post time is a uniform-random draw inside it — not a fixed hour with noise added. A fixed hour plus jitter still clusters on the same minute, which reads exactly like what it is.
Formats can declare several windows, comma-separated, and the Nth item of the day draws from the Nth window. This exists because three stories cannot share one window: the second run of the day would find most of a shared window already in the past, roll the story to tomorrow, and cost a post while every ledger and dashboard looked fine.
The draw also has to come from the part of the window that is still beyond the minimum scheduling lead, not from the whole window with a reject-if-too-early test afterwards. The rejecting version threw the entire day away on an unlucky draw — the 11:45 run opening a 12:00 window failed roughly one day in eight. Same intent, same window, completely different reliability.
The windows are priors, not measurements
Morning for devotional content, evening for the Reel and carousel because sessions are longer and Reels rank on watch time. Both of those are reasonable-sounding guesses I made up. They are labelled in the runbook as guesses, and the plan is to replace them with reach bucketed by hour once there is enough collected data to bucket.
Writing "this is a prior, not a measurement" next to a number is the cheapest honesty mechanism I know. It costs one sentence and it stops a guess from calcifying into a fact six months later.
What I actually got wrong
The runbook for this pipeline contradicted itself from the day I wrote it. One table said two stories a day; a section a hundred and fifty lines earlier argued that one story a day was a deliberate choice; the live config said one. It also described the pipeline as publishing Reels unattended, when the flag that would have enabled that had never been set — so the Reel slot had been empty every single day it claimed to be full.
Documentation drifts from the system. That is normal and expected. This was worse: the document was never true, and it was internally inconsistent in a way that anyone reading it top to bottom would have caught. I did not read it top to bottom, because I wrote it.
Written by
Adrian Romo
Senior Backend Engineer building scalable Python APIs, AWS Lambda architectures, voice systems, and enterprise integrations.
Related
Keep reading
Assembled Primitives vs. a Platform
I built a voice agent out of cloud primitives, and it worked. It has since been replaced by a purpose-built voice platform, and I think that was the right call.
Seconds of Work, Hours of Residency
My morning briefing started failing. Ollama was up and returned HTTP 500, because a 21-second image render was still holding 6.6 GB of VRAM hours later.
What Deserves Attention Today
My homelab produces one verdict each morning: something needs you, or nothing does. Getting the second half honest was much harder than the first.
Keep going
Where to next?
Browse more technical writing, see the engineering case studies, or reach out directly.