# SP7: Loss-Balance Controller — Design

**Status**: approved (2026-05-03)
**Branch**: `sp5-magnitude-differentiation` (extending SP5 closure)
**Predecessor**: SP5 close-out (commits `ab378327e` … `04df20bdc`)

## Goal

Replace the dormant per-branch CQL budget output (hardcoded to 0 by Pearl 2, floored to 0.02 in the consumer) and the floor-pinned per-branch C51 budget output with a single **outcome-driven controller** that adapts both heads' per-branch budgets so each loss head's gradient contribution stays in a target ratio relative to IQN.

This unblocks magnitude differentiation: at production scale (50-epoch baseline at HEAD `2fb7d7f57`, smoke `train-multi-seed-n4qv2` ep4-7) we observed `grad_split_bwd cql=4.13` vs `iqn=0.07` (≈60×), with magnitude Q-values within 0.003 of each other, producing `eval_dist eq=1.000` (full eval-collapse to Quarter via argmax).

## Non-goals

- Not redesigning Pearl 2. Pearl 2 keeps writing `flatness[b]` to `FLATNESS_BASE` (consumed by NoisyNet σ — `pearl_per_branch_noisy_sigma`) and `budget_iqn[b]` to `BUDGET_IQN_BASE`. IQN is the reference; needs to stay stable.
- Not adding new diagnostic instrumentation. The grad-norm signals the controller consumes already exist (per-loss `grad_decomp_launch_*` pinned slots, populated every step).
- Not adapting trail/distill/rec/pred budgets. Out of scope. Same controller pattern can be applied later.

## Architecture

```
                  Existing per-step pipeline (every training step)
                  ────────────────────────────────────────────────
   Forward pass → Backward (IQN, CQL, C51, Ens, distill, …)
                                           │
                  Each loss component runs `grad_decomp_launch_*`
                  which writes a 3-float pinned slot:
                  [mag_norm, dir_norm, trunk_norm]
                                           │
                                           ▼
   ┌────────────────────────────────────────────────────────┐
   │  loss_balance_controller_kernel  (NEW — single block, 8 threads)│
   │                                                        │
   │  Reads:                                                │
   │    iqn_decomp_pinned[3]   ← grad_decomp_launch_iqn     │
   │    cql_decomp_pinned[3]   ← grad_decomp_launch_cql_sx  │
   │    c51_decomp_pinned[3]   ← grad_decomp_launch_c51     │
   │    isv[BUDGET_CQL_BASE+0..4]  ← previous step          │
   │    isv[BUDGET_C51_BASE+0..4]  ← previous step          │
   │                                                        │
   │  Computes (per head h ∈ {CQL, C51}, per slice s):      │
   │    actual_ratio = h_norm[s] / max(iqn_norm[s], EPS)    │
   │    correction   = target_ratio[h] / max(ratio, EPS)    │
   │    new_budget   = (1-α)·old + α·(old × correction)     │
   │    new_budget   = clamp(new_budget, EPS, MAX_BUDGET)   │
   │                                                        │
   │  Writes (raw, pre-smoothing):                          │
   │    scratch[BUDGET_CQL_BASE+0..4]                       │
   │    scratch[BUDGET_C51_BASE+0..4]                       │
   └────────────────────────────────────────────────────────┘
                                           │
                                           ▼
   apply_pearls_ad_kernel (existing) — Pearl A sentinel-bootstrap
   + Pearl D smoothing → ISV[BUDGET_CQL_BASE+0..4],
                          ISV[BUDGET_C51_BASE+0..4]
                                           │
                                           ▼
   Next step's compute_adaptive_budgets() reads these slots
   → drives apply_cql_saxpy / apply_c51_budget_scale.
```

## Inputs (existing — no new producers needed)

All three are pinned 3-float slots `[mag_norm, dir_norm, trunk_norm]`, computed every step by the corresponding `grad_decomp_launch_*` after each loss component's backward pass. The pinned device pointers are already accessible as kernel args.

