Skip to content

Latest commit

 

History

History
630 lines (508 loc) · 38.1 KB

File metadata and controls

630 lines (508 loc) · 38.1 KB

008 — is the M1 pushable-block family decidable enough to carry M2's transfer curve, without being merely chaotic?

Status: NOT-TESTABLE (evaluated 2026-07-28) — the registered pre-fit clause fired at all six gating cells and no arm was ever fit. See ## Result at the foot of this file. The four difficulty knobs were frozen in the commit that registered this gate (24157ca), before any transfer arm existed and before any calibration number was measured.

(Direct discharge of Gate 005's committed "What a STOP costs M2" clause (005-anytime-transfer-metric.md, ~L346 and again ~L551), which handed the resolution to M1 as a world/budget design problem and named three levers. This gate freezes those levers as concrete constants and tests whether they worked. It is a precondition, in the Gate 006/007 sense — the decidability check computed before any arm is fit — and it does not create a rung. It decides whether M2 may be built on this family at all.)

(Naming: per the 2026-07-28 convention, no new rung letter is minted. This is "M1's difficulty knobs (Gate 008)".)


What Gates 004 and 005 established, and the one number that reshapes the fix

Gate 004 (STOP): the self-calibrated test–retest noise floor NF came back 0.0 in every cell — the full-budget few-shot fit converges to the same optimum regardless of fit-RNG, so most goals were never decidable (11–14 of 48 on room). Gate 005 (STOP): the obvious fix — score the fit trajectory rather than its endpoint — was pre-registered and failed.

The number that constrains this gate's design is Gate 005's own TIE_frac = 0.31 / 0.35 / 0.35. Those are bitwise float equalities read over the whole ladder K = {1,2,4,8,16,29} (score_ladder, src/transfer.mojo:803). So ≥31% of goals tie one ES step in, while the endpoint tied 100%.

Consequence: shortening FEW_ITERS cannot clear a tight tie bar on the walls family. It is bounded above at ~0.31 by data already published. Gate 005's lever #2 is real but secondary; the world-content knobs (K1/K2/K4) are what must carry the bar. Any design claiming the budget knob alone suffices contradicts Gate 005's own numbers.

The second consequence is that the tie mechanism is not a budget artifact at all. It is structural: BC_DIM = 18 carries 16 occupancy dims (typically ~[0, 0.15]) against 2 pose dims (typically ~0.5 off) — BC_DIM = BC_BLOCKS + 2, src/sandbox.mojo:79-81. The MSE is dominated by a two-dimensional positioning subproblem that every policy solves the same way.


The knobs (frozen — world-design parameters, not metric knobs)

Unchanged and load-bearing: POLICY_DIM = 294, SB_ACTIONS = 6, BC_BLOCK = 4, BC_DIM = 18, FEW_N = 32, FEW_ALPHA0/1, FEW_SIGMA0/1, BUILD_*, SEL_K = 8, SEL_POOL = 9. SB_ACTIONS/POLICY_DIM staying at 6/294 is deliberate: it keeps the .rep format and every stored elite binary-compatible across the old and new worlds, which M2's cross-world retain needs, and it makes "strict superset" a mechanically checkable property rather than a slogan.

K1 — push_mode (the load-bearing knob). World design.

symbol SB_PUSH_OFF = 0, SB_PUSH_ON = 1; new POD field SandboxTask.push_mode: Int
lives in src/sandbox.mojo (SandboxTask, sandbox_step)
current does not exist; movement into a positive cell is permitted — "the avatar coexists with blocks", src/sandbox.mojo:255-256
frozen __init__ sets push_mode = SB_PUSH_OFF; M1 worlds set SB_PUSH_ON

Under SB_PUSH_ON, a move into a positive cell pushes that cell one step in the move direction if the destination is in-bounds and empty; otherwise the move is refused. Walls (< 0) stay solid exactly as now. The SB_WALL pattern is kept honestly: dynamics parameters already live on SandboxTask by design (src/sandbox.mojo:6), the default reproduces the old dynamics bitwise, and no surrounding code changes.

Mechanism. A pushed block is positive, so _gravity_pass moves it and sandbox_bc counts it. One differing action displaces a block, which changes terminal occupancy — the channel that is currently absent.

K2 — SB_SEED_BLOCKS = 12. World design.

symbol SB_SEED_BLOCKS = 12
lives in src/sandbox.mojo (world construction)
current 0 pre-seeded blocks
frozen 12, placed by the same variant-seeded LCG constants scatter already uses, only on cells directly above a wall or on the floor row, skipping the avatar start cell

Mechanism. Without pre-seeded content a 12-iteration fit would have to paint before it could push, putting the extension out of reach inside budget. Placing blocks only on supported cells makes tick 0 a gravity fixed point, so the terminal arrangement is a direct function of the push sequence rather than of a settling transient — the cleanest possible sensitivity channel. On shelves, the wall rows r = 5 and r = 10 give genuine mid-air support, so a push off a shelf produces a cascade.

K3 — FEW_ITERS_M1 = 12 (Gate 005's lever #2, demoted). World/protocol design.

symbol new FEW_ITERS_M1 = 12, SEL_FIT_ITERS_M1 = 11; doubling probe uses 24
lives in src/transfer.mojo
current FEW_ITERS = 30 / SEL_FIT_ITERS = 29unchanged, not edited
frozen 12 / 11 for M1 worlds

Implementation constraint (contract-bearing). FEW_ITERS has 39 references across src/ and tests/. FEW_ITERS_M1 is a new constant used only by M1 worlds; editing FEW_ITERS in place would break reproduction-contract clause 3 below.

Why 12 specifically, by rules fixed in advance rather than by measurement: (a) strictly above the largest ladder rung (8) at which Gate 005 observed live variation, so it cannot be accused of having been placed inside the informative band; (b) it keeps the ITE budget-honesty ledger intact — SEL_POOL × SB_T = 576 trial ticks still cost strictly less than one ES iteration (2 × FEW_N × SB_T = 4096), verified at src/transfer.mojo:499-500, so a selection win can never be bought with ticks.

Why this is world design and not a metric knob — the clause has to hold up: it is frozen in the registering commit; it is applied identically to every arm; and this gate fits no warm arm, so there exists no warm-over-cold number against which it could have been tuned. The forbidden version would be choosing FEW_ITERS after seeing which value maximizes the warm margin. That is mechanically impossible here.

K4 — goal admission: the top-half-from-trivial rule (Gate 005's lever #3). World design, zero free parameters.

symbol CAL_POOL = 256, REF_CONST = 7, GOAL_ADMIT
lives in the gate harness's goal generator (a sibling of gen_family_disjoint)
current goals are any de-duplicated, repertoire-disjoint random-policy end-state
frozen draw CAL_POOL = 256 candidates; compute each candidate's occupancy-only distance (BC dims 0..15) to the nearest of 8 reference policies; admit only candidates in the upper half by that distance; then apply the existing emap.contains de-duplication and disjointness filters

The 8 references are zero-RNG and hand-free: the zero policy, plus 7 policies with a single output-bias slot set (always-up / down / left / right / paint / cycle / stay). They are the shared attractors — "do nothing", "walk into a wall", "paint in place" — the behaviours a 12-step fit falls into from any seed.

Mechanism. This is exactly Gate 005's "a continuous per-goal score that does not bottom out on an attractor shared by all arms", implemented as goal-set design instead of score design: the score stays byte-identical and the goals that are the attractor are removed instead. There is no threshold to tune later — "upper half" is a rank rule, not a cutoff. The goal set is fixed before any arm sees it, so it is symmetric across arms by construction. policy_score, sandbox_bc, sandbox_cell_key, calculate_fitness and BC_DIM are untouched; the contract below asserts empty diffs.

Why the score-design version is refused. The BC_DIM pose-share analysis above means re-weighting or dropping the two pose dims would fix saturation in one line — and that is exactly the forbidden move. It is a second metric change proposed after the first (Gate 005's) returned STOP; it is unfalsifiable from the inside (any re-weighting can be justified post-hoc); and it changes the number the consumer reads. The pose share is instead reported as POSE_SHARE with an explicit no-promotion clause (Gate 007's RECOV precedent): it may not become a bar in either direction once its value is known.

What is deliberately not a knob

  • No hand-authored sub-goal BC targets. The ROADMAP names "clear-a-path / stack-N-blocks / seal-a-region". This gate registers rollout-generated goals only. A curated target set is the single easiest place to smuggle per-task hand-staging into the family, and a hand-written "stack-N" BC vector is a symbolic target, not an emergent one. The names describe what the block world makes reachable; if hand-authored targets are ever wanted, that is a separate numbered gate.
  • No change to SB_ACTIONS. A dedicated push action is unnecessary (move-into-block pushes) and would break POLICY_DIM and .rep compatibility.
  • No change to the ES schedule beyond the K3 budget.

Hypothesis

On the frozen M1 pushable-block family at the frozen few-shot budget FEW_ITERS_M1 = 12, with an unchanged policy_score, the instrument has real resolution: two independent COLD fits of the same held-out goal disagree by more than float dust on ≥95% of goals (NF > 0, TIE_frac ≤ 0.05), and the instrument resolves a known-sign, arm-blind treatment (cold@12 vs cold@24) far more often than it resolves pure fit-RNG (SEP_2x − SEP_null ≥ 0.15), with a consistent sign (SIGN_2x ≥ 0.80) — so a later warm-vs-cold comparison on this family is capable of returning "no".

Failing the tie/NF clause means the world extension did not desaturate the instrument and M2 must not be built on this family. Failing the sign/separation clause means the family manufactured decidability out of chaos, which is the more dangerous outcome because it looks like success.

Arm-blindness is structural, not a promise. This gate fits no warm arm and builds no retrieval: no emap.nearest, emap.nearest_k, emap.select_uniform, or read of emap.weights appears anywhere in its harness. That is grep-checkable (contract clause 4) and it is what makes it impossible for any frozen value here to have been chosen to favour a warm start.

Metric + consumer

The consumer is run_family_select (src/transfer.mojo:580) — M2's reader. This gate calls a cold-only sibling of it; the fit call, the demo builder (make_demos) and the scoring call are identical. Secondary consumers: Gate 003's banked P_a / τ / RAWPRIOR_SCR via the N_dec ≥ 6 floor (see "Free evidence"), and the published MDE, which M2's own gate must consume.

Primary statistics (definitions taken from the existing harness, not re-invented — the NF / decidable_count machinery in tests/test_anytime_metric.mojo:733 and tests/test_cbr_retain.mojo):

stat definition
NF median_g |d_cold_a[g] − d_cold_b[g]| — two independent cold fits at different fit-RNG stream positions
TIE_frac fraction of goals with d_cold_a[g] == d_cold_b[g] bitwise
NF_rel NF / median_g d_cold_a[g]
SEP_2x decidable_count(d_cold24, d_cold_a, d_cold_a, NF, ng) / ng — the known-sign, arm-blind treatment
SIGN_2x among decidable goals, the fraction where cold@24 is strictly better
SEP_null decidable_count(d_cold_a, d_cold_b, d_cold_a, NF, ng) / ng — the nuisance-matched null
PROG median_g (d_ref[g] − d_cold_a[g]) / max(d_ref[g], EPS), d_ref[g] = best of the 8 constant-action references (zero RNG, zero fits)
SENS_occ pre-fit occupancy sensitivity: fraction of R_SENS = 32 random action-sequence pairs whose terminal occupancy BCs differ, over T_SENS ticks (zero fits)
POSE_SHARE reported diagnostic, no bar, no promotion
MDE minimum detectable effect per cell — the published deliverable M2's gate must consume

SENS_occ is measured in three matched cells per (world, seed), identical layout / references / ticks:

cell what differs
SENS_occ(M1) push_mode = ON, blocks seeded
SENS_occ(blocks-only) push_mode = OFF, blocks seeded (diagnostic, no bar — decomposes K1 vs K2)
SENS_occ(base) push_mode = OFF, no blocks — the family exactly as Gates 004/005 measured it

Thresholds (committed now)

Scored on the two designated M2 target worlds — push_room and push_scatter, both at variant = 0 — across all three seeds: 6 gating cells. push_shelves and push_columns are calibrated and reported (12 cells total) but do not gate: M2 never fits held-out goals in a source world, so gating on cells the consumer does not read would be theatre.

NOT-TESTABLE — checked first, in this order; nothing below is scored if any trips:

  • either reproduction-contract clause fails;
  • SENS_occ(M1) < 0.60 or SENS_occ(M1) − SENS_occ(base) < 0.20 in any gating cell — this is checked before any fit is run;
  • PROG outside [0.15, 0.85] in any gating cell (below ⇒ the family is too hard and every arm bottoms out on a ceiling, the mirror washout; above ⇒ goals are near-solved cold and there is no headroom for a warm start to occupy);
  • the harness references emap.nearest / nearest_k / select_uniform / .weights anywhere.

GO — every condition, all 6 gating cells:

  • NF > 0
  • TIE_frac ≤ 0.05
  • NF_rel ≥ DELTA_REL = 0.02 (the relative floor is binding, not float dust)
  • SEP_2x − SEP_null ≥ 0.15
  • SIGN_2x ≥ 0.80

STOP (fires at ≥ 2 of 3 seeds):

  • TIE_frac > 0.05 with NF = 0.0 — the Gate 004 washout survived the extension;
  • SEP_2x − SEP_null < 0.15 — the instrument cannot resolve a doubled budget, so it is blind to any effect a warm start could plausibly produce;
  • SIGN_2x < 0.60 — it "decides" with near-random sign: dispersion without ordering, i.e. chaos wearing resolution's clothes.

PARTIAL: any GO condition holding at exactly 2 of 3 seeds on a gating world (seed splits are never rounded up), or one gating world clearing a condition 3/3 while the other does not. PARTIAL licenses M2 only on the worlds that individually cleared, and M2 must re-register its world list in its own gate. It does not license dropping goals, worlds, or seeds, nor aggregating.

Seed plan

Seeds 0, 1, 2 — three required, per the house rule; AND-across-seeds for GO, ≥2/3 for a STOP trigger. This is a claim about an instrument, and instruments must hold on every draw.

Each seed independently redraws every stochastic ingredient: the two source repertoire builds, the CAL_POOL = 256 candidate goal policies (hence both the admitted goal set and the SENS reference draws), and all three fit-RNG streams. Three independent goal-set draws, not three reads of one.

Within a cell, the three fits are CRN-paired on one continuous fit-RNG stream, per goal, in a frozen draw order. The SENS probe, the constant-action references, and the admission filter's distance computation consume zero RNG and cannot move the stream:

seed(s) → build push_shelves rep → build push_columns rep   [BUILD_* verbatim, .contains() only]
        → for w in [push_room, push_scatter, push_shelves, push_columns]:
              SENS probe (3 matched cells, 0 draws)
              constant-action reference distances (0 draws)
              goal pool: 256 candidate draws → top-half admit → dedup → disjointness
              for goal g: cold_a@12 → cold_b@12 → cold@24

Stream warning — Gate 005 was nearly voided by exactly this (005-…md, "One registered detail that needed care"). The admission filter draws a fixed 256 candidates regardless of how many are admitted, so the fit-stream position is stable within this gate — but it is not the position Gates 004/005 fit at. Gate 008's numbers are therefore not comparable to the published n_dec = 11/14/10, and no such comparison may be made. Bitwise reproduction of those numbers is guaranteed instead by contract clause 3 (push_mode defaults to 0, so test_cbr_retain / test_anytime_metric draw from unchanged streams).

Refuting controls

The one that reads ~nothing if the knobs don't work: SENS_occ, then SEP_2x. If pushable blocks plus seeded content do not make the terminal occupancy BC a sensitive function of the action sequence, SENS_occ(M1) comes back near SENS_occ(base) and no fit is ever run — the failure costs seconds, not minutes, the property that made Gate 006's ceiling clause pay for itself on first use. If SENS passes but the fit still converges to a shared attractor, SEP_2x reads ≈ 0: a doubled budget is invisible to the metric. Either way the "no" is clean, cheap and unambiguous.

The one that could take the claim away — the nuisance-matched control: SEP_null and SIGN_2x. Gate 007's lesson is that the control must be matched on the nuisance dimension. Here the nuisance is dispersion: a chaotic world manufactures decidability without resolution and would produce a beautiful NF > 0 / TIE_frac = 0.0 table that means nothing. SEP_null is matched on everything — same world, same goals, same budget, same fit machinery, same two cold fits — and differs only in carrying no treatment. SIGN_2x then asks whether the decisions have a consistent direction or are coin flips.

The zero-RNG matched control on a pre-fit statistic: SENS_occ(base) is the family exactly as Gates 004/005 measured it, with the same reference policies and the same ticks. The ≥ 0.20 delta is the load-bearing form of the precondition; the absolute ≥ 0.60 is the more guessable of the two stated bars, and is recorded as such.

Reproduction contract (checked FIRST; failure ⇒ NOT-TESTABLE)

  1. Zero learning-core change. Empty git diff on src/esper_evolution.mojo, src/memory.mojo, src/map_elites.mojo, the Domain trait in src/arc_io.mojo, and ExamplePair/Task in src/hope.mojo.
  2. Zero metric change. Empty git diff on policy_score, compose_score, sandbox_bc, sandbox_cell_key, calculate_fitness. BC_DIM == 18, POLICY_DIM == 294, SB_ACTIONS == 6 asserted at runtime.
  3. The extension is a true strict superset — mechanical proof. ./esper test cbr_retain and ./esper test anytime_metric still reproduce bitwise: Gate 004's room numbers R1 = Rc = 1.0, NF = 0.0, n_dec = 11/14/10. ./esper test fit_traced still passes (0/294 differing floats). This makes "push_mode defaults OFF and nothing else moved" a checkable fact rather than a claim.
  4. Arm-blindness. grep -nE 'nearest|select_uniform|\.weights' over the harness returns nothing.

Anti-stone-soup clause

The fits stay single COLD fits at the frozen budget. Specifically, all of the following are scaffolding, and a pass obtained with any of them is a documented negative regardless of the raw number:

  • Any post-hoc move of a frozen value: push_mode, SB_SEED_BLOCKS = 12, the placement rule, FEW_ITERS_M1 = 12, the probe at 24, CAL_POOL = 256, REF_CONST = 7, the admission rule, the 8 constant-action references, R_SENS = 32, T_SENS, DELTA_REL = 0.02, N_DEC_MIN = 8, NUM_GOALS/NUM_GOALS_ESC, every bar above, the seed set.
  • No per-world or per-goal tuning of FEW_N, FEW_ALPHA0/1, FEW_SIGMA0/1, grav_rate, or the wall variant. One schedule, every cell.
  • Raising FEW_ITERS_M1 mid-gate because the fit "under-fits"; and preserving the 30-step anneal rate under a 12-step budget is itself a design choice frozen here, not a mid-gate adjustment.
  • Any re-weighting, re-scaling, or dim-dropping of the BC — including dropping the two pose dims — is a forbidden metric knob. POSE_SHARE may not be promoted to a bar in either direction.
  • No hand-authored goal BCs, no per-goal curation, no re-drawing a goal set that produced a low n_dec, no raising CAL_POOL or max_tries to fish for goals.
  • No escalation beyond the inherited 24→48 schedule, and escalation stays driven by the endpoint decidable count exactly as in Gates 004/005.
  • No best-of-seeds, no dropped seed, no dropped world, no mean-over-worlds when a per-cell condition fails.
  • SENS_occ(blocks-only) is a diagnostic and may not be promoted to a bar once its number is known.

Freezing rule

The knobs are frozen in the commit that registers this gate — before any transfer arm exists and before any calibration number is measured.

  • On STOP (the family is measurably saturated, or measurably chaotic), the knobs are not re-rollable. Re-tuning world or budget parameters after seeing a saturation number is precisely the post-hoc knob Gate 005 forbade. M2 is not built on the sandbox transfer family, and M5 gains a third publishable negative.
  • On NOT-TESTABLE (the instrument never ran), there is exactly one re-roll, as Gate 009, with the complete screening table published.

The asymmetry follows the Gate 006→007 precedent: NOT-TESTABLE means the instrument never ran; STOP means it ran and answered. An unlimited re-roll budget would make this precondition unfalsifiable.

Free evidence: does this cross Gate 003's N_dec ≥ 6?

Gate 008 itself contributes zero. Gate 003 needs signed warm−cold margins per (world, seed) group; Gate 008 fits no warm arm, so it produces none. Said plainly rather than left to look like progress.

What it can fix is M2's arithmetic, which is why the held-out world count is a frozen knob:

M2's held-out world count groups vs Gate 003's floor of 6 decidable groups
1 (push_room only, Gate 004's shape) 3 impossible
2 (push_room, push_scatter) 6 exactly the floor, zero slack — one undecidable cell sinks it
3 (add a second (kind, variant) pair) 9 3 groups of slack; needs 6/9 decidable

So M2 must register ≥3 held-out (kind, variant) worlds. The layouts are already parametric on variant (gen_walls_layout), so this costs a constant, not a build. Two further registered asks on M2, both free: record the raw W1 prior per group so Gate 003's banked P_a / τ = 0.150423175 / RAWPRIOR_SCR become scorable, and report per-group signed margins, not only medians.

Compute cost

component cost
pre-fit pass (SENS × 3 matched cells, references, admission) — 4 worlds × 3 seeds seconds
fits: 3 cold fits/goal × 24 goals × 4 worlds × 3 seeds at 12/12/24 iterations ~4–6 min CPU
total ~4–6 min CPU

Cheaper than Gate 004's ~3 min per-arm count would suggest, because 48 fit-iterations per goal replaces 150, and no warm arms are fit.

Harness: tests/test_m1_calibration.mojo, default path ES-free (fast tier), fits behind an argv flag (--fits, # suite-tier: full) — Gate 007's --arms pattern, which also keeps the family-freezing commit provably arm-free.

Disclosure — the ways this gate is weaker than it looks

  1. A GO licenses spending M2's budget; it licenses nothing about transfer. It says the instrument can return "no". It is not a signal about what M2 will find.
  2. SEP_2x calibrates resolution of a budget effect, not a transfer effect. If retained structure's effect is much smaller than a 2× budget difference, this is the single most likely cause of a later miss. No arm-blind control can calibrate the size of a transfer effect without fitting a warm arm — and fitting one here would destroy the structural guarantee that makes every frozen value in this gate credible. The effect-size question is pushed onto M2 as the registered, published MDE.
  3. The SENS decomposition is extension-level, not factor-level in its gating form: SENS_occ(M1) vs SENS_occ(base) differs in both push and blocks. The third cell decomposes it, but it is non-gating.
  4. This gate's goal sets are not M2's. The admission filter changes the draw. It counts as calibration, not prediction.

Consequence

  • GO ⇒ M1's frozen knobs are written into ROADMAP verbatim, MDE is published per cell, and M2 may register its Gauntlet gate on this family — still bound by Gate 005's RAND-MATCHED structure-free control rule and by an M2-registered effect-size floor.
  • STOP ⇒ the family is measurably unsuitable. M2 is not built on the sandbox transfer family; the approach closes for a measured reason and the write-up (M5) gains a third publishable negative. No re-roll.
  • NOT-TESTABLE ⇒ exactly one re-roll, as Gate 009, with the complete screening table published.

Registration errata (2026-07-28) — corrections made BEFORE any number was measured

Five registered values could not be implemented as written. Every correction below was made while building the harness, before a single fit was run and before --fits had ever been invoked; the working tree contained no arm code when they were written. Each is recorded as a forced reading — the registered text was unrealizable or absent, and in each case exactly one implementation is consistent with the rest of the frozen spec. None of them is a post-hoc move of a value against a seen number, which is what the freezing rule and the anti-stone-soup clause forbid.

E1 — REF_CONST = 7REF_CONST = 6; "8 references" ⇒ 7 reference vectors. K4 names seven single-bias constant-action policies "always-up / down / left / right / paint / cycle / stay". There is no stay action: SB_ACTIONS = 6 (src/sandbox.mojo:40) is up / down / left / right / paint / cycle-brush. Separately, the zero policy produces all-zero logits, and policy_argmax (src/sandbox.mojo:~419) takes the first maximum, so the zero policy is always-up. The reference set is therefore the zero policy plus one single-bias policy per real action: 7 vectors, 6 distinct trajectories. The duplicate is inert — d_ref[g] is a distance to the nearest reference, so a repeated member cannot change it. REF_CONST remains hand-free and zero-RNG, which is the property K4 depends on.

E2 — T_SENS had no value. It is listed among the frozen constants (this file, "Anti-stone-soup clause") but is never given a number. Frozen as T_SENS = SB_T = 64: the same tick budget every scored rollout already uses. Any other value would introduce a genuinely new free parameter into a gate whose whole claim is that its parameters were fixed in advance.

E3 — MDE had no formula. It is named a published deliverable that M2's gate must consume as its effect-size floor, but no definition is given. Frozen as the threshold the registered decidable_count already applies (tests/test_anytime_metric.mojo:343-344):

MDE = median_g max(NF, DELTA_REL · d_cold_a[g]), reported alongside MDE_rel = MDE / median_g d_cold_a[g].

This introduces no new constant: it is NF and DELTA_REL = 0.02, both already frozen, combined exactly as the decidability rule combines them. MDE is the smallest per-goal effect this instrument can call.

E4 — the anneal rate needed derived endpoint constants. The anti-stone-soup clause freezes "preserving the 30-step anneal rate under a 12-step budget". But fit_operator (src/esper_evolution.mojo:349-350) derives its schedule as alpha_rate = log(alpha1/alpha0)/iters — it anneals by fraction of the budget, not by absolute step. Passing iters = 12 with the stock FEW_ALPHA0/1 and FEW_SIGMA0/1 would compress the whole 0.1→0.02 / 0.3→0.05 decay into 12 steps, a 2.5× faster anneal — the opposite of what is frozen. Reproduction-contract clause 1 forbids editing src/esper_evolution.mojo, so the rate can only be preserved by rescaling the endpoints. Frozen as new derived constants in src/transfer.mojo, computed in code from the already-frozen ones and never hand-typed:

FEW_ALPHA1_M1   = FEW_ALPHA0 * (FEW_ALPHA1/FEW_ALPHA0) ** (FEW_ITERS_M1   / FEW_ITERS)  ~ 0.0525322
FEW_SIGMA1_M1   = FEW_SIGMA0 * (FEW_SIGMA1/FEW_SIGMA0) ** (FEW_ITERS_M1   / FEW_ITERS)  ~ 0.1464844
FEW_ALPHA1_M1X2 = FEW_ALPHA0 * (FEW_ALPHA1/FEW_ALPHA0) ** (FEW_ITERS_M1X2 / FEW_ITERS)  ~ 0.0275946
FEW_SIGMA1_M1X2 = FEW_SIGMA0 * (FEW_SIGMA1/FEW_SIGMA0) ** (FEW_ITERS_M1X2 / FEW_ITERS)  ~ 0.0715455

With these, alpha(t) and sigma(t) are the same functions of t at 12 and at 24, so cold@24 is a literal continuation of cold@12's schedule through its first 12 steps. That is what makes SEP_2x / SIGN_2x a known-sign budget treatment rather than a schedule change — without it the "doubled budget" arm would differ in two ways at once and SIGN_2x would be uninterpretable.

E5 — SENS_occ's "random action-sequence pairs" was undefined, and only one reading is satisfiable. The metric is "the fraction of R_SENS = 32 random action-sequence pairs whose terminal occupancy BCs differ", without saying how the two members of a pair relate. Frozen as: one uniformly random action sequence of T_SENS ticks, paired with the same sequence with one uniformly-chosen tick's action resampled — i.e. the pair differs in exactly one action. This is the gate's own mechanism sentence for K1 ("One differing action displaces a block, which changes terminal occupancy — the channel that is currently absent").

The alternative reading — two independently drawn sequences — is not merely different, it makes the registered bar unsatisfiable by construction: two independent sequences diverge in the avatar's whole paint trajectory, so SENS_occ would sit near 1.0 in the base cell as well as in M1, and the registered SENS_occ(M1) − SENS_occ(base) ≥ 0.20 gap could never open regardless of whether the knobs work. A reading under which the gate cannot fail or pass on its merits is not a reading.

This is the one erratum that is an interpretive call on a scored metric, and it is disclosed as such. It is not self-serving: it is the stricter of the two readings (a one-action perturbation is much harder to detect than two independent rollouts), and it is falsifiable — if SENS_occ(base) comes back near 1.0 even under this definition, the instrument is saturated at the pre-fit stage and the verdict is NOT-TESTABLE, which the harness must report rather than work around.

What did not change. No threshold, no bar, no seed, no world, no goal count, no push_mode / SB_SEED_BLOCKS = 12 / placement rule / FEW_ITERS_M1 = 12 / probe-at-24 / CAL_POOL = 256 / DELTA_REL = 0.02 / N_DEC_MIN = 8, and nothing in the GO, STOP, PARTIAL or NOT-TESTABLE conditions. The reproduction contract and the arm-blindness clause are unchanged and still bind.

E6 — SENS_occ's randomness source was contradictory in the registration, and is resolved by an unregistered constant. The Seed plan says each seed "independently redraws every stochastic ingredient", listing the CAL_POOL = 256 candidate draws and the SENS draws; twelve lines later the frozen draw-order diagram says SENS probe (3 matched cells, 0 draws). Both cannot hold: a probe over "random action-sequence pairs" that consumes zero global draws is unimplementable from the global stream. The 0 draws clause is the load-bearing one (it is what makes the SENS probe provably unable to move the fit stream, and the harness proves it mechanically), so it wins, and the randomness comes from a local LCG — the same scatter LCG the world generator already uses, seeded by

sens_seed_for(kind, variant, seed_idx)
  = SENS_LCG_SEED_BASE + 7919·kind + 104729·variant + 1299709·seed_idx
SENS_LCG_SEED_BASE = 20260728

SENS_LCG_SEED_BASE was chosen at build time and was not registered, yet it directly determines a scored number. That is exactly the exposure E5 was written to disclose, and the errata section above wrongly claimed completeness ("Five registered values…", "no seed" changed) — this erratum corrects that claim.

Because the constant is unregistered, it is screened rather than defended: re-running the six gating cells with everything else frozen, over six alternative bases (./esper run tests/test_m1_calibration.mojo --diag):

SENS_LCG_SEED_BASE cells clearing the 0.20 gap bar worst gap
1 1 / 6 −0.094
12345 0 / 6 −0.031
20260101 0 / 6 −0.063
20260728 (chosen) 0 / 6 −0.094
99991 0 / 6 −0.063
777777777 0 / 6 −0.188

No base clears the bar on more than one of six cells; the chosen base is mid-pack, and base 1 is strictly more favourable than the one used. The verdict does not depend on the choice.


Result — NOT-TESTABLE (2026-07-28)

Run: ./esper run tests/test_m1_calibration.mojo — the ES-free default path, seconds. The registered pre-fit clause fired, so the --fits block never executed and the ~5 min of ES was never spent. New code: K1 push_mode + K2 seed_blocks in src/sandbox.mojo, the derived M1 budget constants in src/transfer.mojo, and tests/test_m1_calibration.mojo.

The scored pre-fit table (R_SENS = 32, T_SENS = 64, variant 0)

world seed SENS_occ(M1) SENS_occ(blocks-only) SENS_occ(base) gap gating
push_room 0 0.781 0.625 0.656 +0.125 yes
push_scatter 0 0.781 0.750 0.875 −0.094 yes
push_room 1 0.719 0.625 0.688 +0.031 yes
push_scatter 1 0.813 0.781 0.781 +0.031 yes
push_room 2 0.688 0.625 0.563 +0.125 yes
push_scatter 2 0.750 0.688 0.688 +0.063 yes
push_shelves 0 / 1 / 2 0.656 / 0.688 / 0.688 0.531 / 0.781 / 0.656 0.625 / 0.656 / 0.656 +0.031 / +0.031 / +0.031 no
push_columns 0 / 1 / 2 0.688 / 0.594 / 0.656 0.719 / 0.625 / 0.594 0.625 / 0.656 / 0.625 +0.063 / −0.063 / +0.031 no

SENS_occ(M1) − SENS_occ(base) ≥ 0.20 fails at 6 of 6 gating cells. By the registered clause that is NOT-TESTABLE, checked and reported before any fit — which is what the ES-free path was built to buy. The stream-integrity probe passed (the SENS path drew 0 from the fit stream), and the reproduction contract holds in full (below).

Two honesty notes on the table. First, push_columns seed 1 reads SENS_occ(M1) = 0.594, under the 0.60 absolute bar — it is correctly non-gating, but a follow-up gate tempted to widen the world list should know. Second, the absolute bar passed on the gating cells only by sampling luck: R_SENS = 32 carries a per-cell standard error near ±0.09, and the --diag precision screen at R = 2000 puts push_room's true SENS_occ(M1) at ≈0.58 — below 0.60 — at all three seeds. The scored numbers remain the R = 32 ones (R_SENS is registered and is not re-chosen after the fact), but the honest reading is that both bars were failing, not one.

Why — the gate's founding premise is falsified by its own probe

K1 and K2 exist because Gate 004's NF = 0.0 was diagnosed as a world problem: terminal occupancy supposedly barely depends on which actions were taken, so independently-seeded fits had nothing to separate them. The probe says otherwise. The base cell — no seeded blocks, push OFF, the family exactly as Gates 004/005 measured it — already flips terminal occupancy on 0.56–0.88 of one-action perturbations. A single differing move relocates the avatar, so every later paint lands on a different cell.

That is not a paint artefact. Restricting the perturbed tick to a move action (--diag) leaves base at 0.50–0.78. The world was never action-insensitive.

The knobs are also not inert — at R = 2000 the M1−base gap is positive in all six cells, +0.018 to +0.038. The push channel is real and roughly 6× too small to reach the registered bar. "K1/K2 add a channel to a world that was never missing one" is the accurate summary, and it is a sharper result than "the knobs did nothing".

Consequence for the roadmap. World difficulty is not the binding constraint behind NF = 0.0. The open question moves to fit/policy space: why do independently-seeded fits converge to the same behaviour in a world that is demonstrably action-sensitive? No world knob can answer that, so the sanctioned route is a new gate aimed at the fit, never nudging K1/K2 until the gap looks right.

Reproduction contract — all four clauses verified

clause check result
1 — zero learning-core change git diff --stat on esper_evolution / memory / map_elites / hope / arc_io empty
2 — zero metric change per-function diff vs HEAD: sandbox_bc, sandbox_cell_key, sandbox_obs, policy_forward, policy_argmax, _gravity_pass, policy_score, compose_score all identical
3 — bitwise reproduction test_fit_traced, test_cbr_retain, test_anytime_metric vs pre-edit captures byte-identical
4 — arm-blindness grep -nE 'nearest|select_uniform|\.weights' tests/test_m1_calibration.mojo no matches

Clause 3 is the one that makes "push_mode defaults OFF and nothing else moved" a fact: the baselines were captured before any src/ edit, and cbr_retain still reproduces Gate 004's published room numbers exactly (R1 = Rc = 1.0, NF = 0.0, escalated n_dec = 11 / 14 / 10).

Known limitation for a follow-up gate

The three "seeds" vary the action-sequence stream only: variant is hardcoded to 0, so all three share one wall layout and one block placement per world. The registered Seed plan says each seed redraws every stochastic ingredient. This does not threaten a negative verdict — the R = 2000 screen confirms the effect is real and tiny regardless — but it means the result is "2 worlds × 3 action-draws in 2 fixed layouts", not three independent world draws, and a successor gate should pass variant = s (gen_walls_layout is already parametric) at no cost.

The --fits block below the pre-fit return is type-checked but has never executed. A follow-up gate that runs it should expect first-run bugs that no compile can catch.