Nine Sharp Needs No Model

For ninety-nine mornings this site was supposed to publish one piece a day. On thirty of them it published nothing, and not once was the reason that there was nothing to say.

What ran at a quarter to nine

The job was one timer and one agent. At 8:45, plus up to ten minutes of deliberate jitter, the timer asked the fleet for an agent. The agent read a day of commits across every repository, picked a story, drafted it, ran the site’s guard suite, built the site, committed and pushed. One session, on the morning the piece was due.

So the morning’s piece inherited everything that session depended on. The fleet refuses to dispatch while it is paused, and I pause it for days at a time; this job read the fleet’s flag and skipped the day. The model runs on a subscription with a weekly cap, and when the cap hits, the reply is a sentence about when it resets, which is a fine thing to tell a person and a useless thing to tell a timer. Some mornings the agent arrived to find the site’s tree dirty from a run that had been cut off, and stopped, as it should. And some mornings it ran, read the day, and decided the material was not worth a piece. That verdict was right every time I checked it. The day stayed blank anyway.

Fifteen of the thirty I filled in later, in one sitting, each dated to the day whose material it used. The other fifteen were still blank when I rebuilt the job: six in late September, five around the turn of the month, four in mid-October while the fleet was parked. Every one of them a day whose piece could have been written on any other day.

The cure is a buffer

The cure for a capped week was to stop needing the model on the day.

The job is now two halves that never meet. The publisher runs at 09:00:00, to the second, on purpose: the unit’s accuracy is set to one second, the random delay is gone, and a fire the box sleeps through runs the moment it wakes. It is plain code, node and git and the site’s own build. It takes the next finished piece from a queue on disk, stamps the day’s date on it, runs every gate the site has (372 tests at the last count, the redaction scanner, the em-dash rule, a full build) and pushes through the one script that is allowed to push this site. Then it reports to the status feed: ok with the number of days banked, warn under three, error on a day with no piece. Nothing in that path can be capped, because nothing in it asks a model anything.

The writer is the other half. Once the publish is done, and at fixed times through the day, an agent is asked to fill the queue back up to seven pieces. A refusal, whether no capacity, a capped subscription or a paused fleet, is a standing state the next fire retries, not a failure. The agent drafts in a scratch directory and enters the queue through one command that runs the publisher’s exact checks first: slug and title against the site and the queue, the frontmatter, the scanner, the word bounds. A piece that fails never enters. A piece that enters is finished.

Seven is not a number I liked the look of. The cap is weekly. A queue deeper than the longest outage it has to cover turns availability into latency: a capped week costs the queue seven pieces and costs the site nothing.

The pause question answered itself once the halves were apart. The writer stands down for both pause levers, because it dispatches agents and that is what the levers are for. The publisher ignores the fleet’s pause on purpose: a paused fleet is a capacity decision, not a reason to skip a day. It honours only the global lever, the one that means stop everything.

What a queue cannot promise

A buffer moves the problem; it does not remove it. If the writer is refused eight days running, the eighth morning is blank again, and the status feed will have said warn on the three mornings before. The queue also changes what the pieces can be. A piece that ships on a morning it does not know cannot say “yesterday”. It cannot lean on anything that might stop being true inside a week. Every piece in the queue, this one included, was written against a day it could not name.

And the fifteen blank mornings were not the only gaps. The site went daily in July; the year started in January. Every day of this year is getting a piece, written from what the repositories were doing that day. The ones written after their date say so on the page, in one muted line under the title, because a repository that lets a piece pass for one written on its date is lying about the one thing a date is for.

The night the queue went live, the status line read forward depth 1 of 7 (writer owes 6). This is one of the six.


← all writing