IGUANA · proposal fine-tuning for SMC · working notes, 2026-07-22

Three failure modes of IGUANA training — isolated, with their fixes

Every curve is a real training run on DS-1000 (Qwen2.5-Coder-3B, LoRA). red = failure mode active   blue = same run with the fix. Panel badges mark the training scale: 10-prompt probe vs full split (591 prompts).

The gradient, and where it breaks

IGUANA fine-tunes the proposal by descending the forward KL to the posterior, estimated with centered, self-normalized importance weights over M sampled particles:

Objective & gradient ∇φKL (π∥qφ) ≈− ∑m=1M wm ∇φlog⁡ qφ (y(m))
IGUANA weight wm= uw(y(m)) −b(x) M·Z(x) , b(x)=Z(x)
Importance weight uw(y)= r(y) pT(y) qφ,T(y) ∈[0,∞), = r(y) ∈ {0,1} at init, where q = p
Running normalizer Z(x)= 1K ∑k=1K Z^k(x) , Z^k= 1M ∑m uw(y(m))
Offline estimate of Z(x) Z(x)= 𝔼y∼pT(·∣x) [r(y)] ≈ 1N ∑i=1N r(yi) = nvalidN , yi∼pT(·∣x)   (offline rollouts; ratio = 1, unbiased; proposal-independent → valid all run)

Unseeded vs seeded vs frozen Z — one formula, three settings of n0. Unseeded (n0 = 0, the original setup and the red run in the Mode-1 panel): Z(x) is built from online visits only — a prompt's first visit has no history (K=0; the code falls back to the current batch's own Ẑ as both denominator and baseline, a within-batch centering), and its second visit runs on K=1, a single heavy-tailed sample — the Mode-1 trigger. Seeded: the offline estimate initializes the running mean as n0 = N⁄M = 10 pseudo-visits (its information content in batches), and online estimates keep folding in — the prior dominates early and decays as real visits accumulate. Frozen: Z(x) = Ẑoff(x) permanently, never updated — legitimate precisely because Z(x) is proposal-independent. Seeded and frozen both eliminate the K=1 regime and behave equivalently in practice; frozen is the simpler/lower-variance choice.

Seeded running mean Z(x)= n0·Z^off(x) + ∑k=1K Z^k(x) n0+K (K = online visits so far; unseeded: n₀ = 0, frozen: n₀ → ∞)
π ∝ pT·r — target posterior pT — prior (base LM) at temp T=0.6 r(y) ∈ {0,1} — potential (code runs) qφ — proposal (LoRA), sampled at T y(m) — particle m of M=10 x — prompt; K — its past visits Ẑk — visit-k batch estimate of Z(x) wm > 0 reinforce · < 0 push away

Clipping (common to every run shown) — three layers, always on: the raw importance weight is clamped, uw ← min(uw, 1.0) (truncated IS, applied both in Ẑk and in the numerator); the centered weight is capped, wm ← clip(wm, ±0.5); and the summed gradient is norm-clipped to 1. Post-fix the uw clamp barely binds (valid particles sit at uw ≈ 1). Note the failure modes below crater through all three layers — they are sign/direction pathologies, which magnitude clipping cannot correct; only the structural fixes do.

A positive weight reinforces sample y(m); a negative one pushes the model away from it. Each failure mode corrupts a different part of this expression:

Mode 1 · denominator degradation

K=1⇒ Z(x)= Z^1 ⇒ |wm|∝ 1Z(x)  (one heavy-tailed sample)

Under exclude-current, a prompt's running Z(x) is the mean of its past visits' Ẑk. From epoch 2, every prompt sits at K=1: one unlucky low Ẑ and 1⁄Z explodes. Scale-dependent — needs long epochs (fires on the full split at iter 591; the 10-prompt probe refreshes K too fast).

Fix: seed or freeze Z(x) from offline rollouts (Ẑoff = nvalid⁄100; unbiased — Z is proposal-independent).

Mode 2 · sign-flip / push-away

resampling  ⇒ uw(m)≈ ∏t s¯t ≪Z(x) ⇒ wm<0 ∀m

SMC resampling resets each particle's weight to the population mean, so the final uw carries the path-survival product (~0.01), not r·pT/qT. With b = Z ≈ 0.6 pinned above it, every weight is negative — the update pushes q away from all of its own samples, valid ones included. Loss is negative from iteration 1.

Fix: train with plain importance sampling (ess = 0, no resampling) + tempered prior, so uw = r ≈ 1 > Z at init. Resampling stays for inference/eval.

Mode 3 · entropy collapse of the estimator

∑m w~m ∇φlog⁡ qφ (y(m)) , y(m)∼qφ ⇒ blind where q ≈ 0

The objective (inclusive KL) is mean-seeking — its minimizer is q = π, entropy included; it cannot prefer a collapsed q. What collapses is the finite-M estimator: samples come only from q, so the mass q stops covering — where forward KL's corrective signal lives — is never observed, and self-normalization hides the loss. Support shrinkage is a one-way ratchet, the negative weights on failed particles push mass onto the modes q already samples, and gradient noise drifts it into the absorbing near-δ state (all particles identical → degenerate).

Guard used here: the anchor β ∇φKL(qφ ‖ p), β = 0.1 — bounds the support ratchet (pragmatic, biases the objective). Principled fixes target the estimator: defensive sampling (a fraction of particles from pT, keeping abandoned regions observable) or MSC/CSMC — a consistent inclusive-KL gradient, which ran single-prompt with no collapse and no anchor.

Working recipe (all three fixes): seed-or-freeze Z(x) + ess=0 + tempered weights + KL anchor. Reference run: 7,538 iterations, no collapse, grad-norm flat ~50; held-out WACC 0.178 → ~0.23.

Training trajectories

Mode 1 — denominator degradation at the epoch boundary

full split · 591 prompts

