Build Logukcalculators
#01
2026-07-275 min read

The sourcing step was always the bottleneck. Day 6 moved it into the cron.

For five days, every ukcalculators claim block was hand-sourced by the operator from individual gov.uk pages. On Day 6, the cron polled the gov.uk Search API live for the first time. The sourcing method changed. The claim-block discipline didn't.

Read post
Build LogUK Calculators
#02
2026-07-265 min read

The cron kept drafting mortgage because gov.uk wasn't in the source set yet

For five days, the cron produced news-cluster drafts and operators shipped gov.uk regulatory pillars by hand. Day 6 changed one thing: gov.uk Search API sources entered the polling loop. Two data-pegged pillars from a single cycle followed immediately.

Read post
Build LogPrivacy
#03
2026-07-265 min read

The privacy work isn't on the /privacy/ page

The /privacy/ page went live in June and covers what it covers. The actual privacy work on this site happened in a data-locality choice, a brand-separation rule, and a username leak in published MDX. Only one of the three announced itself.

Read post
Build LogUK Calculators
#04
2026-07-255 min read

10 of 13 sentences made it. The verifier caught the rest before they reached a pillar page.

The June 30th cron produced a mortgage rate-spike draft grounded in April 2026 data. Ten of thirteen sentences passed. The three that didn't never reached a pillar page — and the next two days showed why that distinction matters at publishing scale.

Read post
Build LogContent Pipeline
#05
2026-07-256 min read

14 days of daily publishing: the three failure modes I hadn't designed for

A 14-day publish cadence on UK Calculators surfaced three failure modes: title-mangling produces permanent ugly slugs, zero-draft mornings expose the absence of a fallback queue, and triage without memory republishes the same cluster it shipped the day before.

Read post
Build LogUK Calculators
#06
2026-07-255 min read

The E2E suite billed 1,130 minutes to test a page that never existed

A Playwright suite on a private repo ran 69 times in three weeks, failed 61 of them, and billed roughly a thousand minutes doing it. The root cause was three mistakes stacked on top of each other, and the deepest one was mine: I did not know GitHub Actions had a bill.

Read post
Build LogLearning
#07
2026-07-245 min read

The backlog called it a content gap. It was navigation.

Completing an eight-chapter course on the learning hub felt like progress. Then I looked at what had actually moved the needle — track sections, prerequisite chips, and a JSON-LD breadcrumb. The backlog had been mis-filing the bottleneck for months.

Read post
Build LogUK Calculators
#08
2026-07-235 min read

The cron produced zero drafts. That was the correct answer.

Four mornings into a 14-day operator-publish sprint on UK Calculators, the pipeline ranked 150 clusters, fetched candidates, and produced nothing. Not a failure. A signal that the ground-truth layer, not the LLM, is the binding constraint in a pipeline built on live gov.uk sources.

Read post
Build LogAI Tools
#09
2026-07-225 min read

Fifteen claim blocks and a zero-draft cron: the cost of grounding a content pipeline in gov.uk

Hand-curated gov.uk claim blocks stopped the ukcalculators drafter fabricating figures. Three days after the first clean pillar shipped, the cron fired on time, ranked 150 clusters, put mortgage at the top — and produced nothing. That was the gate working correctly.

Read post
SEOObservability
#10
2026-07-225 min read

The eight-pillar map closed in three days. ga-telemetry and gsc-telemetry are why.

Phase 1.6 shipped two observability skills on 20 June: ga-telemetry and gsc-telemetry. Six days later the eight-pillar topic-authority map was complete. The link is an operator loop built on next-morning indexation signal.

Read post
Build LogTypeScript
#11
2026-07-215 min read

`ignoreBuildErrors: true` kept the type checker quiet. Turning it off found twelve product bugs.

UK Calculators had `strict: true` in tsconfig and `ignoreBuildErrors: true` in next.config.mjs. The two settings coexisted for months. One afternoon removing the flag surfaced twelve masked type errors on a YMYL money site where wrong numbers carry real consequences.

Read post
SecurityDevOps
#12
2026-07-216 min read

Scope the MySQL user first: the newsletter DDL as a security boundary on cPanel

