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

Both cycles ran clean. Every stage completed. No crash, no broken state, no closed-book drafting. And no PR opened.

This was day one of live operation — the first time orchestrate.py ran with H-1, H-2, and H-3 cleared. Those three human-gate holds had been the conditions Phase X.8 couldn’t launch without. The S-1 doctrine had passed: test_s1_pr21.py and test_s1_integrated.py green on every gate-touching commit. The adversarial sabotage cycles had passed. The cron was loaded.

The pipeline ran twice and produced nothing.

The bug was in ground.py. The function was calculator_scores.

What the hermetic tests proved

S-3.1 and S-3.2 were the adversarial cycles that preceded live launch. S-3.1 tests the defer-ungrounded path: push an article through without grounding data and verify the pipeline defers rather than drafting blind. S-3.2 tests the stale-rate block: supply data beyond the configured staleness window and verify the pipeline refuses. Both are hermetic unittest cases. Both passed.

They covered the failure modes that had been modelled in advance: absent grounding data and stale grounding data. What they didn’t cover was the case where the grounding stage ran, returned data, and the data was wrong.

calculator_scores sits in that gap.

S-3.3, the clean control cycle, was deferred until after live observation. That was explicit. Build the fixture from what you observe, not from what you expect. A fixture written before the first live run reflects your assumptions, not reality. It passes because you put the right values in.

That sequencing was right. The live cycle found something the hermetic tests structurally couldn’t.

What the live cycle found

Hermetic tests run against fixtures. Fixtures are controlled. The values inside them are whatever the test author put there, which means they reflect what the test author expected the pipeline to receive. When the real chain runs against real data, it can encounter values the fixtures never modelled.

ground.py is the grounding stage. calculator_scores is a function inside it. The pipeline reached the grounding stage, called the function, got a result, and continued. The result was wrong. No usable grounded output came through. The pipeline didn’t crash. There was nothing to crash on. It completed. It produced nothing.

Two manual orchestrate.py cycles. Same result both times.

The devlog also surfaces a session-quality issue alongside the calculator_scores bug: two separate problems on the first live day. Neither was architectural. Every stage ran in sequence; the state machine stayed consistent. The problems were in the output, not the structure.

The live harness and what it was built for

The S-3 sprint added .editorial/scripts/s3_harness.py, a script that drives the real chain end-to-end to produce the evidence the operator’s 20-claim spot-check operates against. The hermetic tests prove individual logic paths. The harness proves the full integration: real fetches, real grounding calls, real drafting.

S-3.2’s stale-rate fixture is a controlled input. The harness output is real. If calculator_scores returns wrong values, the spot-check is verifying wrong values. The fixture never surfaces this because the fixture doesn’t call the real function under real conditions.

This is why S-3.3 was deferred. A clean-control fixture built before any live observation is a fixture that confirms your assumptions. The calculator_scores case is the concrete demonstration of why the order matters.

The fix

PR #52 addressed calculator_scores in ground.py. After it landed, the pipeline ran again manually. The second attempt needed two surgical operator tweaks before it cleared. PR #53 opened: the first article shipped through the full X.8 grounded pipeline since the gates lifted. The piece was the Bank of England MMC March 2026 mortgage take.

Two cycles with the bug. Two tweaks after the fix. One published article.

The operator tweaks are worth noting separately. They were editorial corrections made during the run, within normal operating parameters for a grounded pipeline where the operator is in the loop by design. The bug was the blocker; the tweaks were the fine-tuning. These are different kinds of intervention.

What this means for the test suite

S-3.1 and S-3.2 stay. They cover real failure modes with hermetic tests. The defer-ungrounded path defers when grounding is absent. The stale-rate path refuses when grounding is stale. The lesson is not that hermetic tests are wrong. It’s that they prove a different thing from live harnesses.

Hermetic tests prove the logic path. Given input X, does the pipeline do Y? They are fast, deterministic, and exhaustive over the cases you can enumerate. Their blind spot is the fixture-to-reality gap: values, shapes, or ranges the test author didn’t model.

Live harnesses prove the integration. Given the real chain, does it produce a real output? They are slower and less repeatable. Their coverage of individual logic paths is shallow; their coverage of the data layer is deep. They catch what hermetic tests structurally can’t: a function returning wrong values under real inputs, a session-quality issue that fixtures don’t reproduce, a downstream failure that only appears when every stage is running for real.

calculator_scores was a live-harness bug. The only path to finding it was running the real pipeline and observing the output. No hermetic fixture was going to surface it.

What changes now

S-3.3 has a concrete case to model against. A clean-control fixture built from the post-fix observed behaviour reflects reality rather than expectation. That’s the right order of operations, and it required the live cycle to fail first.

The 20-claim spot-check protocol depends on the harness output being correct. A spot-check operating on bad grounding data is a false-negative machine. Having the bug surface before the cron ran unattended for a week is the outcome the live-first sequencing was designed to produce. It worked.

Out of scope: automating early detection of calculator_scores-class bugs. The current position is that the live harness catches them. Adding a fixture for a data-shape assumption not yet observed in the wild is guessing. The fixture comes from the failure, not before it.

The cadence clock is at Day 0. The pipeline has one clean live cycle on record: grounded, operator-reviewed, PR raised, article published. That’s the baseline. The sabotage tests define the floor. The live harness defines the ceiling.

All writing