Dotted line = iter 591 (epoch 2 = every prompt's second visit, K=1). The unseeded run breaks its climb and runs with ~3× the gradient noise from there on; the seeded run sails through. Contained (not catastrophic) because the other two fixes are on.

Mode 2 — sign-flip / push-away under resampling

probe · 10 prompts

Red validity starts near 1.0 only because it counts post-resampling survivors (inflated); the model beneath is being pushed away from every sample — no climb phase, monotone erosion, dead by iter ~180. Switch the metric to Loss: negative from iteration 1 (the signature).

Mode 3 — entropy collapse of the estimator

probe · 10 prompts

Climb-then-crater: concentrating on covered good sequences first helps (validity 0.1 → 0.85 by ~iter 100) — but the estimator never sees the mass q stops covering, so the support ratchet runs one way, into a near-deterministic degenerate proposal (0 by ~iter 160) with the gradient norm running away. The anchored reference stays flat indefinitely. (The objective itself is mean-seeking — this is estimator failure, not objective preference; see the Mode-3 card above.)

Stable training — all three fixes on

Same controls apply. Seeded vs frozen Z(x) — the two variants of the stable recipe (both: ess=0, tempered, KL anchor 0.1) — track each other at both scales; no collapse anywhere.

10-prompt runs, full length

probe · 10 prompts

Full-split runs (in progress)

full split · 591 prompts

DS-1000 results — held-out quality (weighted correctness)

WACC = weight-averaged test-passing rate — the metric we report (the training potential is only “runs without error”). Held-out 12 prompts from the trained libraries; ±≈0.03–0.08 noise, read directionally.

10-prompt arms

probe · 10 prompts

Blue vs green = the same reference run (all fixes) under two eval regimes: training helps under both, the SMC-vs-plain gap is small, and the plain-sampling eval gains more — the fine-tuned proposal needs less of SMC's help. Magenta = a different training run: the ess=0.9 ablation (resampling ON during training, i.e. corrupted weights, held stable only by the KL anchor), evaluated with SMC — it reaches similar WACC here, the honest null: at this scale, anchored training survives resampling's weight corruption without measurable quality loss.

Offline SFT (tempered) — WACC vs particle budget

full 254 eval prompts

The efficiency curve on the complete eval set (R=3, clean weights): single-sample WACC jumps 0.184 → 0.278 after SFT (+51% relative), and SFT at M=1 nearly matches the base model at M=5 — the fine-tuned proposal replaces most of a 5× particle budget. At large M the two converge: SMC with enough particles saturates what the base already knows. All 254 prompts, complete grid; the final checkpoint (iter 10400) is indistinguishable from 7500 — the SFT is converged.

Full-split TRAINING (591 prompts) — quick-eval on 18 held-out prompts

eval set: 18 held-out (3 × 6 libraries) · M=10 — the cheap monitoring set; full-254 results in the panel below

Plain-sampling eval (ess=0) — the proposal-quality metric. Both stable arms improve (validity +30% rel., pass@1 +0.05–0.06 behind these numbers); the SMC-eval (ess=0.9) curve is flat at the posterior value as expected (shown dashed where complete). Early: iter 2000 ≈ 3.4 epochs of 591 prompts — the 10-prompt probes needed ~100 epochs to peak. Frozen ≈ seeded, within 18-prompt noise. Red dash: the skyline on these 18 prompts — Eσ[correct] = 0.244 [0.148, 0.347].

FULL EVAL SET (254 prompts) — WACC and pass@K

full 254 · plain IS · M=10 · R=5 (50 iid draws/prompt)

The main-table numbers. Left/top: WACC (macro mean, prompt-bootstrap CIs) against the skyline — the posterior ceiling Eσ[correct] = 0.323 [0.284, 0.363] on these exact 254 prompts (macro; pooled ratio-of-sums variant: 0.377 [0.331, 0.422]; 59/254 prompts have zero correct-given-valid base samples). Base already sits at 0.302: the no_error target leaves ~2 points of total headroom, and both trained arms land inside the skyline CI — the target, not the optimizer, binds. Bottom: pass@K of the proposal itself (ess=0 particles are iid draws; unbiased estimator, identical prompts for all arms). pass@1 rises 0.192 → 0.266 (+39% rel.) at ~3.4 epochs, β=0.02 > β=0.1 at every K, The deep-k picture (two independently-seeded waves pooled, 150 draws/prompt, 222 common prompts — one shard refilling): EARLY checkpoints (@2000) dominate at small k (pass@1 +43% rel.) but converge to a statistical tie with base by k≈100–150. The PEAK checkpoint changes the story: β=0.1 @10000 dominates base at EVERY k — pass@50 0.715 vs 0.687, pass@100 0.766 vs 0.739, pass@150 0.793 vs 0.770 (+2.3 pts ≈ 5 prompts). Contrast with RLVR (Yue et al. 2504.13837, significant cross-under at large k on code): at its peak, stabilized inclusive-KL training shows coverage GAIN through the deepest k measured — concentration and coverage, not a trade. NEW: the CORRECTNESS-potential arm peaks at WACC 0.349 / pass@1 0.323 at iter 3000 — the FIRST arm above the no_error skyline, and the best pass@1 measured (+68% rel. over base) — then declines (0.319/0.247 @7000) as the training-batch solve rate collapses; see the per-epoch chart below. Recipe: identical to the β=0.02 no_error arm — KL anchor β=0.02 to the base model, ess=0, tempered, frozen Z — plus the terminal reward (real DS-1000 test execution lands multiplicatively on the weights, so the target is p·φ_noerr·correct) and Z re-measured for THIS target (frozen at P(valid AND correct), mean 0.195 — freezing the no_error rates would miscalibrate the baseline b(x)=Z(x) by ~2.5×). The anchor matters twice here: correctness is a much sparser reward (Ẑ≈0.2 vs ~0.5), and sparser targets have higher stability thresholds (the LCB lesson) — β=0.02 holds. Its own skyline is 1.0, not 0.323: the richer target raises the ceiling, and the probe-scale degradation was the 10-prompt memorization artifact. Trajectory: complete 254-prompt cells at iters 1000–7000 are plotted; @4000/@6000 cells still partial. The red dash on the pass@1 group marks the skyline: the pass@1 of a proposal that exactly matches the posterior; both trained arms are below it here (0.266 vs 0.323), i.e. concentration has not yet reached posterior sharpness at 3.4 epochs.

Full-split trajectories — the anchor trades speed for peak

full 254 · plain IS · one checkpoint per point

Both anchor strengths show the same rise–peak–decline shape; β sets the timing and the height. β=0.02 peaks at ~iter 2000 (pass@1 0.266) then recedes; β=0.1 needs 10,000 iterations to reach the same pass@1 but at a higher peak everywhere else — WACC 0.324 (touching the skyline 0.323) and pass@50 0.717 (+4.8 over base, a genuine support gain) — before receding by 19,500. Practical rule: weak anchor if compute-limited, strong anchor + checkpoint selection if not. Frozen-Z at iter 2000 posts the highest pass@1 of any measured cell (0.283, on 222/254 prompts — one shard pending) before declining by 3500 — same shape, possibly a slightly higher/earlier peak than seeded. Curves now show the full peak-bracketing resolution (β=0.02 every 500 iters around its peak; β=0.1 every 2000–3000 across its rise and decline).

Correctness potential at probe scale — richer target ≠ free lunch

probe · 10 prompts · held-out 12 · plain IS

Training against the CORRECTNESS potential (terminal test execution; skyline = 1 by construction) at 10-prompt scale briefly matches the no_error arm (0.300 @500) then degrades to the held-out asymptote (~0.24) and stays there — while the no_error arm (β=0.02) holds its 0.30 plateau to 12,500. Interpretation: with 10 prompts and a sparse reward (Ẑ≈0.2), the correctness signal teaches the solutions to those ten problems (memorization) instead of a transferable skill. The full-split correctness arm (591 prompts, running) is the decisive test — the memorization shortcut doesn't exist at that scale.

Below: the FULL-SPLIT correctness arm's training-batch solve rate (windowed mean of #particles passing valid∧correct per batch; dashed line = the frozen-Z prior rate 0.195, i.e. the base model's solve rate on the training prompts). Because the terminal reward measures correctness directly, this curve aggregates each FULL EPOCH (591 iterations = every training prompt exactly once), so points are directly comparable — no prompt-composition noise. The full β=0.02 arc is now a rise–collapse–RECOVERY: 0.161 → 0.188 (epoch 7, iters ~3500–4100, matching the held-out peak WACC 0.349 @3000), then a full collapse to 0.000 for four consecutive epochs (10–13, iters ~5300–7700 — the held-out decline to 0.319 @7000 is this collapse seen from outside), then a spontaneous recovery back to 0.169 by epoch 17. The recovery is the anchor doing its job: β=0.02 keeps the collapsed proposal within reach of the prior, so coverage regrows — unanchored arms never come back. The β=0.1 twin runs lower and slower, had a shallow dip at epochs 3–4, and is climbing steadily: 0.079 → 0.105 at epoch 10, still rising. (A prior-rate reference line is omitted for now: the rollout-measured prior (0.195) used 400-token generations vs the run\u2019s 200, so it is not directly comparable — a truncation-matched prior can be added later.)

