Build LogPipeline
#106
2026-07-205 min read

Ten cycles, one ready draft: the pipeline was consuming its failures

Between 30 June and 13 July, ten cron cycles produced one ready draft and zero auto-publishes while the skipped counter climbed from 2 to 7. The drafter was not stalling. It was treating failed attempts as permanent completions, and only heartbeat visibility on ready-draft count made the dead zone visible.

Read post
Build LogContent Pipeline
#107
2026-07-205 min read

The cron exited clean. There were no drafts.

Two zero-draft mornings out of four cron firings across 29 June to 2 July, both exits clean. The failure was Haiku's claim extraction underfiring one stage before draft: structurally invisible, no error, no warning, cron returns success.

Read post
Build LogContent Pipeline
#108
2026-07-206 min read

The cron fired at 08:22 and drafted nothing — scheduling is the easy part

On 2 July, the pipeline ran on time, ranked 150 clusters, fetched candidates, and produced zero drafts. The scheduler worked. The grounding step didn't. Automating gov.uk pillar articles forces a distinction that took a zero-drafts morning to make explicit: cron reliability and content freshness are different problems, and only one of them is solved.

Read post
Build LogLearning Hub
#109
2026-07-195 min read

Six navigation phases, one client island: why the keyboard audit took half an hour

The learning hub's navigation plan ran six phases. Five shipped as pure RSC; the server knew the URL, and that was enough. The in-chapter TOC with IntersectionObserver scroll-spy was the only genuine client requirement, and keeping the client surface at one component made the accessible-by-default story apply to everything else.

Read post
Build LogPipeline
#110
2026-07-195 min read

Three weekdays, zero publishes: draft.py was consuming its own failures

Between 7 and 9 July the UK Calculators cron fired on time, triaged candidates, and assembled source packs. It published nothing. Across ten cycles, skipped climbed from 2 to 7 before the cause was found: draft.py was writing failed-* markers to the same index it used for successful consumption, permanently locking out any topic that had ever failed a draft attempt.

Read post