# SP4 — Signal-Driven Magnitude Control Design (revised)

**Date:** 2026-04-30 (revised same-day after critical self-review)
**Branch:** `plan-c-phase-2-thompson` (post-SP3 close-out)
**Predecessor:** SP3 close-out v2 (Mech 9 across 5 Adam kernels, commits `0d7e27d61` + `55576d047`) + Mech 10 (h_s2 clamp)
**Status:** Design — awaiting user spec review before implementation plan.

## Goal

Replace every hardcoded magnitude multiplier in the SP3 mechanism stack and pre-SP3 mechs in the same magnitude-control surface with **signal-driven ISV bounds**. The bound itself lives in an ISV slot computed by a producer kernel from observed signal statistics. Consumers read the slot and clamp directly — no multiplier between ISV read and clamp.

This closes the principle violation in `feedback_isv_for_adaptive_bounds.md` and `feedback_adaptive_not_tuned.md` that accumulated across Mechs 1, 2, 5, 6, 9, 10 — and that drove the smoke-test-jxckl F0=21.09 plateau when Mech 10's hardcoded `100×` multiplier bound F0's heavy-tail h_s2 elements during legitimate training.

This revision (post-self-review) addresses 8 critical issues from the first draft: algorithm parallelization, bootstrap-value derivation errors, EMA convergence in smoke time-budgets, regularization-knob scope creep, F0 acceptance criterion, per-param-group plumbing, the producer-vs-clamp feedback loop, and missing unit-test strategy.

## Scope

**In scope:** every hardcoded magnitude multiplier and every hardcoded ε floor that controls a clamp or diagnostic threshold in the magnitude-bound surface:

- **Mech 1** — target_q clamp (`10 × Q_ABS_REF.max(1.0)`)
- **Mech 2** — atom positions, 3 sites: `atoms_update_kernel`, `iql_value_kernel`, `experience_kernels` (`10 × Q_ABS_REF.max(1.0)`)
- **Mech 5** — diagnostic thresholds slots 36-49 (Adam m at 100×, Adam v at 1e6× squared, weights at 1e3×, target_q post-clip at 95×, atom span at 190×, IQN weight at 1e3×, h_s2 max at 1e3×)
- **Mech 6** — adaptive grad-clip upper bound (`100 × Q_ABS_REF.max(1.0) × prev_slow_ema`); `CLIP_MULTIPLIER` and `MIN_CLIP=1.0` constants subsumed by SP4's `GRAD_CLIP_BOUND` slot.
- **Mech 9** — post-Adam weight clamp across 5 Adam kernels (`100 × Q_ABS_REF.max(1.0)`)
- **Mech 10** — h_s2 activation clamp (`100 × H_S2_RMS_EMA.max(1.0)`)
- **All `.max(1.0)` ε floors** (the SP1 pearl) — replaced by per-slot bootstrap values derived from theoretical-init statistics (Xavier max-element formula, etc.).

**In scope (regularization knobs — added via novel pearls):**