Anchor-free training — the four-arm grid on the fast IS runner

full split · 591 prompts · NEW runner (~13s/iter)

All ess=0 runs are plain importance sampling, so a new runner replaces the genlm SMC E-step with one direct vLLM generate + two scoring forwards (uw = φ·pT/qT, adapter on/off); the IGUANA weight math is the same shared code (~13s/iter vs ~35s; an epoch ≈ 2.1h). On it, three arms differing ONLY in the stabilization mechanism: b002-IS — the production anchored recipe verbatim (uw-clip 1.0, w-cap ±0.5, grad-clip, KL anchor β=0.02); REL-CLIP — IGUANA with Beta-shrinkage Ẑ(x) (μ₀=0.5965 = rollout grand mean; n₀=100, m₀=50) + RELATIVE use-time clip uw ≤ 10·Ẑ(x) (unclipped uw feed the running Ẑ; bounds the centered weights to [−0.1, 0.9] by construction), NO absolute clip, NO w-cap, NO grad clip, NO ANCHOR; MSC — Markovian Score Climbing (CIS kernel): weights SELECT one training sample per prompt, never scale a gradient — no normalizer, no baseline, no clips, NO ANCHOR. Below: per-EPOCH training validity (591 iters = each prompt once). Both anchor-free arms climb monotonically through 9–10 epochs — an order of magnitude past every unanchored collapse we measured (probe @~160, LCB @~600) — and MSC runs at or above the anchored control.

Held-out (254 prompts, plain-IS eval backend; its base cell reproduces the genlm backend within noise: wacc 0.322 vs 0.302, pass@k within ~1.6pt). MSC's trajectory so far: pass@1 rises to 0.280 @3000 (+44% rel. over the same-backend base 0.195) with WACC pinned at the no_error ceiling (~0.31) — anchor-free concentration gains matching the anchored recipe. The honest counterpoint: pass@50 drifts DOWN 0.685 → 0.665 → 0.646 → 0.634 (@0→1000→2000→3000) — no collapse, but a steady coverage tax of ~1.5pt per 1000 iters at k=50 that the anchored arm did not pay (its @2000 pass@50 stayed at base level). All REL-CLIP cells (@250–@9000) have landed and are in the table below.

Validity rate (held-out)

pass@1 of q directly (an M=1 importance sample)

pass@1 of IS at M=10, resample after IS (= wacc)

pass@50 — oracle bound (coverage)