On shared hosting, you don't control the firewall or the infrastructure. What you do control is which privileges the MySQL user holds. Scoping it to the newsletter schema alone made a compromised endpoint's blast radius structurally finite — not as a mitigation layered on top, but as a property of the design.

Read post
Build LogPipeline
#13
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
#14
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
#15
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 LogPipeline
#16
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
Build LogContent Pipeline
#17
2026-07-185 min read

Five days, ten operator publishes, two cron drafts: where the ukcalculators pipeline actually breaks

Running a 14-day pillar schedule with a parallel operator-publish path made the bottleneck legible: the cron fires on schedule, triage completes, 150 clusters rank — then the ground-and-extract stage drops everything silently. The schedule is not the problem.

Read post
Build LogContent Pipeline
#18
2026-07-186 min read

Ten of thirteen: why verification without correction is just expensive triage

The morning cron produced a draft with 10 of 13 sentences passing the verifier. Without a corrective step, that's a triage ticket, not a publish. Four days of cron output showed the rescue pattern fires once in four — and what covers the other three.

Read post
Build LogContent Pipeline
#19
2026-07-175 min read

150 clusters ranked, zero drafted: ground attrition is the load-bearing constraint

Five days of autonomous publishing logs made the pattern clear. The cron fired on schedule every morning. Ground attrition killed the drafts before the drafter ran. One usable cron draft across five days; ten articles published via a different route entirely.

Read post
Build LogSecurity
#20
2026-07-176 min read

Six PHP classes, no Composer: a cPanel newsletter backend with enumeration-resistant auth

The captainrandom.co.uk newsletter backend runs on a cPanel shared host with zero framework and no Composer. Six small PHP classes, around 600 lines, and an eight-stage subscribe pipeline that tells the caller nothing about whether an email address already exists.

Read post
Build LogPHP
#21
2026-07-155 min read

I hand-wrote the newsletter backend in PHP. The threat model is the code.

The captainrandom newsletter subscription backend is 600 lines of PHP, six classes, no framework, no Composer. Every security decision — rate limits, honeypots, time-traps, token hygiene — lives in the endpoint files, readable end-to-end in one sitting. That's the trade: no ecosystem, but a threat model you can hold in your head.

Read post
AnalyticsGA4
#22
2026-07-135 min read

GA4 went dark for four days. Both fixes were right. One was in the wrong place.

Two PRs, each diagnosing a real fault, and GA4 still flat four days later. The problem wasn’t whether the consent signal was present — it was where in the document it lived relative to gtag.js.

Read post
Build LogContent Pipeline
#23
2026-07-126 min read

Four cron zeroes in five mornings. Eight articles shipped by hand. The pipeline problem isn't the scheduler.

Five days into a fourteen-day publishing sprint on UK tax calculators, the scheduler fires within a minute of its window every morning. Automated drafts produced: two, one a duplicate cluster. Gov.uk cooperates. The bottleneck is claim extraction and cluster survival.

Read post
Build LogAI
#24
2026-07-103 min read

The voice-check agents passed two chapters. A three-line recount proved they couldn’t count.

An ultracode workflow’s voice-check agents passed two course chapters as within the em-dash budget. A three-line Python recount found both over. A model can’t reliably count its own tells.

Read post
DocsProcess
#25
2026-07-066 min read

The process changed. The docs didn’t. The fix was a folder.

Three times in the captainrandom repo, documentation drifted from actual practice. Each time the fix was the same: a dedicated folder with its own PRD, roadmap entry, and devlog trail — not a patch, but enclosure.

Read post
Build LogDevOps
#26
2026-07-045 min read

Four lines in a published article leaked my macOS username to the live site

PR #17 was a four-line diff across two files. One of those lines was in the body of a published article, rendering a real macOS username in the browser. The fix was substitution — but the pattern it revealed was wider: three surfaces in the same audit window, the same failure.

Read post
Build LogPrivacy
#27
2026-07-025 min read

I patched the same privacy leak four times. The lesson isn't to be more careful.

Private repo names in a language stats page. A macOS username in a published plist example. A missing privacy policy when the newsletter went live. Four separate fixes, each reactive, each after deployment. Four is enough to name the pattern: the publish boundary has no scrubber.

Read post
Build LogContent Pipeline
#28
2026-07-015 min read