- **Weight decay (`AdamW wd × p`)** — derived from gradient-pressure-projection per param-group (see "Weight decay producer signal" below).
- **L1 lambda (`l1_lambda` in `dqn_adam_update_kernel`)** — derived from gradient-direction entropy deficit per param-group (see "L1 lambda producer signal" below). This is a novel pearl extending `pearl_adaptive_moe_lambda.md` — same template (regularization strength = EMA-tracked deficit of what's being regularized against), applied to feature redundancy.

**In scope (resolved via Pearl D — Wiener-optimal adaptive α):**

- **EMA rates (α values in producer kernels)** — derived per-step from observed signal variance via Wiener-filter optimal smoothing (see "Pearl D: Wiener-optimal adaptive α" below). The recursion problem dissolves via a single uniform structural meta-α (= 1/N_step_history). No per-signal tuning constants. This applies to the 40 new SP4 ISV slots (15 producer kernels via Pearl B fusion) AND retrofits to the ~7 existing pre-SP4 ISV producers (h_s2_rms_ema, grad_norm_slow_ema, aux_heads_loss_ema, moe_gate_entropy_ema, vsn_mask_ema, iqn_quantile_ema, reward_component_ema) — single coordinated migration per `feedback_no_partial_refactor`. Note: `grad_norm_slow_ema` is retired entirely in Layer C since its sole consumer (Mech 6's `update_adaptive_clip` upper-bound) migrates to ISV[GRAD_CLIP_BOUND].

**Out of scope (categories D + E from the brainstorm):** mathematically derived theoretical constants (Adam β₁=0.9, β₂=0.999; Adam ε=1e-8 numerical denominator; Xavier init formula `√(2/K)`; attention 1/√d), and architectural shape constants (hidden_dim=256, num_atoms=51, num_quantiles=5, layer count, batch size).

## Architecture

### The universal contract

```
Producer kernel (per training step, runs BEFORE any consumer reads):
    step_p99 = histogram_p99(|signal|)
    isv[BOUND_X] = (1 - α_X) · isv[BOUND_X] + α_X · step_p99

Consumer (clamp or diagnostic):
    bound = isv[BOUND_X].max(ε_X)        // ε_X = theoretical-init bootstrap, fold-reset
    out = clamp(v, ±bound)               // or diagnostic-fire if |v| > bound
```

**Why p99 over max-abs:** the EMA of `max(|v|)` is outlier-driven by construction — a single bad sample inflates the EMA and the bound chases the inflation. p99 is a robust statistic: a single outlier (in the top 1%) does not move the 99th percentile. Clamping at p99 means clipping the worst 1% of magnitudes to the top-of-typical, which is a precise statistical statement.

**Algorithm: dynamic-range linear histogram (NOT P², NOT log-spaced).**

The first-draft spec proposed P² (Jain-Chlamtac), but P² is a sequential algorithm and does not parallelize cleanly across GPU threads. An earlier revision proposed log-spaced bins from 0 to step_max, but `log(0) = -∞` creates a singularity. This revision uses **linear-spaced** bins, which avoid the log-singularity and give the right resolution where p99 lives (top of distribution):

```
Pass 1 (block-wide max-reduce):
    step_max = max(|v|) over the buffer
    if step_max == 0: ISV[BOUND] is left unchanged this step (degenerate signal — skip update)

Pass 2 (block-wide bin):
    each thread reads strided slice
    bin_idx = floor(|v| / step_max × 256)   (clamped to [0, 255])
    bins are LINEAR-spaced over [0, step_max], 256 bins of width step_max/256
    increments use per-thread register counter → block-shared tree-reduce
    (no atomicAdd; per `feedback_no_atomicadd`)

Pass 3 (block-wide cumulative-from-top):
    thread 0 walks bins from index 255 down to 0, accumulates count
    when cumulative ≥ N/100, stop — that bin's upper edge is p99
```

Linear bins are the right choice for p99 because we care about resolution AT the top of the distribution. With 256 linear bins from 0 to step_max, the top bin width is `step_max/256 ≈ 0.4%` of step_max — sufficient for 1% quantile estimation with safety margin.

All three passes use `__syncthreads()` between them, no scratch buffer beyond shared memory (256 ints + 1 float). Single-block, 256-thread, deterministic, no atomicAdd. Cost: ~5-10 μs per launch on L40S for buffers up to 64k elements.

The 256-bin choice is **derived from the resolution required for a 1% quantile** (need ≥100 bins for 1% precision; 256 gives ~0.4% precision with safety margin). This is a numerical-precision choice, not a tuning knob — same theoretical-constant category as floating-point precision.

The "if step_max == 0 skip update" branch handles the degenerate case where the signal is identically zero (e.g., grads after a NaN-sanitization). The producer leaves the ISV slot unchanged, so the consumer reads the previous bound. No NaN propagation through the histogram math.

### Pre-clamp vs post-clamp producer reads (the feedback-loop fix)

The first-draft spec hand-waved producer placement. This revision is explicit:

**Buffer-based clamps (h_s2, target_q, atom_positions): producer runs BEFORE consumer.**

The captured forward-graph order becomes:
1. Trunk forward writes `save_h_s2`
2. `h_s2_p99_producer` reads `save_h_s2`, updates ISV[H_S2_BOUND]
3. Mech 10 clamp reads ISV[H_S2_BOUND], clamps `save_h_s2` in place
4. Downstream consumers see clamped `save_h_s2`

Producer sees PRE-clamp distribution → no feedback loop.

Same pattern for target_q (Mech 1) and atom_positions (Mech 2).

**In-kernel clamps (weights via Mech 9, Adam m/v in dq/iqn/iql/attn/curiosity Adam kernels): producer reads POST-clamp, feedback loop ACCEPTED with documented behavior.**

These clamps live inside Adam kernels (the clamp is the last step before `params[idx] = p_val`). Splitting Adam into "compute new value" + "producer reads" + "clamp + writeback" requires two kernel launches per Adam call (per-step latency cost) and 5× the plumbing (one split per Adam kernel).

The pragmatic choice: producer reads post-clamp `params` after Adam runs. Feedback loop dynamics:
- If post-clamp p99 < pre-clamp p99 (clamp engaged on outliers): EMA stays at clamp value or below
- Producer never sees pre-clamp tail growth → bound never grows → outliers always clamped
- During stable training: post-clamp p99 ≈ pre-clamp p99 (clamp rarely engages) → EMA tracks natural distribution
- During regime shift: real p99 grows; clamp lags one step (uses previous EMA); next step's producer sees post-clamp distribution which now includes some clamped elements (= bound value); EMA gradually shifts toward bound; bound stays anchored at the regime-shift transient

**Net effect:** the feedback loop creates a "soft anchor" that prevents runaway growth but allows gradual upward tracking when the network's natural p99 grows. The Mech 8 anchor-staleness pathology does NOT recur because (a) the anchor is per-step EMA not multi-fold lag, and (b) fold-boundary reset to bootstrap re-anchors per fold.

This is documented as a design choice, not a hidden artifact.

### Diagnostic = clamp engagement (not separate threshold)

Existing Mech 5 diagnostic slots (36-49) currently fire at `1e3 × Q_ABS_REF` etc. — a separate "headroom" threshold above the clamp. Under SP4, **the diagnostic threshold = the clamp bound itself**. Slot N fires when the clamp engaged on this step (any element ≥ bound this step).

**Implementation differs by clamp location:**

**Buffer-based clamps (Mech 1, 2, 10) — diagnostic in producer:**
The producer kernel reads PRE-clamp buffer for histogram-p99. Pass 1 computes `step_max = max(|v|)` over the buffer. Producer compares `step_max` to its current `isv[BOUND_X]` (the bound the consumer is about to use). If `step_max > isv[BOUND_X]`, the producer writes `nan_flags_buf[diag_slot] = 1`. Sticky flag — captures "the clamp WILL engage this step on at least one element". Cost: one comparison in Pass 1 (~100 ns).

**In-kernel clamps (Mech 6, 9 — weights and Adam m/v) — diagnostic INSIDE the kernel + Pearl C self-correction:**
The producer for these signals reads POST-clamp distribution (since the kernel mutates in-place and there's no separate pre-clamp buffer). Post-clamp `|v| ≤ bound` by construction, so a producer-side magnitude comparison NEVER fires.

The diagnostic must therefore live inside the Adam (or weight-update) kernel itself, immediately at the clamp step. For each Adam kernel, the engagement count uses the existing block-wide reduction pattern from `dqn_grad_norm_kernel` (per-thread register counter → block-shared tree-reduce → single block-leader writes the per-block count to a per-(block, group) output array).

```cuda
/* Mech 9 clamp + SP4 diagnostic + Pearl C engagement counter */
__shared__ int s_block_engage[BLOCK_REDUCE_WIDTH];   // shared-mem reduction scratch
int local_engage = 0;                                // per-thread register counter

// ... existing Adam compute → p_val ready for clamp/writeback ...
if (weight_clamp_max_abs > 0.0f) {
    if (fabsf(p_val) > weight_clamp_max_abs) {
        nan_flags_buf[diag_slot] = 1;            // sticky flag — race-free idempotent write
        local_engage = 1;                        // per-thread register, no contention
    }
    p_val = fminf(fmaxf(p_val, -weight_clamp_max_abs), weight_clamp_max_abs);
}

/* Block-wide tree-reduce (no atomicAdd, mirrors dqn_grad_norm_kernel pattern) */
s_block_engage[threadIdx.x] = local_engage;
__syncthreads();
for (int s = blockDim.x / 2; s > 0; s >>= 1) {
    if (threadIdx.x < s) s_block_engage[threadIdx.x] += s_block_engage[threadIdx.x + s];
    __syncthreads();
}
if (threadIdx.x == 0) {
    /* One write per block per Adam launch — block_idx selects the slot */
    clamp_engage_per_block_buf[engage_buf_offset + blockIdx.x] = s_block_engage[0];
}
```

The `nan_flags_buf` write is per-thread but idempotent (all threads writing 1 — race-free per existing convention at `dqn_utility_kernels.cu:1808`). The engagement count uses register-then-tree-reduce (no atomicAdd, consistent with `feedback_no_atomicadd`). Each block writes its local count to a separate slot of `clamp_engage_per_block_buf`, then the host sums across blocks (cheap reduction over `n_blocks` ≤ ~256 floats). Cost: two comparisons + one register update per affected thread + one block-wide reduction per Adam launch.

Each in-kernel-clamp Adam kernel takes a `diag_slot: i32` and `engage_buf_offset: i32` alongside `weight_clamp_max_abs: f32`. Launch site passes the appropriate offsets for that param-group. `clamp_engage_per_block_buf` is a mapped-pinned `[N_GROUPS × MAX_BLOCKS]` int buffer (≤ 8 × 256 = 2048 ints ≈ 8 KiB).

### Pearl C — engagement-rate self-correction (post-clamp feedback-loop fix)

**Theoretical baseline:** for a p99-tracked clamp, the natural engagement rate is exactly **1%** (top 1% by definition of p99). Sustained engagement above 1% means the bound is binding tighter than true p99 — the post-clamp feedback loop has saturated below the natural distribution.

**Producer extension (in-kernel-clamp signals only):**

```
After Adam kernel finishes, host reads `clamp_engage_buf[group]` total → divides by group's param count → engagement_rate[group] = fraction clamped this step.

Per-step rate-deficit signal:
    rate_deficit = engagement_rate - 0.01      // signed: + means rate > 1% (tight), - means rate < 1% (loose)

Producer compares rate_deficit to its EMA (using α* from Pearl D on this signal too). If sustained-positive (EMA > 0 for many steps with low diff_var → stable above 1%), the feedback loop has saturated. Action:
    isv[BOUND_X] = step_max          // force-bump bound to current observed maximum
    (one-time, this step only; producer resumes EMA from there)
```

**Why this works:**
- Buffer-based clamps (Mech 1, 2, 10): producer reads pre-clamp, naturally sees true p99, engagement rate stays at 1%, Pearl C never triggers. Correct (no feedback loop here).
- In-kernel clamps (Mech 6, 9): producer reads post-clamp. If feedback loop saturates EMA below true p99, engagement rate creeps above 1% (more than 1% of elements clamped each step because the bound is too tight). Pearl C's rate-deficit EMA detects this. Action: force-bump bound to step_max for one step → next step's producer sees the new (looser) bound, engagement rate drops back toward 1%, normal EMA resumes.
- The `0.01` constant comes from the *definition* of p99 (top 1% by definition) — it's a mathematical identity, not a tuning knob. Same category as `1/2` in mean (mathematically required, not chosen).

**Pearl C subsumes the "in-kernel feedback loop accepted" limitation in the first draft.** The loop is now self-correcting, not just documented.

**Pearl name candidate:** `pearl_engagement_rate_self_correction` — for any post-clamp / post-mutation EMA-based bound, track engagement rate against the theoretical p-quantile baseline; force-recalibrate when sustained deviation detected.

This drops the `× 10` and `× 1e3` headroom patterns entirely. All three diagnostic mechanisms (producer-side magnitude check for buffer clamps, in-kernel flag for weight/Adam clamps, Pearl C engagement-rate self-correction for feedback-loop sites) fire rarely under stable training and react automatically during regime shifts.

## Migration scope

### ISV slot allocation (40 new slots)

| Slot family | Count | Tracks | Consumed by |
|---|---|---|---|
| `TARGET_Q_BOUND` | 1 | p99(\|target_q\|) | Mech 1 clamp + slot 46 diagnostic |
| `ATOM_POS_BOUND[branch]` | 4 (per branch) | p99(\|atom_positions[branch]\|) | Mech 2 clamps × 3 sites + slot 47 diagnostic |
| `WEIGHT_BOUND[group]` | 8 (per param-group) | p99(\|params\|) per group | Mech 9 clamps × 5 Adam kernels + slots 44/45/48 diagnostics |
| `ADAM_M_BOUND[group]` | 8 (per param-group) | p99(\|adam_m\|) per group | slots 36-39 diagnostics |
| `ADAM_V_BOUND[group]` | 8 (per param-group) | p99(\|adam_v\|) per group | slots 40-43 diagnostics |
| `GRAD_CLIP_BOUND` | 1 | p99(grad_norm) | Mech 6 + adaptive_clip |
| `H_S2_BOUND` | 1 | p99(\|h_s2\|) | Mech 10 clamp + slot 49 diagnostic |
| `WD_RATE[group]` | 8 (per param-group) | EMA of \|w·g\|/\|\|w\|\|² per group | Adam kernel `weight_decay` arg |
| `L1_LAMBDA[group]` | 1 (only trunk has L1) | (mean(\|g\|)/mean(\|w\|)) × entropy_deficit | Adam kernel `l1_lambda` arg |
| **Total** | **40** | | |

### Param-group definitions (for the `[group]` slots)

**8 distinct param groups** corresponding to the live training-path Adam kernels' param buffers (DT excluded — offline-RL, outside SP4 scope). Each producer reads its group's `(buf_ptr, offset, length)` and computes p99 over that slice:

| Group index | Adam kernel | Param-buffer slice |
|---|---|---|
| 0 | `dqn_adam_update_kernel` | trunk slice (offset 0..end-of-trunk) |
| 1 | `dqn_adam_update_kernel` | value-head slice (within main params, contiguous) |
| 2 | `dqn_adam_update_kernel` | branch-heads slice (4 branches concatenated) |
| 3 | `iqn_adam_kernel` | IQN online_params (separate buffer entirely) |
| 4 | `iql_adam_kernel` (high-tau instance) | IQL value-head params (`gpu_iql.params`) |
| 5 | `iql_adam_kernel` (low-tau instance) | IQL low-tau params (`gpu_iql_low.params`) |
| 6 | `attn_adam_kernel` | attention/TLOB params (shared cubin) |
| 7 | `curiosity_adam_step` | curiosity-module params |

IQL high-tau and low-tau share the `iql_adam_kernel` cubin but have separate param buffers and Adam states (one `GpuIql` instance per τ value). They are 2 distinct param groups (4, 5), 8 groups total.

The trunk/value-head/branch split for the main DQN params buffer requires the producer to know offsets. These offsets are already known to the Mech 9 launch sites (since they pass `total_params` etc.). Producer kernels take `(offset, length)` as launch args, same pattern.

### EMA rates — Pearl D: Wiener-optimal adaptive α

**No hardcoded α values across all 47 producer kernels (40 new + 7 retrofit).** Each producer derives its own α per step from observed signal variances via Wiener-filter optimal smoothing.

For a signal `x[t]` (the producer's per-step observation, e.g., step_p99):
```
sample_var[t] = (1-α_meta) · sample_var[t-1] + α_meta · (x[t] - x_mean[t-1])²
diff_var[t]   = (1-α_meta) · diff_var[t-1]   + α_meta · (x[t] - x_lag[t])²
α*[t]         = diff_var[t] / (diff_var[t] + sample_var[t] + ε_div)
x_mean[t]     = (1 - α*[t]) · x_mean[t-1] + α*[t] · x[t]
x_lag[t+1]    = x[t]
```

`ε_div = 1e-8` is **division-safety** (Adam-ε numerical category — protects 0/0 in α* formula, not a magnitude estimate).

`α_meta` is the META-EMA rate for tracking the variances themselves. To avoid the recursion problem (what controls α_meta?), it is fixed at `α_meta = 1 / N_step_history` where N_step_history is structurally determined by the smoke's per-fold step count (typically ~1000 → α_meta = 1e-3). Documented as a structural-design parameter (analogous to "buffer size" — derived from training cardinality, not magnitude tuning). One value across all producers (uniform meta-smoothing).

**Why this is theoretically optimal:** for a signal modeled as `x[t] = μ[t] + noise[t]` where `μ[t]` is the latent value we want to track and `noise[t]` is observation noise, the optimal Wiener-filter estimator weight is exactly `α* = process_variance / (process_variance + noise_variance)`. `diff_var` estimates process variance (how much μ moves between steps); `sample_var` estimates noise variance.

**Behavior:**
- **Step 0 (cold start):** Pearl A (sentinel-detection branch in the producer) overrides Pearl D entirely. Producer sees `x_mean[t-1] = 0` (sentinel) and writes `x_mean[0] = x[0]` directly, bypassing the Wiener formula. Wiener state buffers initialize at `(sample_var=0, diff_var=0, x_lag=x[0])` for the next step.
- **Step 1+:** Pearl D applies normally. α* derives from variance ratio.
- **Stationary signal:** μ is constant. diff_var → 0 (no step-to-step movement). sample_var > 0 (noise around the constant). α* → 0. Mean stays anchored. Correct behavior.
- **Non-stationary signal:** μ is moving. diff_var dominates. α* → 1. Mean tracks new observations. Correct behavior.
- **Mid-regime:** α* in (0, 1), exact value reflects signal-to-noise ratio of process change.

**Producer state per ISV bound slot:** 3 floats (`sample_var`, `diff_var`, `x_lag`) stored in a dedicated mapped-pinned buffer `wiener_state_buf: MappedF32Buffer` of total size `40 × 3 = 120 floats` (480 bytes). Per `feedback_no_htod_htoh_only_mapped_pinned`. Plus the producer's own working state (e.g., histogram bins in shared memory).

The current EMA value (`x_mean`) IS the ISV bound slot itself — no separate storage. Producer reads `isv[BOUND_X]` (= old x_mean), computes new x_mean using Pearl D formula + state buffer, writes back to `isv[BOUND_X]`.

**Pearl D does NOT subsume Pearl A.** Pearl A's first-observation replacement requires an explicit sentinel-detection branch in the producer (`if isv[BOUND_X] == 0.0 then write step_observation directly, bypassing Wiener`). At step 0 Pearl D's formula gives `x_mean[0] = (1-0)·0 + 0·x[0] = 0` (NOT `x[0]`) — wrong. Both pearls are necessary; Pearl A handles t=0, Pearl D handles t≥1. The earlier claim that D subsumes A was mathematically incorrect and is corrected here.

**Subsumes the per-signal time-window argument from the first-draft spec.** No more "α=0.05 for fast signals, α=0.001 for slow signals" per-producer choice — α* adapts per-signal per-step automatically.

### Cold-start — Pearl A: First-observation bootstrap

**No theoretical-formula bootstrap values.** No Xavier formulas, no `2.33` z-score, no `√2` magnitudes. The bootstrap IS the first observation.

```
At fold-boundary reset:    isv[BOUND_X] = 0                       (sentinel "uninitialized")
                           wiener_state[BOUND_X] = (0, 0, 0)      (sample_var, diff_var, x_lag all 0)
Producer step 0 detects sentinel (isv[BOUND_X] == 0):
                           isv[BOUND_X] = step_observation        (full replacement, NO Wiener math)
                           wiener_state[BOUND_X].x_lag = step_observation
Producer step 1+:          Pearl D Wiener-optimal blend (sentinel no longer 0)
Consumer cold-start:       bound = isv[BOUND_X].max(ε_clamp_floor)  (ε_clamp_floor = 1.0)
```

**Two epsilons (distinct names, distinct purposes):**
- `ε_clamp_floor = 1.0` — consumer-side numerical safety floor. Prevents `bound = 0` from collapsing the clamp at step 0 before any producer runs. Same Adam-ε category — protects arithmetic, not a magnitude estimate.
- `ε_div = 1e-8` — producer-side division safety in Pearl D's α* formula. Prevents 0/0. Different value, different role; named separately to avoid confusion.

**Why this is a pearl:**
- Eliminates every magnitude-derived constant from cold-start.
- Adapts to actual signal distribution at fold start. F1's natural p99 may differ from F0's; the bootstrap automatically tracks.
- Pure form of "the bound lives in ISV": even cold-start values come from observation, not theory.

**Pearl A and Pearl D are complementary, NOT hierarchical.** Pearl A handles t=0 (sentinel-detection branch); Pearl D handles t≥1 (Wiener formula). The earlier claim that D subsumes A was mathematically incorrect — Pearl D's formula at t=0 yields `x_mean[0] = 0`, not `x[0]`. Both mechanisms are explicit in producer code.

### Reset semantics

Per `StateResetRegistry`: at fold boundary, all 40 ISV bound slots AND their `wiener_state` triples (sample_var, diff_var, x_lag) reset to **0** (Pearl A sentinel — no theoretical-formula bootstrap). The producer's first step after reset sees the sentinel and replaces with `step_observation` (Pearl A). Subsequent steps use Pearl D's adaptive Wiener formula.

Reset entries follow existing pattern in `state_reset_registry.rs`: one entry per ISV slot for `isv[BOUND_X] = 0` plus three entries per slot for `wiener_state[BOUND_X].{sample_var, diff_var, x_lag} = 0`. Total: `40 + 40×3 = 160` reset entries (compact tabular addition; existing infrastructure handles iteration).

This automatically handles the cross-fold anchor-staleness pathology without needing Mech 8.

### Weight decay producer signal

`AdamW` weight decay subtracts `λ × p` from each parameter per step. The "right" λ balances gradient pressure (which grows weights) against decay (which shrinks them). At equilibrium of `d/dt(||w||²) = 2(w·g) - 2λ||w||²`:

```
λ_equilibrium = (w · g) / ||w||²
```

This is the per-step **gradient projection onto weight direction divided by weight norm squared** — derived purely from observables. Sign-corrected variant: `λ = |w · g| / max(||w||², ε)` (always positive; ε from per-group bootstrap).

Producer kernel `wd_rate_update[group]`:
- Pass 1 (block-wide reduce): compute `w_dot_g = Σ_i w[i] × g[i]` over the param-group's slice
- Pass 2 (block-wide reduce): compute `w_norm_sq = Σ_i w[i]²` over the slice
- Final: `step_wd = |w_dot_g| / max(w_norm_sq, bootstrap²)` ; EMA into `ISV[WD_RATE[group]]`

Adam kernels read `weight_decay = ISV[WD_RATE[group]]` from the launch site (no kernel-internal change beyond consuming the launch-site-supplied value, replacing the existing scalar field). Per-group → 8 producers, 8 ISV slots.

**EMA rate and bootstrap follow the universal Pearls A+D contract** — no hardcoded α (Pearl D Wiener-optimal adaptive α from variance ratio), no theoretical bootstrap (Pearl A sentinel-detection at t=0). The producer's per-step output `step_wd = |w·g|/||w||²` flows through the same Wiener-EMA path as every other producer.

This is the same equilibrium-derivation principle the Adam paper uses for the β values — derived from a continuous-time stability analysis. Theoretically grounded, not tuned.

### L1 lambda producer signal — gradient-direction entropy deficit pearl

L1 regularization induces sparsity by pushing weights below `lr × λ` to exact zero. The principled question: when should pruning happen, and at what strength?

**Insight:** L1 is regularizing against feature redundancy. The natural signal is **gradient-direction entropy deficit** across input features.

For the trunk's first weight matrix `w[H, K]` (where K is input feature dim):
- For each input feature `i`: `g_feat[i] = ||g_w[:, i]||₂` (L2 norm of gradient column — how much that feature's weights "want to change")
- Normalized distribution: `p[i] = g_feat[i] / Σ_j g_feat[j]`
- Shannon entropy: `H_observed = -Σ_i p[i] · log p[i]`
- Maximum (uniform): `H_uniform = log(K)`
- Deficit: `D = (H_uniform - H_observed) / H_uniform ∈ [0, 1]`

D is a self-curriculum signal:
- **D ≈ 0** (gradient uniform across features) → network hasn't differentiated → λ=0 (no pruning, let learning proceed)
- **D ≈ 1** (gradient concentrated on few features) → network has identified what matters → λ grows to prune the rest

Combined with a magnitude scale derived from observables:
```
λ[group] = (mean(|g|) / mean(|w|)) × D[group]
```

Producer kernel `l1_lambda_update[group]`:
- Pass 1 (per-column reduce): `g_feat[i] = sqrt(Σ_h g_w[h,i]²)` for each i ∈ [0, K) — single block, K-thread tile
- Pass 2 (block-wide reduce): `Σ g_feat[i]` for normalization, plus `mean(|g|)` and `mean(|w|)` over full slice
- Pass 3 (single-thread): compute `H_observed`, `D`, `λ_step = (mean_g / mean_w) × D`
- EMA into `ISV[L1_LAMBDA[group]]` (currently only trunk has L1 → 1 producer, 1 ISV slot)

Adam kernel reads `l1_lambda = ISV[L1_LAMBDA[group]]` at the launch site, passes through to the existing L1 proximal step.

**EMA rate and bootstrap follow the universal Pearls A+D contract** — no hardcoded α, no fixed bootstrap. Pearl A sentinel-detection at t=0; Pearl D Wiener-optimal adaptive α at t≥1. The natural curriculum behavior emerges automatically: at fold boundary, the producer's first observation `step_λ = (mean|g|/mean|w|) × D` is small because `D ≈ 0` (gradient still uniform across features pre-differentiation), so the bound starts small and ramps as `D` grows. No `Bootstrap = 0.0` constant needed — the curriculum is in the signal itself.

**Pearl name (post-validation):** `pearl_signal_driven_regularisation_strength` — extends `pearl_adaptive_moe_lambda` to a class of regularization-strength controllers driven by entropy-deficit of the regularized quantity. Captured in memory once SP4 ships and validates.

## Producer architecture — Pearl B: Fused per-param-group statistics oracle

**Producer count: 15 kernels (NOT 40).** The naive "40 separate producer kernels" would waste memory bandwidth — the 4 per-group signals (WEIGHT_BOUND, ADAM_M_BOUND, ADAM_V_BOUND, WD_RATE) all read the same 4 buffers (`params, grads, adam_m, adam_v`) for the same group. Fusing them into one kernel per group reads each buffer once and computes all 4 stats in a single multi-pass operation. L1_LAMBDA (trunk only) extends the trunk's fused producer with one more pass for the gradient-direction entropy.

### Final producer kernel inventory

| Producer | Reads | Writes ISV slots | Count |
|---|---|---|---|
| `target_q_p99_update` | denoise_target_q_buf | TARGET_Q_BOUND | 1 |
| `atom_pos_p99_update[branch]` | atom_positions[branch] slice | ATOM_POS_BOUND[branch] | 4 |
| `param_group_stats_oracle[group]` | params, grads, adam_m, adam_v (all per-group slice) | WEIGHT_BOUND[group], ADAM_M_BOUND[group], ADAM_V_BOUND[group], WD_RATE[group] (+ L1_LAMBDA for group 0 only) | 8 |
| `grad_norm_p99_update` | grad_norm_buf | GRAD_CLIP_BOUND | 1 |
| `h_s2_p99_update` | save_h_s2 | H_S2_BOUND | 1 |
| **Total** | | **40 ISV slots** | **15 kernels** |

The fused per-group oracle uses 4 sequential passes inside one kernel (sharing block sync barriers):
- Pass A: max-reduce + linear-histogram-p99 of `|params|` → WEIGHT_BOUND[group]
- Pass B: max-reduce + linear-histogram-p99 of `|adam_m|` → ADAM_M_BOUND[group]
- Pass C: max-reduce + linear-histogram-p99 of `|adam_v|` → ADAM_V_BOUND[group]
- Pass D: 4-way parallel block-wide reduce computing `Σ w·g`, `Σ w²`, `Σ |g|`, `Σ |w|` (each thread maintains 4 register accumulators; tree-reduce per accumulator using shared memory) → WD_RATE[group] = |w·g|/||w||²; the `mean|g|`, `mean|w|` are also available for L1's λ scale.
- (Group 0 only) Pass E: per-feature gradient L2 norm `||g_w[:,i]||` for i ∈ [0, K); single-thread normalize, entropy H, deficit D; L1_LAMBDA[trunk] = (mean|g|/mean|w|) × D.

All passes apply Pearls A+D to update each ISV bound slot via Wiener-optimal EMA with sentinel-detection (no hardcoded α, no theoretical bootstrap).

Each pass uses block-shared-memory infrastructure. Memory bandwidth: each buffer read once (4× reduction from non-fused). Per-step cost per oracle: ~30-50 μs (proportional to sum of buffer sizes).

**Total per-step launches: 15** (vs 40 non-fused). Latency overhead ~0.3% of step time.

**Why this is a pearl, not just an optimization:** the architectural insight is that per-param-group bounds are a *unit* — they all derive from the same buffers, serve the same Adam kernel, and must be reset together at fold boundaries. Fusing at the producer level mirrors the conceptual grouping. Future per-group signals (spectral-norm tracking, gradient-direction stability, etc.) extend the existing oracle by adding a pass — no new kernel infrastructure required.

**Pearl name candidate:** `pearl_fused_per_group_statistics_oracle` — when N signals derive from the same buffer set with the same time-window, fuse the producer to amortize memory bandwidth and codify the conceptual unit.

### Per-signal kernels

The 15 producers, mirroring the existing `h_s2_rms_ema_update` shape but extended with Pearls A+D (sentinel-detection + Wiener-state pointers; NO `ema_alpha` arg).

Single-output producer signature (`target_q`, `atom_pos`, `grad_norm`, `h_s2`):

```cuda
extern "C" __global__ void <signal>_p99_update(
    const float* __restrict__ buf,                  /* signal buffer */
    int          offset,                            /* slice offset (bytes/4) */
    int          length,                            /* slice length */
    float*       __restrict__ isv,                  /* ISV bus */
    int          isv_bound_index,                   /* this signal's ISV slot */
    float*       __restrict__ wiener_state,         /* mapped-pinned [N_BOUNDS × 3] */
    int          wiener_state_offset,               /* this signal's offset into wiener_state (= isv_bound_index × 3) */
    float        alpha_meta,                        /* Pearl D meta-α for variance EMAs (single uniform value, structurally derived) */
    int*         __restrict__ nan_flags_buf,        /* sticky-flag diagnostic */
    int          diag_slot                          /* this signal's diagnostic slot */
);
```

Single-block, 256-thread:
- Pass 1 (max-reduce): block computes `step_max = max(|v|)` over slice, also writes `nan_flags_buf[diag_slot] = 1` if `step_max >= isv[isv_bound_index]` (diagnostic = clamp engagement).
- Pass 2 (bin): 256 log-spaced bins covering `[0, step_max]`. Each thread bins its values via shared-memory counters (no atomicAdd; per-thread sub-counts merged via tree reduce after).
- Pass 3 (find p99): thread 0 walks bins from highest, accumulates count. When cumulative ≥ N/100, that bin's lower-edge is `step_p99`.
- Final: thread 0 writes `isv[isv_bound_index] = (1-α) × prev + α × step_p99`.

Cold-path launches in `training_loop.rs` alongside `launch_h_s2_rms_ema` for the buffer-based bounds (h_s2, target_q, atom_pos). For the captured-graph bounds (h_s2, target_q, atom_pos), the producer is INSIDE the captured graph immediately before the consumer clamp — see "Pre-clamp vs post-clamp" above. For weights/Adam (post-clamp acceptable), producer in cold-path after captured graph.

**Total per-step launches added:** 28 (vs 4-5 existing producers). Each ~5-10 μs single-block on L40S = 140-280 μs added per step. F0 baseline ~50 ms/step → ~0.3-0.6% latency overhead. Acceptable.

### Unit testing

Each producer kernel ships with a unit test that validates the p99 computation against a known-distribution input:

```rust
#[test]
fn h_s2_p99_producer_matches_known_distribution() {
    // Synthetic input: 1000 samples drawn from N(0, 1)
    // Theoretical p99 ≈ 2.33
    // Run producer kernel, read ISV slot
    // Assert |computed_p99 - 2.33| < 0.1 (4% tolerance for histogram quantization)
}
```

One test per producer (28 total). Each test runs in ~100 ms via mapped-pinned synthetic input. Catches algorithm bugs *before* L40S smoke — addresses critical issue #8 from self-review.

## Migration sequencing

Per `feedback_no_partial_refactor`, the contract change is one atomic commit. Three sequential layers manage risk:

### Layer A — additive infrastructure (no behavior change)

- Add 40 ISV slot indices to the bus (extend `ISV_TOTAL_DIM` as needed)
- Allocate `wiener_state_buf: MappedF32Buffer` of size `40 × 3 = 120 floats` for Pearl D's per-slot (sample_var, diff_var, x_lag) state (per `feedback_no_htod_htoh_only_mapped_pinned`)
- Allocate `clamp_engage_per_block_buf: MappedI32Buffer` of size `8 × MAX_BLOCKS_PER_ADAM` for Pearl C's per-(block,group) engagement counters (≤ 2048 ints)
- Implement 15 producer kernels per Pearl B (1 target_q + 4 atom_pos + 8 fused-per-group oracle + 1 grad_norm + 1 h_s2), each with unit tests for every output slot it writes
- Implement Pearl D (Wiener-optimal adaptive α) inside each producer's EMA-update step
- Implement Pearl A (sentinel = first-observation replacement) at the EMA step (explicit branch — Pearl A is NOT subsumed by Pearl D)
- Implement Pearl C (engagement-rate self-correction) for in-kernel-clamp signals: per-Adam-kernel register-then-tree-reduce engagement counter + host-side rate-deficit EMA + force-bump trigger
- Wire 15 launch sites (4 in captured-graph for buffer-based bounds: target_q, 4 atom_pos; 11 in cold-path `training_loop.rs` for 8 fused oracles + grad-norm + h_s2)
- Retrofit Pearls A and D into the 7 existing pre-SP4 ISV producers (h_s2_rms_ema, grad_norm_slow_ema, aux_heads_loss_ema, moe_gate_entropy_ema, vsn_mask_ema, iqn_quantile_ema, reward_component_ema) — replace their hardcoded α with adaptive α; sentinel-bootstrap at fold boundary; allocate Wiener state for each in `wiener_state_buf` (extending its size to `(40 + 7) × 3 = 141 floats`)
- Add 40 `StateResetRegistry` entries for the new ISV bound slots (each resets to sentinel `0.0`) + 40 entries for the corresponding Wiener-state triples (each resets to `0.0`) + 7 retrofit entries for existing producers' Wiener states
- Update existing-producer reset entries to use sentinel `0.0` (no formula bootstraps)

After Layer A: all 40 ISV slots populate correctly each step via Pearls A/B/D; cargo check + 40 unit tests + L40S smoke (regression check) all pass.

### Layer B — atomic consumer migration (the contract change)

ALL consumer sites flip simultaneously in one commit. Files modified:

- `cuda_pipeline/gpu_dqn_trainer.rs` — Mech 1 clamp call site reads `ISV[TARGET_Q_BOUND]`; Mech 6 reads `ISV[GRAD_CLIP_BOUND]`; Mech 10 clamp call site reads `ISV[H_S2_BOUND]`; Adam launch sites compute `weight_clamp_max_abs` from `ISV[WEIGHT_BOUND[group]]`, `weight_decay` from `ISV[WD_RATE[group]]`, and `l1_lambda` from `ISV[L1_LAMBDA[group]]` (trunk only).
- `cuda_pipeline/atoms_update_kernel.cu` (and `iql_value_kernel.cu`, `experience_kernels.cu`) — Mech 2 clamps read `isv_signals[ATOM_POS_BOUND[branch]]`
- `cuda_pipeline/dqn_utility_kernels.cu` — Mech 5 fused diagnostic kernel: per-slot threshold reads from corresponding `isv_signals[BOUND_X]` instead of computing `K × q_abs_ref_eff`. Adam kernel's `weight_decay` and `l1_lambda` args become ISV-sourced (replacing existing scalar config fields).
- `cuda_pipeline/iqn_dual_head_kernel.cu`, `iql_value_kernel.cu`, `attention_backward_kernel.cu`, `curiosity_training_kernel.cu` — each Adam kernel's `weight_clamp_max_abs` and `weight_decay` args read from their group's bounds (no L1 in these — only trunk has L1).
- `crates/ml/src/trainers/dqn/hyperparams.rs` (or similar) — remove `weight_decay`, `l1_lambda` from `HyperParams` config struct since they're now signal-driven (not configurable). Replace with documentation pointing to ISV slots.

Single coordinated commit. Hardcoded multipliers, weight decay, L1 lambda — ALL removed. `feedback_no_partial_refactor` honored. SP4 closes 100% of the magnitude/regularization surface.

### Layer C — validation + cleanup

- L40S smoke validation (multi_fold_convergence::test_multi_fold_convergence)
- Remove now-dead `q_abs_ref_eff` and `h_s2_rms_ema_eff` parameters from the Mech 5 fused-kernel signature (unused after Layer B's per-slot ISV reads)
- **Retain `grad_norm_slow_ema` infrastructure (cross-fold legitimate baseline) — migrate the EMA UPDATE to GPU per `feedback_no_cpu_compute_strict`:** the original Layer C plan called for retiring `grad_norm_slow_ema_pinned` after Mech 6's migration to `ISV[GRAD_CLIP_BOUND]`. Verification on 2026-05-01 surfaced a SECOND live consumer — `fold_warmup_factor_update` (commit `4ef1d8ebb`, predates SP4) reads the slow EMA as the cross-fold steady-state grad-norm baseline that the warmup factor's denominator compares against. Legitimate cross-fold mechanism; not orphan. The actual defect surfaced during the audit was that the EMA UPDATE itself ran as host-side `(1-α) × prev + α × obs` arithmetic in `update_adaptive_clip` — exactly the pattern `feedback_no_cpu_compute_strict` (saved 2026-05-01) strictly forbids. Layer C C1 redesigned: keep both EMA buffers, migrate the per-step UPDATE to a new single-thread `update_grad_norm_emas_kernel` chained on the producer's stream after `launch_grad_norm_finalize`. The `grad_norm_ema` (fast EMA, used elsewhere) stays.
- Decide retention of `ISV[Q_ABS_REF=16]` and `ISV[H_S2_RMS_EMA=96]` producers: after Layer B, no clamp consumer reads these. They may still be useful for HEALTH_DIAG monitoring and `mag_concat_qdir`'s adaptive scale (P4.T2c.3c.6 consumer). **Decision:** keep producers running, drop only the orphaned Mech 1/2/5/6/9/10 consumer reads. Document as "monitoring-only ISV" in the audit.
- Update audit docs (`dqn-backward-nan-audit.md` mechanism table, slot allocation table; `dqn-wire-up-audit.md` Invariant 7 entries — one big entry covering 40 new slots + 15 producers + Pearl C engagement-counter buffer + Wiener-state buffer + retired components)
- Update `MEMORY.md` and add memory entries:
  - SP4 close-out (project entry — like SP3)
  - `pearl_first_observation_bootstrap`
  - `pearl_fused_per_group_statistics_oracle`
  - `pearl_engagement_rate_self_correction`
  - `pearl_wiener_optimal_adaptive_ema`
  - `pearl_signal_driven_regularisation_strength`

## Validation

### Pass criteria for the post-Layer-C smoke

1. **F0, F1, F2 all complete 5 epochs** — no early-stop on NaN (zero NaN-CLAMPED log lines)
2. **F1 Best Sharpe > 0** — fixes the original SP3 motivation; no h_s2-overflow cascade
3. **F0 Best Sharpe ≥ 37.5** — *revised criterion* matches `smoke-test-76pnm`'s 1e30-effectively-unclamped control. The 45.47 v2 baseline may be unreachable due to launch-scheduling-shift (the diagnostic smoke already showed F0=37.53 with no value-clamping at all). 37.5 is the realistic upper bound for any Mech-10-class design.
4. **F2 Best Sharpe ≥ 55** — F2 reached 80.55 with Mech 10 100×, and 56.70 with v2; ≥55 is the conservative floor
5. **Producer unit tests all pass** with <5% linear-histogram-quantization error (magnitude bounds, with bin width = step_max / 256), <2% relative error vs. analytical formula (weight decay, L1 lambda)
6. **No new ISV slots beyond the 40 in this design**
7. **No new kernels beyond the 15 fused producers + Adam-kernel-internal Pearl C engagement counter**
8. **Each consumer site reads exactly one ISV slot** (no compound expressions like `K × isv[X]`)
9. **Mech 5 diagnostic slots fire only on regime shifts** — sticky-flag pattern
10. **L1 lambda starts low and ramps up** — entropy-deficit pearl: log `ISV[L1_LAMBDA[trunk]]` per fold. At fold-boundary reset, Pearl A sentinel-detection means producer step-0 writes `step_λ = (mean|g|/mean|w|) × D` directly. With network just reset, D ≈ 0 (gradient still uniform) → step_λ ≈ 0 → ISV[L1_LAMBDA] ≈ 0 at step 0. Subsequent steps: λ ramps as D grows. If λ stays near 0 throughout a fold, gradient never differentiates → either D signal is degenerate or the network's gradients are uniformly distributed (separate investigation thread).
11. **Weight decay tracks gradient pressure** — log `ISV[WD_RATE[group]]` per fold; expect non-zero, smooth, per-group-specific values. If WD_RATE is identically zero, gradient projection onto weights is zero (network not learning anything from those weights → separate concern).
12. **No `weight_decay` or `l1_lambda` config fields remain in `HyperParams`** — the contract is fully ISV-driven; removing the config field prevents accidental hardcoding regression.
13. **Pearl C engagement-rate self-correction fires when expected** — log per-step `engagement_rate[group]` for in-kernel-clamp signals. Expected: ≈ 0.01 (1%) under stable training; transient bumps during regime shift; Pearl C force-bump events should be rare (< once per fold) under correct operation. Frequent force-bumps signal a deeper issue (e.g., distribution shift faster than EMA tracking).
14. **No hardcoded α values in any producer** — grep the producer-kernel sources for hardcoded ema_alpha/α values; should find zero outside `α_meta` (the structural meta-EMA constant). Zero magnitude constants in clamps. Zero theoretical bootstrap formulas.

### Regression sentinels (logged, not pass/fail)

- Per-step count of clamp engagements per bound — diagnostic for distribution shifts
- p99-EMA values across folds — should be smooth; sudden jumps signal regime shifts
- Producer-launch latency total — should be ~0.5% of step time, <1% upper bound

## Self-review acknowledged limitations

The first-draft spec carried 8 limitations. Pearls A-D resolve most of them; the remaining limitations are genuinely irreducible (novel algorithms not yet field-tested, F0 launch-scheduling variance independent of design choice).

**Resolved by pearls (no longer limitations):**

- ~~In-kernel clamp feedback loop accepted~~ — **Pearl C resolves** via engagement-rate self-correction. Force-bump on sustained engagement deviation from theoretical 1%.
- ~~Smoke time-budget marginally validates Adam EMAs~~ — **Pearl D resolves** via adaptive α. Step 0 = first-observation replacement (Pearl A); subsequent steps use Wiener-optimal α. EMA converges within tens of steps regardless of fold length.
- ~~28/36/40 producer kernels is plumbing-heavy~~ — **Pearl B resolves**. 15 fused producers with conceptual per-group grouping (1 target_q + 4 atom_pos + 8 fused-per-group oracle + 1 grad_norm + 1 h_s2 = 15).
- ~~Stale-by-one-step bound at fold boundary~~ — **Pearl A resolves**. Sentinel = 0 detected by producer → first-observation replaces immediately. No theoretical bootstrap to lag from.
- ~~Histogram log-spacing 256-bin choice~~ — still a numerical-precision choice (0.4% quantile precision). Same Adam-ε category. Documented but not iterated.
- ~~Carved-out items (weight decay, L1 lambda)~~ — **resolved in scope expansion**. Both fully signal-driven via gradient-pressure-projection (WD) and gradient-direction-entropy-deficit (L1).

**Remaining limitations (genuinely irreducible):**

1. **F0 may not return to v2's 45.47.** The 1e30 diagnostic smoke (effectively-no-clamping) showed F0=37.53, suggesting ~8 points of F0 variance comes from launch-scheduling-shift independent of clamp value. SP4's success criterion is F0 ≥ 37.5, not 45. This is a property of GPU kernel scheduling under TF32, not the design.

2. **Layer B is high-risk atomic flip.** 15 consumer sites + 7 retrofitted existing producers simultaneously change behavior. Mitigated by: (a) Layer A unit tests catch producer bugs before Layer B; (b) single L40S smoke after Layer B is fast (~17 min); (c) revert is one git commit if smoke fails.

3. **L1 lambda pearl is novel and untested at scale.** The gradient-direction entropy-deficit signal extends `pearl_adaptive_moe_lambda` to a class of regularization-strength controllers — but the specific formulation has not yet been validated in an ML pipeline at this scale. Risk: the curriculum (D ramps up as gradient differentiates) might not match the network's actual learning dynamics. Mitigation: validation criterion #10 explicitly checks that λ ramps from 0 to non-zero within each fold; if not, the signal is degenerate and we'd need a separate investigation thread. Bootstrap-via-Pearl-A means worst-case behavior is "no L1 regularization" until a meaningful step_p99 is observed (equivalent to L1-disabled), which is graceful.

4. **Weight decay equilibrium formula is theoretically sound but untested at scale.** `λ = |w·g| / ||w||²` derived from `d/dt(||w||²) = 0` equilibrium analysis. Real training is non-stationary — equilibrium may not hold. Mitigation: Pearl D's adaptive α smooths step-to-step variation; Pearl A's first-observation bootstrap is graceful; validation criterion #11 logs WD_RATE values per fold so anomalies are visible.

5. **Pearl C requires engagement-counter buffer per param-group.** Adds ~7 floats of mapped-pinned scratch space (one count per param-group). Per-step cost: per-thread shared-memory increment + block-wide reduce on existing nan-flag-buf path. Negligible.

6. **Pearl D's adaptive α adds 3 floats of state per ISV bound slot.** sample_var, diff_var, x_lag. 47 slots × 3 floats × 4 bytes ≈ 564 bytes mapped-pinned (`wiener_state_buf`). Negligible.

## Estimated effort

- **Layer A:** 15 fused producer kernels (per Pearl B) + 40 unit tests (one per output ISV slot) + Pearls A/D adaptive-EMA scaffolding + Pearl C engagement-rate self-correction (per-Adam-kernel block-reduce + host-side rate-deficit EMA + force-bump trigger) + retrofit of 7 existing ISV producers + Wiener-state buffer (141 floats) + Pearl C engagement-counter buffer (8 × MAX_BLOCKS_PER_ADAM ints) + ISV plumbing + reset entries + 15 launch sites + retire `grad_norm_slow_ema` ≈ 3500-4500 LOC, ~1.5-2 weeks of focused work. Per-kernel is more complex (multi-pass + adaptive α) but kernel count is 60% lower than first draft. Net effort comparable.
- **Layer B:** ~15 consumer sites × ~10 LOC change each ≈ 200 LOC, one focused day
- **Layer C:** smoke + audit doc + memory entries ≈ 100 LOC + validation runs

Total: ~3500-4500 LOC across 3 layers, 1.5-2.5 weeks of focused work, 1 L40S smoke validation. Mid-large project.

## References

- `docs/superpowers/specs/2026-04-29-numerical-stability-sp2-sp3-design.md` — predecessor SP3 spec
- `crates/ml/src/cuda_pipeline/h_s2_rms_ema_kernel.cu` — pattern reference for producer kernels
- `crates/ml/src/trainers/dqn/state_reset_registry.rs` — reset infrastructure
- `feedback_isv_for_adaptive_bounds.md`, `feedback_adaptive_not_tuned.md`, `feedback_no_partial_refactor.md` — guiding principles
- `pearl_adaptive_moe_lambda.md` — exemplar for "diagnostic signal drives controller" pattern
- SP1 ε-floor pearl — preserved in spirit (cold-start protection via per-slot theoretical-init bootstrap rather than hardcoded `.max(1.0)`)