cellwacc
(IS)
wacc
(resampled)
pass@1pass@5pass@10pass@50SIR p@5validity
base (IS backend)0.3220.3230.1950.4370.5330.6850.5680.493
MSC @2500.3060.3090.2450.4630.5380.6500.5250.644
MSC @5000.3070.3080.2570.4460.5060.6180.4860.660
MSC @10000.3110.3100.2710.4760.5440.6650.5130.685
MSC @20000.3110.3140.2680.4600.5280.6460.5280.692
MSC @30000.3100.3090.2800.4570.5170.6340.5130.726
rel clip @2500.3000.3030.2060.4380.5300.6650.5310.540
rel clip @5000.3060.3070.2210.4580.5450.6810.5390.571
rel clip @10000.3020.3020.2250.4550.5360.6650.5390.591
rel clip @20000.3180.3160.2450.4690.5460.6930.5450.616
rel clip @30000.3270.3310.2540.4850.5670.7010.5600.627
rel clip @60000.3280.3280.2750.4940.5610.6650.5440.692
rel clip @90000.3300.3310.2840.4890.5530.6540.5260.736
MSC @60000.3190.3180.3060.4530.5040.6060.4840.767
MSC @120000.3340.3340.3320.4440.4840.5670.4650.789
b002 anchored @30000.3490.3450.2990.5100.5740.6730.5620.714
b002 anchored @80000.3350.3350.2980.5160.5840.7050.5600.754
no-weight-clip @2500.299—0.2210.4510.5360.673—0.564
no-weight-clip @5000.310—0.2310.4600.5440.673—0.593
no-weight-clip @10000.306—0.2360.4590.5380.677—0.601
no-weight-clip @1770+COLLAPSED at epoch ~3 (fp16 overflow → NaN adapter, no ℓ-clamp in this run); validity → 0, never recovered — see training chart and verdict below. Resampled-wacc / SIR p@5 not computed for these cells (—).

Long-horizon update (@6000–@12000) — every arm concentrates, every arm pays coverage, and the ablation verdict is in. MSC keeps climbing to pass@1 0.332 @12000 (+70% rel.) with wacc finally above the skyline (0.334) — but its oracle coverage keeps eroding (pass@50 0.685 base → 0.567), the steepest coverage tax of any surviving arm. The rel-clip arm's early coverage advantage was a PHASE: pass@50 peaked at 0.701 @3000 and drifts down (0.654 @9000) while wacc/pass@1 keep inching up (0.330/0.284). The SURPRISE is the anchored control on the same runner: wacc 0.349 @3000 — the best posterior-side cell of the campaign — with pass@50 0.705 @8000 ABOVE base; the anchor buys the best concentration-coverage trade after all. And the bounding-location ablation has its verdict: gradient clipping is NOT a substitute for weight clipping. The no-weight-clip arm (identical shrinkage Ẑ, no anchor, gradient-norm clip 1.0 as the only safety net) collapsed catastrophically at epoch ~3 — validity 0.626 → 0.319 → 0.000 as pre-clip gradient norms tripled (111 → 322) — and stayed dead for 21 epochs (red curve above; its @250–@1000 held-out cells, taken pre-collapse, were healthy). Norm-clipping bounds the update magnitude but preserves the pathological direction — exactly the collapse-theory prediction, and the opposite of the 1.5B pilot's experience behind alg:iguana's no-cap recommendation: the trade appears to reverse with scale. The restart sanity guard caught the collapsed model at every requeue ('!!!'-degenerate generations) and refused to continue — zero garbage checkpoints.

Mixing & diversity diagnostics — testing the "sticky chain" hypothesis

from training logs · no new runs

Hypothesis (sticky-chain): MSC's high validity but weaker proposal could come from poorly-mixing CIS chains — "stuck on one valid example per prompt for a long time" — while IGUANA sees more diverse samples. Two diagnostics from the logged training data. (1) The chain is NOT sticky: the retained reference survives a visit (same y reused for the update) only 14–20% of the time — barely above the ~12.5% a well-mixed near-uniform selection among ~8 valid candidates would give; >80% of visits a fresh sample replaces it. (2) The diversity gap is real but lives in the PROPOSAL, not the chain: distinct valid samples generated per prompt per visit — MSC 2.77 vs rel clip 4.03 vs anchored control 4.16 (medians 2.39 / 4.11 / 4.24). MSC's plain-MLE update — no centered/negative weights ever pushing mass outward — concentrates q until each batch contains ~33% fewer distinct valid candidates; the same narrowing that shows up as its below-base pass@50. Consequence (the pink/green swap): the wacc−pass@1 gap, i.e. what the weighting layer adds at M=10, is 0.127 (base) → 0.073 (rel clip) → 0.030 (MSC): MSC yields the best M=1 POLICY (deploy without weights), rel clip the best PROPOSAL for IS (deploy with weights) — a real deployment-regime complementarity.

Per-prompt ESS of the training batches — and it runs OPPOSITE to health. Normalized ESS/M of the training-batch weights: MSC RISES 0.35 → 0.56 (a concentrated q yields near-duplicate samples with near-equal weights — even weights over ~2.8 distinct candidates), while rel clip FALLS 0.51 → 0.37 ("crummy weights" are real — the cost of maintaining broad coverage) even as its wacc and pass@50 improve. ESS alone is therefore misleading in this regime: it is highest exactly where candidate diversity has collapsed. The informative batch measure is closer to ESS × distinct-valid-per-visit (even-weighted variety).

armdistinct valid / visit (mean)median
MSC (no anchor)2.772.39
rel clip (no anchor)4.034.11
b002 anchored control4.164.24

Improvement probes — best held-out WACC per arm

probe · 10 prompts · held-out 12

Single-knob variants of the reference recipe, best checkpoint each (iters ≤ 2000, except the frozen-Z twin whose checkpoint cadence is 2000/4000/6000 — its best is iter 4000). Whiskers: 95% CI from the macro-mean prompt bootstrap over the 12 held-out prompts (base model: plain [0.093, 0.312], SMC [0.077, 0.296]). With only 12 prompts the intervals are wide and all arms overlap each other and the base model — this panel ranks the knobs; the full-split runs are the confirmatory eval. Dashed lines mark the base model (iteration 0) on the same held-out 12 (plain 0.195, SMC-eval 0.178 — every arm clears both) and the skyline: the posterior ceiling Eσ[correct] on these exact 12 prompts, 139/594 = 0.234 (pooled ratio-of-sums; bootstrap 95% CI [0.116, 0.346]; 4 of 12 prompts have zero correct-given-valid base samples). β=0.02's plain 0.304 sits ABOVE the skyline — legitimate at finite M: plain-eval WACC at M=10 is proposal-dependent, and a proposal with more correct mass than the posterior beats the asymptote. The no_error target is exhausted here. Weakening the anchor to β=0.02 is the biggest single win — its plain-sampling WACC (0.304) sits essentially at the no_error target's posterior ceiling (≈0.31 measured on the full 254), i.e. this target is nearly exhausted; further gains need the correctness potential. Warm-starting from the offline SFT (anchored to the init, not the base) gives the best SMC-eval number and composes offline + online training.

