2026-07-31 ยท ← ledger ยท ← iter 4 ยท PR #686 ยท LEDGER row 0u
choose_block_length contains exactly the right fix for Y_tail:
if is_tail:
L_pw = max(L_pw, K.exceedance_run_p95(y > 0))
and the call site passed False, hardcoded. So exceedance_run_p95 was unreachable, and Y_tail — which is the cascade question this whole axis exists to ask — was tested with a block shorter than its own exceedance clusters.
A too-short block destroys the clustering the null must preserve. The null comes out too narrow and the test over-rejects — pushing in the same direction the C2 defect (D12) did, on the target where it matters most.
L was calibrated on kyle_lambda_proxy × Y_cost and reused for all 3 candidates × 3 targets. But Politis–White derives L from the IC integrand zrank(f) · zrank(y), so L is a property of the pair — and Y_tail's integrand is two-valued, because 95% ties collapse its z-rank to two distinct values. Nothing like Y_cost's continuous one.
Third: the calibration window was slice(0, 3000) — the oldest 3,000 bars. On a 2018–2026 span that is 2018 microstructure calibrating a test applied through 2026.
L is now calibrated per target, with the tail floor live for Y_tail only, on the candidate whose Politis–White length is largest — the most conservative null, the same worst-L discipline already used to choose among the sweep — over the most recent rows.
An offline structural smoke of the whole pipeline produced the confirmation:
Y_cost calibrated on kyle_lambda_proxy Y_tail calibrated on aggression_ratio <- a DIFFERENT candidate Y_delta calibrated on kyle_lambda_proxy
The per-pair dependence the single-L design assumed away is visible on the first run that looks for it.
The fourth sub-defect — size calibrated on a subsample and asserted at full N — is retained as an honest limit. Block-permutation size depends on L/n, so a subsample calibration is an approximation whichever window you pick; there is no version of this that is free. It is now recorded per target alongside its window and n, so a reader can see the approximation instead of inheriting it.
| suite | result |
|---|---|
tests/test_axis2_seal_controls.py | 60 passed, 1 skipped (9 at iter 0) |
axis2:self-test | 12/12 |
ch_concurrency_smoke_test.py — live | 9/9 |
27 found ยท 13 closed ยท 1 retired ยท 13 open ยท 0 blockers.
The three remaining substantive items — D25, D26, D27 — share a property worth naming: none of them can change a verdict. They change what can be reproduced and audited about one. D25: a single serial RNG with data-dependent consumption, so no individual cell can be re-run and reproduce its own numbers — which is what a frozen seed is for. D26: RUSAGE_SELF excludes the ClickHouse client subprocesses and the server, so it cannot supply the very measurement the envelope demands. D27: seven inline literals with no SSoT binding, in a file the self-test's T4 does not scan.
SEAL′ still refuses to fire until the 2.5 GiB ceiling sweep artifact is committed.