โ€บNavigation

Iteration 2 โ€” five defects closed RULE 0 ยท NO GATE RUN ยท 0 DECIDED

2026-07-31 ยท ← Axis-2 evaluation ledger ยท ← iter 1 ยท PR #686 ยท LEDGER rows 0q–0r

5defects closed (D12/13/24/15/16)
32tests, from 9 at iter 0
0candidates decided
D14next blocker

D12 — C2 could never pass (operator ruling)

run_c2 compared the Clopper–Pearson upper 95% bound against 0.05 over 200 draws, which tests whether size is significantly below α rather than bounded by it. C2 is correctly sized by construction, so E[hits] = 10; the gate admitted at most 3.

P(hits <= 3 | size = 0.05) = 0.00905     per cell
P(all nine cells pass)     = 4.07e-19    for the control

A failed control hard-stops the loop, so the campaign as built could never have reached its first candidate. It also contradicted the sibling gate in the same file: choose_block_length accepts an L only if its size lands inside a band that contains 0.05. Whenever calibration succeeded, C2 failed.

Operator ruling: the size band, frozen as amendment 31a. On the over-rejection side the band and the one-sided alternative are identical at m=200 (both fail at ≥17 hits), so two-sidedness is free where §5's wording is directive. What it buys is the low end — where a degenerate null, one whose block permutation ran out of distinct orderings, would otherwise pass as “conservative”. That is not hypothetical: it is defect D14, still open.

D13 + D24 — the N_min evidence binding was decorative

Both reproduced against the pre-fix code before being touched, because an audit finding is a claim until it is.

D13:  *** RUN VOID — N_MIN.json references result artifact
      .../seal_prime_controls_result_750.json, which is absent

D24:  load_n_min(250) -> 99999      (250's own artifact was never hashed)

D13 pointed at a path nothing ever wrote, so load_n_min failed forever and F1/F4 were permanently unrunnable — the mechanism whose whole purpose is binding a number to its evidence was guaranteed to reject its own output. D24 kept one hash at the top level and overwrote it each firing, so every threshold but the last was served unbound.

Binding is now per-threshold and path-relative to N_MIN.json; emission is gated on SEAL-PASS, not merely on the power stage having run. Given D12, every run to date would have been a SEAL-FAIL that still seeded the SSoT.

D15 — the envelope gate was skippable by leaving a variable unset

The check sat inside a truthiness test on SPIKE_CAMPAIGN_DIR. Unset it, and all of E1–E5 vanished — no shim on PATH, no readonly=2, no memory cap — and ~180,000 permutations were dispatched at a shared production ClickHouse anyway. Nothing printed; no field recorded the skip. The comment above it read “Fail-closed preamble.”

An unset variable is now the loudest failure rather than the quietest, which is the right ordering: not knowing what the caps are is strictly worse than knowing they are wrong. Set-but-wrong fails identically, or the variable is a rubber stamp.

D16 — the ceiling was never merely unvalidated. It was unapplied.

The audit found the verification half. Reading the enforcement path turned up the half that actually mattered, and it is measurable, so it was measured — through the real shim, against the live server:

without SPIKE_ENVELOPE_JSON    max_memory_usage = 5368709120    (5 GiB, the BASE)
with    SPIKE_ENVELOPE_JSON    max_memory_usage = 2684354560    (2.5 GiB, the campaign)
readonly = 2 and max_threads = 2 in both

clickhouse_client_shim.sh caps the server off SPIKE_ENVELOPE_JSON; the runner exported AXIS2_ENVELOPE_JSON and never that. So the halving was decorative and two concurrent queries could reach 10 GiB — exactly what the campaign envelope was written to prevent.

The verification half compounded it: preflight.py read the same unset variable and passed while confirming the untightened base. Repointing it was not a fix, because E1 is an equality tripwire — 2.5 GiB failed as DRIFT. AXIS2-ENVELOPE.json's own claim that “preflight.py E1 enforces this direction” was false, and is now corrected in-file.

The fix, and why E1 was left alone

envelopequestiongate
BASE SPIKE_BASE_ENVELOPE_JSONhas the frozen declaration drifted?E1 — still equality
ACTIVE SPIKE_ENVELOPE_JSONis the server capped the way this run declares?E3–E5
and does it only tighten the base?E1b — new

E1 was deliberately not loosened to . The probe-arbitration campaign states its measured results against those exact frozen numbers; weakening the tripwire would silently re-scope another campaign's evidence — precisely what this envelope's own _why_not_edit_the_shared_file exists to prevent. When no campaign envelope is declared, both paths resolve to the same file and behaviour is unchanged for every existing caller.

ACTIVE keeps the name SPIKE_ENVELOPE_JSON on purpose: that is the variable the shim reads. Verification and enforcement must resolve the same file, or the check is theatre — which is this defect in one sentence.

readonly is excluded from the comparison and requires exact equality. It is a mode, not a budget: readonly=0 is numerically smaller and strictly more dangerous, so a naive tightening rule over every key would wave the most important weakening straight through.

Both controls, in force on bigblack

POSITIVE   E0 E1 E1b E2 E3 E4 E5 L1 L2 L3   all PASS
           E3: server observes max_memory_usage = 2684354560

NEGATIVE   widened ceiling + changed readonly:
           E1  PASS  (base still matches frozen)
           E1b FAIL  max_memory_usage (10737418240, 5368709120, 'looser than base')
                     readonly         (1, 2, 'must be identical — it is a mode')

A gate never observed to fail is not a gate. E1b was observed both ways.

Verification

suiteresult
tests/test_axis2_seal_controls.py32 passed, 1 skipped (9 at iter 0)
axis2:self-test12/12
ch_concurrency_smoke_test.py — live9/9
preflight E0–E5 + L1–L3 — liveall PASS

What is still open

D14 is the next blocker: derived N_min is floored by nb! permutation granularity rather than by power. At |ic| = 1.000 — a perfect signal — the p-value floor is 0.43 at nb=2 and 0.24 at nb=3, so nothing is detectable. The planned fix is derived rather than chosen: refuse grid points below nb ≥ 4, so at least 1/α = 20 distinct orderings exist and p ≤ 0.05 is attainable at all — and record the refusal rather than reporting a floored number as if it were a measurement.

And a distinction worth keeping straight: the 2.5 GiB ceiling is now enforced. It is still not validated — nobody has shown the worst cell survives at that ceiling. Those are different claims, the second is attention A2, and run_seal_prime.sh still refuses to fire until a sweep artifact is committed.

Standing count

27 defects found, 7 closed, 20 open. Still no candidate evaluated, and the harness has still never run a gate end to end.

Iter 1 predicted the remaining defects would live in the paths that only open once the harness gets far enough to reach them. D16's enforcement half fits that shape exactly: it was invisible until someone traced what the shim — not the checker — actually loads.