β=0.02 — per-prompt held-out improvement

best ckpt vs base · held-out 12 · mean of R=10, M=10

Where the gain actually lives. Red dashes: each prompt's own skyline — Eσ[correct] measured on its base rollouts (correct-given-valid, N=100). The four zero-skyline prompts (319, 388, 42, 799) stay at ≈0 after fine-tuning: training toward the posterior cannot manufacture correctness the posterior does not contain (799's +0.012 is the lone exception — the tuned proposal occasionally emits a correct program the base produced 0/100 times). All macro improvement comes from the 8 solvable prompts, and on 5 of them the tuned proposal ends above its own skyline in plain eval (909: 0.771 vs 0.667; 770: 0.624 vs 0.297) — legitimate at finite M, where WACC is proposal-dependent. Regressions are mild and mode-specific: 728 in plain (started far above its skyline), 875 in SMC eval.

Sample generations — what the models actually write

verbatim particles from training logs (per_particle.jsonl)

One representative particle per regime. The collapse row is not an illustration — it is the verbatim output of the unanchored run after the entropy ratchet completes.

model stateverdictsamplereading
base model (iter 50)invalid (KeyError at run time)
df = df.set_index('dt')
all_dates = pd.date_range(df.index.min(), df.index.max())
result = df.groupby('user').val.reindex(all_dates, fill_value=233)
result = result.to_frame(name='val').reset_index()
df = df.set_index('user')
result = pd.concat([df,result])
plausible-looking pandas that crashes — the typical base failure the no_error target trains away
IGUANA β=0.02 (iter 7200)valid + correct
result = [tf.compat.as_text(x_) for x_ in x]</code>
terse, well-formed, terminates properly — what a good proposal sample looks like
IGUANA β=0.02 (iter 7350)valid but wrong
result = (df
              .apply(pd.Series.value_counts)
              .unstack()
              .astype(float)
      )
clean, runnable, wrong semantics — the gap no_error cannot see (motivates the correctness potential)
NO ANCHOR, post-collapse (iter 950)degenerate
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
every particle, every prompt: the proposal collapsed to a delta on a single token — q→δ, literally

LiveCodeBench — the anchor A/B at full scale

Online IGUANA on LCB: the anchor delays but β=0.02 does not prevent collapse

full 500-prompt split · Qwen3-4B nothink · strict-public × noerror target · frozen measured Z

A controlled simultaneous A/B on the binding LCB target (strict public tests; prior rate Z̄=0.51): identical recipes, hardware, and data — the ONLY difference is the KL anchor. The unanchored arm collapses by iter ~300; the β=0.02 arm survives to ~600 and then collapses too, sitting degenerate through iter 2800+ with no recovery — support abandonment is absorbing in both. UPDATED CONCLUSION: the anchor delays collapse, but the required dose is TARGET-DEPENDENT — on DS-1000 (denser reward, 200-token sequences) the stability boundary is β ∈ (0.005, 0.01]; on LCB-strict (many Ẑ≈0.03 prompts, 1024-unit sequences → faster KL drift) it lies above 0.02. This matches the support-floor scaling (floor ∝ e−1/β against a domain-dependent leak rate). RESOLVED: the β=0.1 arm is STABLE AND LEARNING — validity climbs from the prior rate 0.51 to ~0.55 by iter 1000, through both predecessors’ death windows, with calm gradients. The LCB stability threshold sits in (0.02, 0.1]; held-out evals of its checkpoints are running. (Earlier LCB probes: bare arm died @~700, frozen-Z-only @~300.)

The normalizer-inflation collapse — mechanism, live data, and the cure

LCB · 3 arms · discovered & fixed 2026-08

Notation: everything here is PER PROMPT x. Z(x) = Ey∼p_T(·|x)[φ(x,y)] = P(valid | x) is that prompt's true normalizer (a probability, so Z(x) ≤ 1); Ẑ(x) is its running estimate, updated when prompt x is visited; b(x) is that prompt's baseline. The only global quantity is the shrinkage prior μ̂ (pooled mean across prompts), which cold-start prompts lean on — one channel by which a single prompt's poison can spread. Log lines and charts show the Ẑ(x) of the prompt visited at each iteration, window-averaged.

The problem in one sentence: the baseline b(x)=Ẑ(x) can climb above every honest weight, and from then on the gradient REPELS the model from everything it generates — including verifiably correct programs (see the exhibit below). Every anchor-free arm we sent at LiveCodeBench died — and the two newest deaths shared a single mechanism, a five-step FEEDBACK LOOP. LCB's 1024-token generations make the importance ratios p/q heavy-tailed (the log-ratio is a per-token sum, 5× longer than DS-1000), and the recipe folds RAW weights into the running normalizer statistics:

1 · rare monster weight
uw = φ·p/q spikes (heavy tail)
→ 2 · raw fold poisons Ẑ
Ẑ(x) inflates above 1 — impossible for the true Z
→ 3 · baseline flips every sign
b(x) = Ẑ(x) > every honest uw ⇒ ALL weights negative
✂ THE CURE CUTS HERE: b(x) ← min(Ẑ(x), 1)
→ 4 · uniform repulsion
q pushed away from its own VALID samples
→ 5 · q drifts from p
ratios get MORE extreme (uw → 1e-156) … back to 1: the loop closes, validity → 0