| Field | Source | Wired? |
|---|---|---|
| `iqn_norm[mag, dir, trunk]` | `grad_decomp_launch_iqn` (per-step) | yes |
| `cql_norm[mag, dir, trunk]` | `grad_decomp_launch_cql_sx` (per-step) | yes |
| `c51_norm[mag, dir, trunk]` | `grad_decomp_launch_c51` (per-step) | yes |

The `cql_sx` decomp captures the SAXPY-applied delta (post-budget); `iqn` and `c51` capture the pre-budget delta. We use `cql_sx` for parity with what actually lands in `grad_buf`.

## Outputs (existing dormant slots)

| Slot | Current state | New state |
|---|---|---|
| `BUDGET_CQL_BASE+0..4` (ISV 198..202) | Pearl 2 writes 0 → floors to 0.02 in `compute_adaptive_budgets` | Controller writes adaptive per-branch budget |
| `BUDGET_C51_BASE+0..4` (ISV 190..194) | Pearl 2 writes `flatness × …` → in production drops below floor 0.05 | Controller writes adaptive per-branch budget |

`BUDGET_ENS_BASE+0..4` and `BUDGET_IQN_BASE+0..4` remain Pearl 2's responsibility for now (Ens is residual; IQN is reference).

## Math

For each loss head `h ∈ {CQL, C51}` and each branch `b ∈ {dir, mag, ord, urg}`:

