The morning of 13 June, the stale-PR sweep surfaced something. PR #36 had been open since 11 June, written against a pre-X.8 codebase. The description made it look routine. The full diff against current main was not routine: merge it as written and it would delete 4,000+ lines of work that had shipped since the PR was cut.
That PR wasn’t merged. The rate approach inside it was rebuilt from scratch. That’s how Tier 1a of the editorial gate acquired live Bank of England rate fetches. Not because live data was the obvious correct choice, but because the stale-fixture approach had become a revert bomb.
What the cycle does
UK Calculators runs financial content through a two-stage pipeline. A triage step classifies and scores incoming content via Haiku. A composer produces the article body. The cycle gates publication by tier. Tier 1a requires accurate rate data: base rate, mortgage rates, savings rates. Below that gate, nothing moves.
The test regime is cohort-based. A named cohort drives the full cycle against a controlled input set and captures every block reason at each tier boundary. S-4 was the cohort in this sprint, run on 12 June. The results land in a spec file (s4-result-2026-06-12.md) that names failure types by count. When a single failure type shows up 20+ times across 8 trials, the decision about what to fix isn’t a matter of taste. The data closes the argument.
Two things the S-4 results forced.
Decision one: live rate fetches
X.8.5, 13 June. PR #36 had been modelling a static fixture update. By the time the stale-PR sweep found it, the codebase had evolved past the point where merging it was safe. The full diff against main showed a 4,000+ line deletion. Fixtures that worked against a pre-X.8 base didn’t survive the delta.
The replacement was a live BoE rates fetcher. It pulls current rates at cycle time rather than relying on fixtures that may lag the base rate by days, closing the Tier 1a gate-hole. The trade-off is an external call on every cycle run. That’s acceptable. The alternative was a gate that passes stale data as if it were live, which is exactly what the verifier can’t catch without external-rate comparisons.
Out of scope: rate caching, API-outage fallback fixtures. Both are second-iteration concerns. The cycle needed to close end-to-end first.
Decision two: composer restraint
X.8.9, also 13 June, came from a different kind of evidence. Post-X.8.7, tier-2-unsupported was the dominant block reason in the S-4 cohort: more than 20 instances across 8 trials. The composer was generating language that implied tier-2 features existed in the product when they don’t.
The output read well. Coherent, fluent, confident. Wrong about product scope in one consistent direction: it extended capability into unsupported territory. The model wasn’t inventing from nothing; it was making plausible inferences from adjacent context. Those inferences landed outside what the calculator could do, and the verifier named the failure type precisely enough to diagnose it.
Calibration C, shipped in X.8.9, added interpretive-language restraint to the composer prompt. The instruction: when describing what the calculator does, restrict claims to what the triage input explicitly states. Don’t infer from adjacent features. Don’t extend from similar products. If it isn’t in the input, it isn’t in the output.
The cohort verifier for Calibration C confirmed the block count dropped. Not “this reads better”. tier-2-unsupported instances fell. That’s the data point that closed the argument.
Closing the cycle: X.10
The subscription path had been the cycle-completion blocker since B-050 named it. X.10 closed it on 13 June via a four-layer defensive approach, taking option (a): direct Anthropic API for triage Haiku scoring, a roughly 520-line implementation.
The alternatives weren’t rejected on principle. Option (a) was the route that didn’t introduce an abstraction layer over a call pattern the rest of the pipeline already used. With X.10 in, the cycle could complete end-to-end: triage scores, composer runs, verifier validates, subscription clears.
What happened after shipping
The uk.bytebridges.ukcalculators.editorial-cycle cron ran on 14 June. Then it didn’t run again. Exit code 126 on four consecutive weekdays, no alert. The first signal was a question on 18 June: “review the pipeline this morning.” Four days of silent stall, discoverable only by asking.
Exit code 126 is permission denied or command not found at the shell level. In a macOS launchd context that usually means TCC: Transparency, Consent, and Control, the privacy framework that guards file-system access at the kernel layer. The cron’s spawned shell had no inherited Full Disk Access. The fix was a TCC grant to the relevant binary, plus preflight self-tests in the cron wrapper that surface this class of failure before it can stall silently for four days.
The pattern is the same one that came up during the captainrandom.co.uk launchd work: TCC errors don’t identify themselves as TCC errors. The exit code is generic, the logs don’t name the framework, and the failure looks indistinguishable from a dozen other causes until you check FDA status directly. The runbook now lives alongside the plist. The preflight tests run before the main script body.
The design sprint ran alongside all of this
While the pipeline was being diagnosed and closed, 25 PRs shipped the Aether/Bebop Lounge dark editorial-fintech theme. The Paper DNA (FT × Stripe × Monzo, Japanese restraint, German engineering precision), assembled from a Claude Design handover bundle (8nWnBA39lgSmaBzUS17wDA). The design work and the pipeline debugging ran concurrently across the same sprint window.
The design output looks considered. The pipeline decisions, by contrast, were forced moves: a stale fixture that became a revert bomb, a cohort block count that named a composer failure mode, a cycle-completion blocker, a cron that stalled without announcement. None of it came from a plan drawn up at the start of the week.
The cohort verifier is what makes that acceptable. The data is precise: tier-2-unsupported, 20+ instances, 8 trials. The decision about what to change isn’t ambiguous. You change that thing, run the cohort again, and the block rate either falls or it doesn’t. Preference-driven decisions about composer tone or fixture freshness are harder to validate and slower to converge. The S-4 results weren’t a rubber stamp on conclusions already reached. They were the conclusions.