The cure — and why it is bias-free. Split the two roles the per-prompt estimate Ẑ(x) plays. The DENOMINATOR (a positive scale) keeps the honest raw-fold estimate: any error there only rescales one prompt's step (prop:cross_iter_unbiased). The BASELINE is capped at 1 — the mathematical ceiling of the true Z(x) for an indicator target — and by the score identity ANY baseline is mean-zero, so the cap introduces NO estimator bias. It just makes step 3 impossible, which severs the loop. The charts below are the live data: left — training validity: both uncapped arms crash (rel-clip @~650, grad-clip-only @~430: the mechanism does not care where the gradient bounding sits), the capped-baseline arm sails past 3,300 and climbs. middle — the baseline vs the ceiling: the dying arms' b crosses 1 and keeps rising; the cured arm's b pins at exactly 1. right — the denominator on a log scale: the cured arm absorbed the WORST inflation of all (Ẑ(x) → 300–400×, log₁₀ ≈ 2.5) and survived anyway — proof that inflation itself is harmless scale; it was the baseline that killed.

Worked example — real numbers from the run logs. Recall wi = (uwi − b(x)) / (M·Ẑ(x)) with M = 10. Four moments from the UNCAPPED rel-clip arm, then the same situations in the capped-baseline arm:

moment logged numbers per-particle weight w signal
healthy (iter ~130) Ẑ(x) = b(x) = 0.54; valid particle uw = 0.87, invalid uw = 0 valid: (0.87−0.54)/5.4 = +0.061
invalid: (0−0.54)/5.4 = −0.100
correct: attract valid, repel invalid
the poisoning fold (iter 183→184) one sample spikes to uw = 610; folded RAW into the running stats Ẑ(x) jumps 1.60 → 2.10 in one iteration estimate now 2× the mathematical ceiling (true Z(x) ≤ 1)
the sign flip (iter 604) 10/10 particles VALID; largest honest uw = 5.2·10⁻³¹; b(x) = Ẑ(x) = 1.89 every particle: (≈0−1.89)/18.9 = −0.100 = −1/M all ten correct solutions repelled at FULL garbage force — note (0−Ẑ(x))/(M·Ẑ(x)) = −1/M no matter how big Ẑ(x) gets
the spiral closes (iters 603–609) uw medians 10⁻⁴¹…10⁻⁸⁰ (were ~0.9 when healthy) as q drifts from p weights can never climb back over b = 1.9 permanent −1/M repulsion; validity → 0 by ~650
cure arm, same spike (iter 211) uw = 310 hits while Ẑ(x) = 0.61 — same poison as above denominator Ẑ(x) absorbs it (eventually → 331); baseline capped: b(x) = min(Ẑ(x), 1) step 3 impossible — the loop never closes
cure arm, deep in training (iter 3302) 10/10 valid; uw_max = 4.1; Ẑ(x) = 331 (!); b(x) = 1 best: (4.1−1)/3310 = +0.0009; worst possible: (0−1)/3310 = −0.0003 signs still correct; inflated Ẑ(x) in the DENOMINATOR only damps the step (harmless per-prompt scale) — in the baseline it flips signs
THE FIX  ·  b(x) ← min(Ẑ(x), 1)  —  cap the baseline at the mathematical ceiling of the true Z(x); keep the raw Ẑ(x) in the denominator.
Bias-free: by the score identity ANY measurable baseline is mean-zero, so the cap changes no expectation. It simply makes the sign flip (step 3) unreachable, severing the feedback loop. Note what it does and does not do: it PREVENTS the poisoned state — it cannot rescue a model already 200 nats adrift. Prevention is enough: the capped-baseline arm sailed past 3,600 iterations and was stopped by hand, still healthy, where every uncapped variant died by ~700.

The sign flip, observed on real outputs. To check whether the repelled samples were actually bad, we reloaded the dying arm's iter-600 checkpoint (four steps before the batch above), regenerated M = 10 samples on 3 training prompts at the training settings, and graded every sample on three ladders plus its log-ratio:

prompt ran w/o error (=φ) passes ALL public tests passes FULL private suite log p−log q (min/med/max)
1015/105/101/10−235 / −143 / −77
4920/100/100/10−426 / −293 / −107
594/100/100/10−315 / −167 / −119

The exhibit: a verifiably CORRECT solution being repelled. The 1/10 above is a complete, correct program — it passes the full hidden private suite — generated by the dying model itself (290 tokens):

```python
class Solution:
    def countCompleteSubstrings(self, word: str, k: int) -> int:
        def is_valid(s):
            if k == 0:
                return True
            freq = {}
            for c in s:
                freq[c] = freq.get(c, 0) + 1
            for count in freq.values():
                if count != k:
                    return False
            return True
        
        def check_adjacent(s):
            for i in range(len(s) - 1):
                if abs(ord(s[i]) - ord(s[i+1])) > 2:
                    return False
            return True
        
        result = 0
        
        # Split the word into parts where adjacent characters differ by more than 2)
        parts = []
        start = 0
        for i in range(1, len(word)):
            if abs(ord(word[i]) - ord(word[i-1])) > 2:
                parts.append(word[start:i])
                start = i
        
        parts.append(word[start:])
        
        for part in parts:
            n = len(part)
            # Check all possible substrings of the part")
            for i in range(n):
                for j in range(i+1, n+1):
                    substr = part[i:j]
                    if is_valid(substr) and check_adjacent(substr):
                        result += 1
        
        return result
```
this correct program a crashing sample
φ (runs on public inputs)10
log qT (drifted model, T=0.6)−6.9, i.e. Pq(program) ≈ 1/1000 — an ASTRONOMICALLY high probability for a 290-token sequence (97.6% per token): the proposal emits nearly this exact program once per thousand samples—
log pT (base model, T=0.6)−223.0, i.e. Pp(program) ≈ 10−97 — NORMAL for this length (≈46%/token compounds to 10−97 for ANY 290-token text p writes): p treats it as ordinary, plausible code—
log pT − log qTp/q ≈ 10−94 — the program is 1094× MORE probable under the proposal than under the base model. The gap is q's pathological overconfidence, not p's dislike (untempered gap −131 nats; ×1/T = 1.67 tempering ≈ −218 ✓)—
weight uw = φ·pT/qTe−216 ≈ 10−940
gradient coefficient w = (uw − b(x))/(M·Ẑ(x)), with poisoned b(x)=Ẑ(x)=1.9 −0.100 = −1/M−0.100 = −1/M