The target ratio is **flatness-gated** (per `pearl_q_flatness_gates_conservatism`,
realized here for the first time). Flatness is read per-branch from
`FLATNESS_BASE+b` (Pearl 2's output, ISV slots 206..210). The structural
relationship between Q-distribution flatness and each loss head's utility
is opposite:

- **CQL** — pushes Q down to prevent OOD inflation. Useful when Q is
  well-separated (sharp). Harmful when Q is flat (pushes all actions down
  equally → worsens differentiation). Target ratio scales with `(1 − flatness)`.
- **C51** — shapes the value distribution. Useful when Q is flat (helps
  atoms differentiate). Less needed when Q is sharp. Target ratio scales
  with `flatness` (matches Pearl 2's existing C51 design intent, now
  realized as a closed-loop ratio target instead of an absolute budget
  formula).

```
flatness_b   = isv[FLATNESS_BASE + b]               // [0, 1], from Pearl 2
target_ratio[CQL][b] = ANCHOR_CQL_RATIO * (1.0 - flatness_b)  // ANCHOR_CQL_RATIO=2.0 (ceiling)
target_ratio[C51][b] = ANCHOR_C51_RATIO * flatness_b          // ANCHOR_C51_RATIO=1.0 (ceiling)

iqn_n = max(iqn_norm[s], EPS_DIV)
h_n   = h_norm[s]
actual_ratio = h_n / iqn_n
correction   = target_ratio[h][b] / max(actual_ratio, EPS_DIV)

old_budget = isv[BUDGET_h_BASE + b]
candidate  = old_budget * correction

// Pearl D — Wiener-optimal α (per pearl_wiener_optimal_adaptive_alpha):
// Tracks per-step diff variance and sample variance EMAs in dedicated ISV
// slots (see "Wiener-optimal α" section below).
alpha_eff = diff_var_b / (diff_var_b + sample_var_b + EPS_DIV)
alpha_eff = clamp(alpha_eff, ALPHA_FLOOR, ALPHA_CEIL)

new_budget = old_budget + alpha_eff * (candidate - old_budget)
new_budget = clamp(new_budget, EPS_DIV, MAX_BUDGET)

scratch[BUDGET_h_BASE + b] = new_budget
```

**Anchors are ceilings, not targets**: when flatness=0 (sharp Q), CQL fires
at full ANCHOR_CQL_RATIO=2.0; when flatness=1 (flat Q), CQL targets 0 and
the controller drives the budget toward 0 (with a floor at `EPS_DIV` — see
"Floor cleanup" below). C51 is the mirror: 0 when sharp, ANCHOR_C51_RATIO
when flat.

**Slice→branch mapping**:

| ISV slot | Branch | Slice used |
|---|---|---|
| `BUDGET_CQL_BASE+0` | direction | `dir` |
| `BUDGET_CQL_BASE+1` | magnitude | `mag` |
| `BUDGET_CQL_BASE+2` | order | `trunk` |
| `BUDGET_CQL_BASE+3` | urgency | `trunk` |
| `BUDGET_C51_BASE+0..3` | same pattern | same pattern |

Order and urgency reuse the trunk grad-norm ratio because `grad_decomp_kernel` only emits trunk/dir/mag slices. This is acceptable because:
- Order (3 actions) and urgency (3 actions) get the same gradient pressure as trunk receives;
- The TRUE per-branch grad-norm extension would require modifying `grad_decomp_kernel` to track 4 branches, which is out of scope and not load-bearing for the immediate problem (CQL dominance affects magnitude head, which we DO have a dedicated slice for).

## Cold start (Pearl A — sentinel bootstrap)

ISV slots start at sentinel 0 (per `pearl_first_observation_bootstrap`). The
key insight: `cql_norm[s]` reflects the *floor-applied* budget (the consumer
`compute_adaptive_budgets` already floors a 0-valued ISV read to
`COLD_START_FLOOR=0.02` before passing to `apply_cql_saxpy`). So the
"current effective budget" on a sentinel step is the floor, not zero.

First controller invocation seeds:

```
if cql_norm[s] < EPS_DIV || iqn_norm[s] < EPS_DIV:
    // Either side is uninstrumented or warmup hasn't started.
    seed = COLD_START_FLOOR              // 0.02 — Invariant 1 anchor
else if old_budget < EPS_DIV:            // sentinel 0 → first observation
    // The norm we observed was produced under cql_budget=COLD_START_FLOOR
    // (consumer-side floor). Multiplicative correction with that floor as
    // the basis seeds at the value that achieves target_ratio next step.
    actual_ratio = cql_norm[s] / iqn_norm[s]
    correction   = target_ratio[h] / max(actual_ratio, EPS_DIV)
    seed = COLD_START_FLOOR * correction
    seed = clamp(seed, EPS_DIV, MAX_BUDGET)
else:
    // Standard EMA update path (above).
```

Same logic for C51.

**Contract**: the cold-start basis (`COLD_START_FLOOR=0.02`) must equal the
floor used by `compute_adaptive_budgets` in `fused_training.rs:3386`. If
that floor changes, this constant moves with it. Same anchor, same value,
defined once in `loss_balance_controller_kernel.cu` and referenced from a
header comment in `fused_training.rs`.

## Fold-boundary reset (Pearl A)

The slots `BUDGET_CQL_BASE+0..4` and `BUDGET_C51_BASE+0..4` are already registered in `StateResetRegistry` (`state_reset_registry.rs:585-595`) with sentinel 0. **No new registration needed.** On fold boundary the controller automatically re-runs cold-start logic.

## Smoothing (Pearl D — Wiener-optimal α)

The kernel writes raw new-budget values to a scratch buffer. The existing `apply_pearls_ad_kernel` then runs:

- Pearl A: detects sentinel 0 in source ISV, replaces directly (skips smoothing on first observation).
- Pearl D: Wiener-optimal EMA based on `α = diff_var / (diff_var + sample_var + EPS_DIV)`.

The two variances are tracked in dedicated per-branch state-tracker EMA slots
(allocated below). This realizes `pearl_wiener_optimal_adaptive_alpha`
end-to-end — there is no fixed `α=0.01` constant left in the design.

**State-tracker slots** (new — appended to ISV layout):

| Slot block | Width | Purpose |
|---|---|---|
| `LB_DIFF_VAR_CQL_BASE` | 4 | per-branch EMA of (candidate_cql − old_cql)² |
| `LB_SAMPLE_VAR_CQL_BASE` | 4 | per-branch EMA of (cql_norm[s])² (signal energy) |
| `LB_DIFF_VAR_C51_BASE` | 4 | per-branch EMA of (candidate_c51 − old_c51)² |
| `LB_SAMPLE_VAR_C51_BASE` | 4 | per-branch EMA of (c51_norm[s])² |

Total: 16 new ISV slots. The state-tracker EMAs themselves use the
**existing** Pearl D apply pass (sentinel-bootstrap + Wiener α), making
this self-consistent — every adaptive scalar in the system is α-driven
the same way.

**α bounds** (numerical-stability anchors, NOT tuned):

| Bound | Value | Justification |
|---|---|---|
| `ALPHA_FLOOR` | 1e-4 | Absolute floor — prevents Wiener α going to 0 if denominator is dominated by noise. Allows budget tracking even in steady state. |
| `ALPHA_CEIL` | 0.5 | Half-step cap — prevents catastrophic over-correction on outlier sample variance spikes. |

These are explicit Invariant-1 carve-outs because Wiener-optimal α can
mathematically span [0, 1] and the controller is in a closed loop with
the Q-network — unbounded α destabilizes both layers.

The state-tracker block is added to `StateResetRegistry` with sentinel 0
(automatic Pearl A bootstrap on fold boundary).

## Pearl 2 changes

`pearl_2_budget_kernel.cu` is modified to **stop writing** to `scratch[BUDGET_CQL_BASE..]`, `scratch[BUDGET_C51_BASE..]`, `scratch[BUDGET_ENS_BASE..]`. It still writes:
- `BUDGET_IQN_BASE+0..4` — IQN budget (reference, needs to stay stable)
- `FLATNESS_BASE+0..4` — consumed by NoisyNet σ pearl

Pearl 2 launch order in `training_loop.rs:3704-3710` is unchanged. The new controller launches **after** all `grad_decomp_launch_*` calls (so iqn/cql/c51 norms are populated) and **before** `apply_pearls_ad_kernel` (so smoothing applies to the new outputs).

The `compute_adaptive_budgets` function in `fused_training.rs:3376` is unchanged — it reads ISV slots regardless of who wrote them.

## Floor cleanup (consumer side)

`compute_adaptive_budgets` in `fused_training.rs:3384-3387` currently floors
the read budgets:

```rust
c51[b] = self.read_isv_signal_at(BUDGET_C51_BASE + b).max(0.05_f32);
iqn[b] = self.read_isv_signal_at(BUDGET_IQN_BASE + b).max(0.05_f32);
cql[b] = self.read_isv_signal_at(BUDGET_CQL_BASE + b).max(0.02_f32);
ens[b] = self.read_isv_signal_at(BUDGET_ENS_BASE + b).max(0.02_f32);
```

The 0.05 / 0.02 floors are tuned constants that fight the controller — when
flatness=1 (Q very flat) the controller wants `cql_budget → 0` to let IQN
differentiate, but the floor pins it at 0.02. Per
`feedback_isv_for_adaptive_bounds`, these tuned floors must go.

**Change** — replace per-slot floor with sentinel-aware bootstrap:

```rust
// Helper, lives at the call site of compute_adaptive_budgets.
fn budget_or_bootstrap(raw: f32, bootstrap: f32) -> f32 {
    if raw < EPS_DIV { bootstrap } else { raw }
}

cql[b] = budget_or_bootstrap(self.read_isv_signal_at(BUDGET_CQL_BASE + b),
                              CQL_BOOTSTRAP_BUDGET);
c51[b] = budget_or_bootstrap(self.read_isv_signal_at(BUDGET_C51_BASE + b),
                              C51_BOOTSTRAP_BUDGET);
ens[b] = budget_or_bootstrap(self.read_isv_signal_at(BUDGET_ENS_BASE + b),
                              ENS_BOOTSTRAP_BUDGET);
// IQN keeps the existing structural floor (it's the reference, can't be 0):
iqn[b] = self.read_isv_signal_at(BUDGET_IQN_BASE + b).max(BASE_IQN);
```

The semantics flip: **once the controller has written any value ≥ `EPS_DIV`,
the consumer takes it verbatim** (no clamping back up). On a sentinel-0
read (cold start, fold boundary), the consumer uses the bootstrap value.
The bootstrap value is what the controller's cold-start basis assumes
the consumer used (contract — they reference the same constant).

**Bootstrap values (Invariant-1 anchors)**:

| Constant | Value | Justification |
|---|---|---|
| `CQL_BOOTSTRAP_BUDGET` | 0.02 | Same as the prior consumer floor; preserves SP4 baseline behavior on first step |
| `C51_BOOTSTRAP_BUDGET` | 0.05 | Same as the prior consumer floor |
| `ENS_BOOTSTRAP_BUDGET` | 0.02 | Same as the prior consumer floor |
| `BASE_IQN` | 0.11 | Existing Invariant-1 anchor (Pearl 2) — IQN reference floor |

## Stale doc cleanup (per `feedback_trust_code_not_docs`)

The docstring at `fused_training.rs:3409` claims:
```rust
/// B4/G5: Last adaptive CQL gradient budget (0.10×(1−regime)×health).
```

This formula is not implemented anywhere — the actual code at `fused_training.rs:3386` just floors to 0.02. The "regime_stability allocator" referenced in `state_reset_registry.rs:588` does not exist. **Both docstrings get rewritten** to describe the new controller (or deleted if redundant).

## Files touched

| File | Change |
|---|---|
| **NEW** `crates/ml/src/cuda_pipeline/loss_balance_controller_kernel.cu` | ~180 LOC kernel: 2 heads × 4 branches, flatness-gated targets, Wiener-α update with per-branch state-tracker EMA writes |
| `crates/ml/src/cuda_pipeline/sp5_isv_slots.rs` | add 16 new ISV slots: `LB_DIFF_VAR_CQL_BASE`/`LB_SAMPLE_VAR_CQL_BASE`/`LB_DIFF_VAR_C51_BASE`/`LB_SAMPLE_VAR_C51_BASE` (4 each) |
| `crates/ml/src/trainers/dqn/state_reset_registry.rs` | register the 16 new slots with sentinel 0 (Pearl A automatic bootstrap on fold boundary) |
| `crates/ml/src/cuda_pipeline/pearl_2_budget_kernel.cu` | drop CQL/C51/ENS writes; keep IQN + flatness |
| `crates/ml/src/trainers/dqn/fused_training.rs` | (a) add launch site after grad_decomp; (b) replace per-slot floor in `compute_adaptive_budgets` (line 3384) with sentinel-aware bootstrap helper; (c) delete stale "regime_stability allocator" docstring at line 3409 |
| `crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs` | kernel slot in trainer struct + load + capture path; new `launch_loss_balance_controller` fn |
| `crates/ml/build.rs` | cubin manifest entry for new kernel |
| `docs/dqn-wire-up-audit.md` | Fix 31 entry — what landed, what was deleted, what's new |
| `~/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_loss_balance_controller.md` | new memory pearl |
| `~/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/MEMORY.md` | index entry for the new pearl |

## Invariant-1 anchors (per `feedback_isv_for_adaptive_bounds`)

These are constants because they encode structural design choices or
numerical-stability anchors, NOT tuned values that should adapt:

| Constant | Value | Class | Justification |
|---|---|---|---|
| `ANCHOR_CQL_RATIO` | 2.0 | structural ceiling | Max CQL/IQN ratio when flatness=0 (sharp Q). Modulated by `(1−flatness)` per branch — actual target adapts. |
| `ANCHOR_C51_RATIO` | 1.0 | structural ceiling | Max C51/IQN ratio when flatness=1 (flat Q). Modulated by `flatness` per branch. |
| `ALPHA_FLOOR` | 1e-4 | numerical stability | Wiener α floor — keeps tracking even in steady state. |
| `ALPHA_CEIL` | 0.5 | numerical stability | Wiener α ceiling — prevents over-correction on outliers. |
| `EPS_DIV` | 1e-8 | numerical stability | Division-by-zero guard; same as Pearl 2. |
| `MAX_BUDGET` | 1.0 | structural | Budget is a multiplier in [0, 1]. |
| `CQL_BOOTSTRAP_BUDGET` | 0.02 | cold-start anchor | Initial CQL on sentinel read; matches prior consumer floor. |
| `C51_BOOTSTRAP_BUDGET` | 0.05 | cold-start anchor | Initial C51 on sentinel read; matches prior consumer floor. |
| `ENS_BOOTSTRAP_BUDGET` | 0.02 | cold-start anchor | Initial Ens on sentinel read; matches prior consumer floor. |
| `BASE_IQN` | 0.11 | structural floor | IQN is reference (denominator) — zero-ing breaks ratio compute. Existing Pearl 2 anchor. |

**No `ALPHA_META` constant** — replaced by Wiener-optimal α from per-branch
state-tracker EMAs.

**No `TARGET_*_OVER_IQN` constants** — replaced by flatness-modulated
per-branch targets.

## Acceptance criteria

All thresholds are ISV-relative where the corresponding signal exists.
Hardcoded numerical bounds appear only as numerical-stability anchors.

1. **Code lands clean**: `SQLX_OFFLINE=true cargo check -p ml --offline` and
   `SQLX_OFFLINE=true cargo build -p ml --release --offline --features cuda`
   both green at branch HEAD.
2. **Local RTX 3050 Ti smoke**: `multi_fold_convergence` passes. The
   per-branch CQL budget written to `BUDGET_CQL_BASE+b` differs from
   `CQL_BOOTSTRAP_BUDGET` (0.02) by at least `EPS_DIV` for at least one
   branch by ep5 — confirms the controller is writing, not just relying
   on bootstrap. Same check for `BUDGET_C51_BASE+b` vs `C51_BOOTSTRAP_BUDGET`.
3. **L40S 5-epoch smoke**: at HEALTH_DIAG[4],
   `actual_ratio_cql[mag] / target_ratio_cql[mag]` is within `[0.5, 2.0]`
   (i.e., the controller has converged to within 2× of the per-branch
   flatness-gated target). Same for C51. Tolerance is structural —
   Wiener α can take 2-3 steps to settle.
4. **L40S 50-epoch full validation — magnitude differentiation**:
   `(q_full − q_quarter) > 2 × NOISY_SIGMA[mag]` by ep20 — Q-spread
   exceeds NoisyNet σ so the policy has signal-dominant differentiation
   (currently <0.003 vs σ=0.0226, ratio 0.13 — fails this check).
   `eval_dist[ef] > eval_dist[eq] / 4` — Full magnitude is at least 25%
   of Quarter mass (currently 0% vs 100%).
5. **No regressions** — `label_scale` stays within
   `[0.05 × NOISY_SIGMA[mag], 5 × NOISY_SIGMA[mag]]` band (Bug-1 invariant
   preserved). `magnitude_distribution` smoke still passes (sentinel +
   bootstrap path is benign on synthetic data).

## Risk + rollback

**Risk 1: feedback loop instability**. If `α=0.01` is too aggressive, budget oscillation could cascade into Q-value oscillation. Mitigation: clamp range `[EPS_DIV, MAX_BUDGET=1.0]` prevents catastrophic drift. Pearl D smoothing further dampens.

**Risk 2: target ratio is wrong**. If CQL=2× IQN turns out to suppress IQN learning, magnitude differentiation may fail in different ways. Mitigation: target ratios are Invariant-1 anchors but the framework supports trivially changing them; smoke metrics will surface the issue at ep5.

**Risk 3: cold-start interaction with fold boundary**. The `cql_norm < EPS_DIV` check seeds with `COLD_START_FLOOR=0.02` — same as current behavior. If fold boundary lands a step where the gradient hasn't been computed yet (chunk boundaries), seed is benign. Mitigation: `EPS_DIV` is `1e-8`, well below any real gradient signal.

**Rollback**: revert the commit. Pearl 2's CQL/C51/ENS writes restored automatically (preserved as removed-then-re-added in single revert). No data migration concerns.

## Out-of-scope but related

- **C51 budget below floor mystery**: the `c51_budget_per_branch [dir=0.0500 …]` flat-at-floor observation suggests Pearl 2's flatness logic produces values below the floor. This is independent of SP7 and resolved automatically since SP7 (a) takes over `BUDGET_C51_BASE`, and (b) replaces the consumer floor with sentinel-aware bootstrap. (Pearl 2's flatness signal is preserved at `FLATNESS_BASE` for NoisyNet σ AND is now consumed by the SP7 controller as the target modulator.)
- **Per-branch grad_decomp**: extending `grad_component_delta_norm` from 3-slice (trunk/dir/mag) to 4-branch (trunk/dir/mag/ord/urg) would let SP7 use dedicated grad-norms for ord and urg branches instead of trunk fallback. Worth doing later as a generalization, but the current trunk fallback is the right MVP.
- **Ens budget**: SP7 takes over `BUDGET_ENS_BASE` via the sentinel-aware
  consumer change but the controller does not actively drive Ens (Ens
  budget stays at `ENS_BOOTSTRAP_BUDGET=0.02` because Ens has no clear
  ratio target relative to IQN at the current architecture). Future work
  if Ens contribution becomes load-bearing.