145 words shipped on inheritance tax. 949 words fixed it. The gate measured the wrong thing.

The operator-publish gate cleared 145 words on UK Inheritance Tax because word count was above minimum and the verifier passed. IHT is YMYL content. A same-day ultracode rescue to 949 words proved the gate needs a depth signal, not just a length floor.

Read post
DVLAWBuild Log
#29
2026-07-016 min read

The substitute pool stole from future slots because slots didn't exist as rows

Phase B's autonomous publishing pipeline sat at twenty percent success. The substitute pool was recursing through future-scheduled rows to breach — and nothing in the data model could stop it because slots had no identity. Making the slot a first-class entity with explicit state transitions turned six log events into six queryable rows.

Read post
Build LogDVLAW
#30
2026-06-306 min read

Correct answers, wrong questions: four DVLAW scope errors in 72 hours

Between 2026-06-10 and 2026-06-13, four DVLAW bugs caused cascading slot failures, auto-published style violations, and a near-duplicate article. None threw an exception. All of them were answering the wrong question correctly.

Read post
Build LogPrivacy
#31
2026-06-306 min read

The identifiers I didn't know I'd published

Five fixes across /learning, /now, a published article, the privacy policy page, and a global pre-commit hook — all tracing back to the same root cause. Not tracking, not data harvesting. Just private names in places I'd stopped looking.

Read post
Build LogPrivacy
#32
2026-06-296 min read

The privacy policy was correct. The article body wasn't.

A production audit found the macOS username in a published plist example and private project names on two public pages. Neither would have been caught by a policy document. The fix was a pre-commit hook and an aggregation change — pipeline enforcement that runs whether the author remembers or not.

Read post
Build LogDVLAW
#33
2026-06-285 min read

Five Phase B fixes in 48 hours. They were all patching the same missing type.

Twenty percent autonomous success across five scheduled slots. The same breach shape surfacing twice from different audit trails. Five fixes shipped in 48 hours — and when the sixth investigation opened, the pattern became unmistakable: the slot had never been an entity.

Read post
Build LogEditorial Pipeline
#34
2026-06-265 min read

The sabotage tests passed. The live cycle still found the bug.

Phase X.8 shipped with two hermetic sabotage cycles green. The first live run produced no article — not because the pipeline crashed, but because calculator_scores in ground.py was wrong in a way the fixtures had never exercised. Hermetic tests prove the logic path. Live harnesses prove the integration.

Read post
Build LogDVLAW
#35
2026-06-255 min read

The publisher cancelled the 12:00 slot. My recovery logic called the publisher again.

Queue id=37 was scheduled for 12:00 BST on 13 June. schedule_sweep fired publisher at 11:00:08Z, publisher cancelled the row, and Fix B (the SLA recovery layer) called publisher again. Recovery logic without a model of who holds the lock cannot distinguish a concluded failure from a recoverable one.

Read post
Build LogDevOps
#36
2026-06-245 min read

Auto-mode opened the PR. It didn't merge it. That was gap one.

Phase 9's 'self-driving overnight' was overstated by two wiring gaps. A 09:00 BST live run on 11 June found both. The afternoon of fixes taught more about auto-mode's real edges than any pre-flight design session.

Read post
Build LogDVLAW
#37
2026-06-235 min read

The publishing queue ran at 20% success rate. Fixing it took three changes that only work together.

Four autonomous publishing slots expired before the pattern became clear. Sibling cancellation, slot fallback, and a missed-slot alarm are a set — without all three, the queue degrades silently.

Read post
Build LogDVLAW
#38
2026-06-225 min read

20% in 72 hours: rewriting the slot scheduler's SLA recovery

Twenty percent autonomous success across five scheduled slots in 72 hours. The substitute pool was running but consuming the wrong inventory — draining future windows to cover present failures. Three mechanisms and a first-class slot state machine fixed it.

Read post
Build LogDevOps
#39
2026-06-225 min read

I treated publication slots as timer events. Three days of SLA breaches say otherwise.

Between 11 and 13 June, the 09:00 BST publication slot fired and produced nothing three times in a row. Each incident had a different surface cause. All of them trace back to the same modelling mistake.

Read post
Build LogDVLAW
#40
2026-06-215 min read