The update rule pushed the model away from a proven-correct program with EXACTLY the same force as from garbage — the discrimination between them was Δw ≈ 10−95. Rescoring p and q separately sharpens the picture — and corrects a tempting misreading ('the base model puts low probability on the correct program'): the −216 gap is almost entirely Q-DRIVEN — the repulsion loop had collapsed the drifted model to near-determinism (97.6% per-token confidence on its own output), while the base model finds the same program perfectly ordinary code. Once q's entropy collapses, EVERY weight ≈ pT(y) ~ e−0.77·len, exponentially small in length — so no sample of any quality can ever climb back above a baseline near 1: the sign flip becomes STRUCTURAL, independent of what the model writes. (The T=1.0 rescoring also measures the tempering amplification directly: raw gap −131 nats, ×1/T = 1.67 → −218 ≈ the −216 observed.) That is the precise sense in which the failure is estimator-level, not model-level: the weights told the truth (−216 nats of drift, honestly measured), the model could still produce correct code, and the poisoned baseline turned both facts into a sign-inverted, quality-blind gradient. Two honest caveats: (i) by iter 600 the indiscriminate repulsion had already degraded the model badly (only 9/30 samples even ran, one prompt produced nothing but 1024-token ramblings) — the claim is not that a healthy model was misjudged, but that the gradient was blind to quality while the model decayed under it; (ii) a free early-warning diagnostic falls out: the MEDIAN log-weight is a per-iteration readout of −KL(qT‖pT) — it had fallen to −100s while validity still looked fine (~100 iterations before death). Monitor it.

The asymmetry in one line: an inflated Ẑ(x) in the denominator makes the step smaller; the same Ẑ(x) in the baseline makes the step point the wrong way — and at (0−Ẑ(x))/(M·Ẑ(x)) = −1/M the repulsion is at full strength however large Ẑ(x) grows, so there is no self-damping on the dying path.

training validity (/10)

baseline b(x) vs the per-prompt ceiling

denominator log₁₀ Ẑ(x)

Held-out results — LCB cure arm (rel-clip + capped baseline, noerror φ)

214 eval prompts · M10 × R5 · correctness = FULL private suite

Survival ≠ learning. The capped-baseline arm is the only anchor-free variant to train stably on LCB (3,600 iterations; every uncapped variant died by ~700) — but its HELD-OUT numbers are flat against the base model: wacc +0.005 at best, pass@50 +0.010, and the held-out validity rate FALLS 0.810 → 0.756 while the training-prompt validity was climbing 4.7 → 6.6/10. The training gains were train-set-specific. One suspected contributor: the arm's own denominator inflation (Ẑ(x) → ~400) cut its effective per-prompt step size ~800× for much of the run — the mild form of the freeze pathology described above. Weights at eval use the same noerror φ as training; base@0 is evaluated by the identical pipeline.

cell wacc (IS)pass@1 pass@5pass@10 pass@50validity rate
base@00.4250.4180.5140.5550.6400.810
cure@5000.4200.4150.5050.5410.6310.798
cure@10000.4250.4200.5170.5610.6450.779
cure@20000.4300.4210.5200.5590.6450.770
cure@30000.4280.4190.5230.5650.6500.756

wacc (IS pass@1)

pass@1 (q directly)

pass@50 (oracle bound)

validity rate of q

Why the noerror ceiling was tiny — and where the real headroom is (strict φ)

measured on the 214 eval prompts, Qwen3-4B no-think T=0.6

Training q toward the target pT·φ/Z does not change the posterior — the posterior is fixed; training only amortizes it into the model. So the most held-out pass@1 can gain from PERFECT convergence is the gap between the posterior's correctness (the skyline) and the base model's own pass@1. Measuring that gap for both potentials explains the flat table above and identifies the real prize:

noerror φ (runs on public inputs) strict φ (passes ALL public tests)
base rate of satisfying φ0.81P(T) = 0.503
posterior correctness (skyline)≈ 0.425 (= base wacc)P(C|T) = 0.822
base pass@1 (what q must beat)0.418P(C) = 0.414
held-out ceiling for training q+0.007 — conditioning on a constraint the base model already satisfies (81%) buys nothing; the cure arm captured ≈+0.002 of it, i.e. the flat table IS the objective near its ceiling+0.408 — passing public tests DOUBLES correctness (0.414 → 0.822); amortizing that filter into q is the real experiment

Success criteria for the strict-φ arm, then: q's own pass@1 climbing 0.414 → 0.822 and its validity P(T) climbing 0.503 → 1. Its training telemetry below shows the current blocker — the DENOMINATOR FREEZE. Left: training validity sits parked at the prior P(T) ≈ 0.5 — no sign flip (the capped baseline holds), but no learning either. Middle: raw folds inflate the denominator Ẑ(x) four orders of magnitude past the true ceiling by iter ~1200. Right: gradient norms collapse 200 → ~0.05 in lockstep — an effective per-prompt learning rate of ~1/20,000. The arm with all the headroom is parked by a fixable estimator issue: bound the influence of each FOLD (e.g. fold min(uw, c·Ẑ(x)) into the statistics) — the resulting scale bias is harmless for the gradient direction, and it would close both raw-fold syndromes (baseline sign-flip AND denominator freeze) at their shared root.

Worked example — the FREEZE, with real numbers from the strict-φ arm's logs. Same estimator, same two roles as in the sign-flip panel above — but this time the baseline is capped, so watch what the OTHER role does. Recall wi = (uwi − b(x)) / (M·Ẑ(x)), M = 10:

moment logged numbers per-particle coefficient w signal
healthy (iter ~147) Ẑ(x) = b(x) = 0.55; a sample passing ALL public tests has uw ≈ 1.0; a failing one uw = 0 pass: (1.0−0.55)/5.5 = +0.082
fail: −0.100
correct signal, healthy magnitude; ‖g‖ ≈ 200
the poisoning ladder (iters 569–1052) raw folds swallow tail spikes: uw = 1.9·10⁴ (iter 569) → Ẑ crosses 1; uw = 1.1·10⁵ (580) → Ẑ 4.1; uw = 3.3·10⁸ (iter 1052) → Ẑ(x) → 2.5·10⁴ one sample sets the running mean for the rest of the run (zero-breakdown estimator; the ℓ-clamp permits up to e³⁰ ≈ 10¹³) baseline SAFE — the cap pins b(x) at 1, no sign flip — but the DENOMINATOR keeps the raw 2.5·10⁴
the frozen state (iter ~2697) 10/10 pass all public tests, uw ∈ [0.6, 1.6]; b(x) = min(Ẑ, 1) = 1; M·Ẑ(x) = 2.1·10⁵ best sample: (1.6−1)/(2.1·10⁵) = +2.9·10⁻⁶ — same SIGN as healthy, 28,000× smaller every step is correct in direction and negligible in size; ‖g‖ 200 → 0.05; validity parked at the prior 0.49 ≈ P(T) for 1,600+ iterations
why it never recovers — or dies Ẑ is a running (shrinkage) mean: diluting 2.5·10⁴ back to O(1) needs ~10⁴× more folds, and each prompt is visited once per ~500 iterations effective per-prompt learning rate ≈ lr / 20,000, indefinitely signs stay correct so nothing collapses; steps stay ≈ 0 so nothing learns — a PARKED arm, silent failure. The model itself stayed pristine (log-ratios still within ±2 at iter 3,684) — the freeze is even accidentally protective against drift

Side by side with the panel above: the SAME raw-fold poison expresses through Ẑ(x)'s two roles in opposite ways — in the baseline it flips every sign (loud death: validity → 0); in the denominator it divides every step by 10⁴ (silent death: validity flat at the prior forever). The baseline cap fixes only the first; the fold-clip (bounding each fold at 10·Ẑ_grad(x), raw pool kept for reporting) fixes both at the source — the telemetry charts below are this table's three columns drawn over the whole run.

training validity (parked at prior)

log₁₀ Ẑ(x) — denominator inflation

log₁₀ ‖g‖ — the freeze

Run inventory (DS-1000)

Shared config across all runs: Qwen2.5-Coder-3B, LoRA (r=8, all layers), ipi=1, M=10, temp 0.6, lr 1e-5, max_tokens 200 — and the three clipping layers: uw-clip 1.0 (truncated IS), weight cap wm ∈ ±0.5, gradient norm-clip 1.0. Rows differ only by the knob listed.

panelrun (red = mode active)scalediffers from fixed run byoutcome
Mode 1ftnoseedfull splitno Z-seeding (running Z, exclude-current)boundary break @591, degraded plateau
Mode 1 fixftfixfull split—stable through boundary, gnorm ~29
Mode 2mode210 promptsresampling on (ess=0.9), no anchor, frozen Znegative loss from iter 1, dead @~180
Mode 3zfrozen_nokl10 promptsno KL anchorclimb 0.85 → crater @~160
Modes 2–3 fixreference10 prompts—7,538 iters flat, +0.05 held-out WACC

Training status — all arms stopped (2026-08-04): analysis & eval phase

Every full-scale training is now concluded or deliberately paused; the GPU allocation goes entirely to draining the eval backlog. All checkpoints and resume bundles are intact — any "paused" arm can be resumed. A new plain-IS fast runner (same command line, --estep is) replaces the genlm SMC E-step with one direct vLLM generate + two scoring forwards (uw = r·p_T/q_T on the training model, adapter on/off): since ess=0 runs never resample, the SMC machinery was pure overhead. The IGUANA weight math is the same shared code, so resumed/re-run arms stay comparable; expected ~3–4× per iteration (smoke run validating).

armtarget · βfinal iterstatewhy stopped
ftb002no_error · 0.023,700retiredpeak @~1500–2000 mapped, declining after
ftzfb002no_error · 0.02, frozen-Z3,500retiredfrozen ≈ seeded confirmed; best pass@1 @2000 then decline
ftb01no_error · 0.119,500completefull rise–peak–decline mapped; peak @10000 touches the skyline
ftb001 / ftb0005no_error · 0.01 / 0.005—retireddose-response probes: stability boundary β ∈ (0.005, 0.01]
ftcorcorrectness · 0.0210,164complete (ALLDONE)full rise–collapse–recovery arc captured (per-epoch chart above)
ftcor01correctness · 0.15,934pausedtraining solve rate still climbing @epoch 10 — resumable (on the fast runner) if the pending @2000/@4000 evals warrant
lcb offlineLCB both-filter BC9,609complete3 epochs over 25,624 rejection-sampled rollouts
lcb noanchor / kl002LCB strict · 0 / 0.023,325 / 2,825completeboth collapsed (@~600) — the LCB threshold sits ABOVE β=0.02
lcb kl01LCB strict · 0.15,365stoppedstable & learning (validity 0.51→0.56 @2000); trajectory sufficient for the ladder, checkpointed through 5365
2026-08-05 — new wave on the fast IS runner (see the three-arm panel above):
msc-isno_error · no anchor12,469stoppedMSC/CIS: 21 epochs anchor-free, validity 0.89 and climbing; held-out pass@1 0.332 @12000, coverage tax to 0.567
relclip-isno_error · no anchor9,041stoppedshrinkage Ẑ + relative clip, no grad clip: 15 epochs stable; wacc 0.330 @9000; coverage peaked @3000
b002-isno_error · 0.028,483stoppedanchored control on the fast runner — best posterior cell of the campaign (wacc 0.349 @3000; pass@50 0.705 @8000)
nowc-isno_error · no anchor, no weight clip14,700COLLAPSED @ep3bounding-location ablation: grad-clip-only died at epoch 3 (validity → 0, never recovered); ABORTED by the restart sanity guard