## Memory pearls created/updated

**NEW** `pearl_loss_balance_controller.md`:
> When two loss heads contribute to the same parameter buffer at structurally
> different magnitudes (e.g., CQL's softmax-of-logsumexp emits gradient on
> all actions × atoms while IQN only emits on the action taken), a budget
> floor in the consumer pins them apart from any controller's intent. The
> fix is a closed-loop ratio controller with two layers:
>
> 1. **Outer layer (signal-driven target)**: per-branch target ratio is
>    flatness-modulated — `target_cql/iqn = ANCHOR × (1 − flatness)` so
>    CQL turns off when Q is flat (it would only push uniform Q down
>    further); `target_c51/iqn = ANCHOR × flatness` so C51 turns off when
>    Q is sharp. Realizes `pearl_q_flatness_gates_conservatism` — flatness
>    gates conservative regularization.
> 2. **Inner layer (outcome-driven control)**: multiplicative correction
>    `new_budget = old × target/actual`, with Wiener-optimal α tracked in
>    dedicated per-branch EMA state slots, replacing the canonical fixed
>    α used in earlier controller pearls.
>
> The consumer floor must be sentinel-aware (`if raw < EPS_DIV use bootstrap
> else use raw verbatim`) — a hard floor that floors EVERY read pins the
> budget against the controller's intent. Generalizes
> `pearl_adaptive_moe_lambda` to multi-head loss balancing and lifts the
> α to Wiener-optimal per `pearl_wiener_optimal_adaptive_alpha`.

**Why**: future-me dispatching a similar adaptive-coefficient task should
reach for two-layer (signal-modulated target × outcome-driven controller)
designs when there's a known structural asymmetry between loss heads, AND
should grep for hard floors in consumers that would silently fight the
controller.

**How to apply**: when a new loss head is added to the training stack:
1. Decide whether it's a reference (drives own budget independently) or
   managed (driven by ratio to a reference).
2. Identify the structural signal that should modulate the target —
   flatness, regime_stability, gradient_disagreement, etc.
3. Add the controller's launch site after the head's `grad_decomp_launch_*`
   so the grad-norm is populated.
4. Verify the consumer-side budget read is sentinel-aware bootstrap, not
   hardcoded floor.
