Every journalist who’s filed a story at night and teed it up to go live at 6 a.m. the next day has used a scheduling feature they probably never thought about.
It might be for convenience, so you don't have to get up super early to press the button, or for coordination, when all the social posts are set to fire in sync. Or a public-relations team might set a time for a press release to go live.
This week I had to build this scheduling functionality into DistroVerse, our agentic press, providing an opportunity to see how such a feature actually works.
When you develop a news platform from scratch, you notice things you used to take for granted. DistroVerse users had the binary option of publishing stories right away or saving them as a draft. What DistroVerse couldn’t do was take a finished story and set it up to go out at an exact future moment.
In professional news publishing, having a scheduling system like this is table stakes. Without the feature, we wouldn't be taken seriously.
So asked Claude Code to map it out, and then build it.
Building a story scheduler
Part of the plan is the obvious stuff a reporter sees: a “Schedule” button, a calendar, a place to store the go-live time.
But the heart of it, the part that actually makes a held story go live, is a small background worker called the scheduler.
Here’s how that journey looks, at a high level:
flowchart LR
classDef human fill:#0ea5e9,stroke:#0369a1,color:#fff,stroke-width:2px
classDef hold fill:#f59e0b,stroke:#b45309,color:#fff,stroke-width:2px
classDef decide fill:#a855f7,stroke:#6b21a8,color:#fff,stroke-width:2px
classDef go fill:#22c55e,stroke:#15803d,color:#fff,stroke-width:2px
classDef liveNode fill:#16a34a,stroke:#14532d,color:#fff,stroke-width:2px
A([Editor picks<br/>a future time]):::human
B([Story waits,<br/>hidden from readers]):::hold
C{"Has the moment<br/>arrived?"}:::decide
D([Story flips<br/>to published]):::go
E([Live everywhere<br/>at once]):::liveNode
A --> B
B --> C
C -- Not yet --> B
C -- The moment arrives --> D
D --> EThe story sits in the system, completely hidden from readers, until its moment. Then it flips to published and shows up everywhere at once.
The obvious question is how the system knows the moment has arrived. When Claude Code laid out the options, it asked how precise we needed to be. I said we’re a professional news publishing platform, so we should go for the exact-second kind, the one embargoes demand. That choice is why the timing has to be sharp.
It works with two things together: a patient, periodic lookout and a sharpshooter.
flowchart TB
classDef check fill:#0ea5e9,stroke:#0369a1,color:#fff,stroke-width:2px
classDef alarm fill:#a855f7,stroke:#6b21a8,color:#fff,stroke-width:2px
classDef go fill:#16a34a,stroke:#14532d,color:#fff,stroke-width:2px
A["Once-a-minute check<br/>(the patient lookout):<br/>anything due<br/>in 90 seconds?"]:::check
B["Precise alarm set<br/>for the exact second<br/>(the sharpshooter)"]:::alarm
C["Alarm fires<br/>at 6:00:00"]:::alarm
D([Story published,<br/>on the dot]):::go
A -- Spots a story due soon --> B
B -- Waits for the moment --> C
C --> DA routine check runs once a minute and looks ahead. When it spots a story due soon, it sets a precise alarm for the exact second. The once-a-minute check is the patient lookout; the alarm is the sharpshooter. That pairing is what lets an embargo lift at 9:00:00 and not 9:01.
The nervous part of any timed system is what happens when the lights flicker. We update our software often, which restarts the program. If a restart landed at 5:59 and your story was due at 6:00, a careless system would just miss it.
flowchart TD
classDef restart fill:#ef4444,stroke:#991b1b,color:#fff,stroke-width:2px
classDef look fill:#0ea5e9,stroke:#0369a1,color:#fff,stroke-width:2px
classDef decide fill:#a855f7,stroke:#6b21a8,color:#fff,stroke-width:2px
classDef go fill:#22c55e,stroke:#15803d,color:#fff,stroke-width:2px
classDef calm fill:#94a3b8,stroke:#475569,color:#fff,stroke-width:2px
A([Software restarts<br/>after an update]):::restart
B["First thing it does:<br/>look for overdue stories"]:::look
C{"Any overdue<br/>stories?"}:::decide
D([Publish it<br/>right away]):::go
E([Carry on, setting<br/>alarms as usual]):::calm
A --> B
B --> C
C -- Yes --> D
C -- No --> ESo the first thing the program does when it wakes up is sweep for any story whose moment passed while it was down, and publish it right then. Late by a few seconds at worst, never missed.
I asked whether we’d invented something here. We hadn’t. This recipe – a periodic check, a precise alarm, and a catch-up sweep after every restart – is the standard approach any program reaches for when it needs to do something at a set time, whether that’s sending a reminder or charging a subscription. We didn’t invent the plumbing. We assembled known parts and made sure they fit a newsroom’s rules, where an embargo can’t leak early and an update can’t drop a story on the floor.
What’s novel, for me, is understanding it at all.
Over three decades as a journalist at CoinDesk, TheStreet, Bloomberg News, Chicago Tribune and the Gainesville Sun, I used the systems that were provided to me, and they all had a scheduling feature like this. I never once asked how the story knew when to publish.
Most journalists probably wouldn't care, just wanting it to work, so they can spend more time doing real journalism. And that is good, normal, appropriate, healthy, sane.
But I must admit it's kinda fun to understand the mechanics of these features we take for granted.
Now you can just tell an AI coding agent what you want to build. At one point in time, people had to figure it out.
(HOW AI WAS USED IN THIS STORY: I asked Claude Code to write the first draft of this article – including the diagrams – by relying on the transcript of my coding session inside Visual Studio (Claude CLI) and a custom skill I developed to replicate my own writing style. I then used Distro Publisher, our official MCP server, to file the story as a draft directly from Claude CLI to my newslode. I then edited the draft in DistroVerse’s editing interface and checked all the facts before publishing. I used ChatGPT to create a story illustration.)