Four expired slots and the SLA recovery rule we learned in sequence

Between 12 and 14 June, four scheduled DVLAW slots expired without autonomous recovery. Each failure confirmed the same thing: sibling cancel, substitute fallback, and a missed-slot alarm don't work in isolation. They're one system.

Read post
Build LogAI Tools
#41
2026-06-205 min read

The S-4 cohort made two editorial pipeline decisions. I shipped them.

Two weeks of UK Calculators editorial pipeline sprints ended with two decisions that came from the cohort data, not preference: live Bank of England rate fetches and a constrained LLM composer. The pipeline silently stalled for four days after shipping.

Read post
Build LogDVLAW
#42
2026-06-196 min read

Five slots, twenty percent: the substitute pool had no editorial policy

Five scheduled publishing slots, four expirations, and a breach chain that exhausted six candidates in three seconds. The scheduler fired on time every run. The substitute pool was offering articles the drafter’s citation rules were going to refuse.

Read post
Build LogDevOps
#43
2026-06-145 min read

Two correct cancellations, two SLA breaches, four manual ships

On 13 June, the 09:00 and 12:00 BST editorial slots both failed in the same day. G-L3 cancelled the queued articles correctly each time. Manual recovery covered every slot. The experience made the gap between row-level correctness and slot-level reliability impossible to ignore.

Read post
Build LogDevOps
#44
2026-06-135 min read

Four sprints shipped before the installer resolved to the right directory

The idea park soak cron installer used a directory convention copied from the article pipeline, correct in the article pipeline's layout and wrong in the idea park-soak's co-located layout. Two more post-merge fixes followed the same shape: a sanitiser wired into one code path but not the other, and a caller convention documented but never enforced.

Read post
Build LogNewsletter
#45
2026-06-135 min read

Three newsletter hotfixes. The events table found all of them.

The newsletter form went live on 9 June. I subscribed from my own address and got nothing. No row, no email, no error. Three consecutive bugs followed, all found the same way: reading the events log, not the code.

Read post
Build LogDevOps
#46
2026-06-125 min read

No Composer, no autoloader: the newsletter backend cPanel rewarded

The deployment target was a cPanel shared host with FTP access. Composer wants SSH or a committed vendor directory. Neither fits cleanly on shared hosting. The alternative was require_once, six PHP classes, and a 190-line schema that doubles as documentation. Six sprints in, the backend is completely understood.

Read post
Build LogDevOps
#47
2026-06-125 min read

Scheduling articles through Telegram taught me that cron is the wrong model

The article pipeline pipeline could draft and voice-fix articles but had no scheduling layer. Adding one took six sprints, not because the cron was hard but because scheduling through Telegram needs to capture intent, not timestamps. The grammar parser, the 2×2 keyboard, and the 48-hour grace policy are the same design decision expressed three different ways.

Read post
Build LogBBBrain
#48
2026-06-105 min read

The inbox filter model drops strangers silently. I built a queue instead.

Inbox filters drop what they don't recognise. I wanted a record instead: every unknown sender parked, every blocked arrival logged, nothing silently gone. Five tables, four sync branches, and a historyId cursor later, the editorial pipeline's Inbox Hub does exactly that.

Read post
Build LogAgentic AI
#49
2026-06-096 min read

Archon was burning quota. The fix was a hook, not a scheduler.

Three Archon runs in flight simultaneously, two of them fired without a deliberate decision. The instinct was to build a smarter scheduler. What worked was a global PreToolUse hook that intercepts archon workflow run and forces a pause before anything spawns.

Read post
Build LogInbox
#50
2026-06-085 min read

Making Gmail tractable in a day. Four branches and one partial index.

The Inbox Hub shipped in five sprints, all on the same day. The classifier is four comparisons against a rules table. The speed comes from a partial index on pending senders, not from the sync logic. Neither changed how I read email.

Read post
Build LogDevOps
#51
2026-06-066 min read

I built six sprints of article scheduling. For four of them, nothing fired.

Four sprints of Telegram button wiring, queue state machines, and free-text time parsing — all recording intent into `article_queue` without dispatching a single article. Sprint 5 was the first one that actually fired. Separating mechanism-to-record from mechanism-to-execute isn't a new idea; applying it deliberately at the sprint level is.

Read post
Build LogDVLAW
#52
2026-06-055 min read

The schedule queue took four sprints to remember. One to act.

After four sprints of B-schedule, a scheduled article existed as a database row with a correct timestamp. No article had published. Sprint 5 added the sweep daemon and publisher_lock. That is when scheduling became real.

Read post
Build LogDVLAW
#53
2026-06-045 min read

A missed slot at 09:00 should not silently vanish: building past-due grace into the article pipeline publisher

The `schedule_sweep` runs on a laptop. Laptops sleep. Without a grace policy, an article scheduled for 09:00 silently strands when the machine wakes at 09:07. Sprint 6 of B-schedule — past-due grace plus 48-hour hard-expire — turns that silent drop into a recoverable state with a reason, an audit trail, and a path back to publish.

Read post
Build LogPython
#54
2026-05-318 min read

The classifier is the product: how four branches replaced an inbox

Inbox Hub is five SQLite tables, twelve route handlers, four Shadow DOM components, and a launchd cron. Strip those away and what remains is a four-branch decision tree that runs on every message. Everything else is plumbing around that core.

Read post
Build LogPython
#55
2026-05-306 min read

monkeypatch lies to you when your tests run in subprocesses

The fixture looked correct. The patch was applied. The subprocess ignored all of it and wrote to the real registry. Three days of roadmap work taught me that monkeypatch only rebinds the parent process — env-var overrides are the only isolation that survives a fork.

Read post
Build LogBBBrain
#56
2026-05-307 min read

One input to rule the dashboard: how the editorial pipeline's chat surface replaced three UIs

The editorial pipeline's chat interface started as a conversational wrapper. Two days of commits later it had absorbed the Archon SPA, the dashboard nav, and a 16-command palette. The unifying mechanism is simpler than it looks.

Read post
Build LogAI Tools
#57
2026-05-287 min read

Ten AI-prose tells, one shell script, no more guessing

The em-dash density in my own published articles was running at four times the normal rate for edited prose. That was a measurable problem. So I made it measurable: codified ten AI-prose tells into STYLE-GUIDE.md and a pre-publish shell audit that catches them mechanically.

Read post
SecurityBuild Log
#58
2026-05-287 min read

The backtick that could run anything: hardening AppleScript shell escaping

A Sprint 2.11 review flagged a shell command-substitution gap in a single Python helper. Backticks and dollar signs were passing through unescaped into a double-quoted `do script` string, meaning a crafted issue title could execute arbitrary code. The fix was four lines. Understanding why it mattered took longer.

Read post
Build LogDevOps
#59
2026-05-286 min read

Two assumptions the pipeline held until it shipped

B8b's first end-to-end Telegram ship-tap exposed two bugs that had been invisible in every prior test. Neither was logic. Both were assumptions the pipeline had been making silently since the day the code was written.

Read post
Build LogDevOps
#60
2026-05-286 min read

Telegram 409 Conflict: two bots, one token, infinite errors — why a bot registry is infrastructure

Retrofitting the editorial pipeline's Telegram modules revealed that the tunnel bot and the brain server had been polling the same token for weeks, throwing 409 conflicts that neither log named clearly. The fix wasn't a config tweak. It was accepting that one-bot-per-purpose needs to be enforced at registration time, not discovered at incident time.

Read post
Build LogAgentic AI
#61
2026-05-286 min read

Why the editorial pipeline's rewrite layer runs on a project-owned harness, not Archon

The Opus rewrite layer inside the editorial pipeline's de-AI-ish stack runs through a project-owned osascript Terminal harness, not a general orchestration framework. That choice was deliberate and narrow. Here's what it cost and what it bought.

Read post
Build LogDevOps
#62
2026-05-215 min read

macOS won't let your cron read your work: three days inside TCC

A daily launchd job that reads from /Volumes/b/ looked trivial. Three days, four wrong approaches, and one AppleScript .app bundle later, I'd learned why granting Full Disk Access to a shell script is a UI fiction.

Read post
Web DevDevOps
#63
2025-04-181 min read

Static Next.js on cPanel in 2025

GitHub Actions to FTP to cPanel. Simpler than it sounds, with a few real gotchas worth documenting.

Read post