The eval-mode policy was producing diverse Boltzmann picks (verified by the
new `val_dir_dist` HEALTH_DIAG line) but every active-direction pick (Long /
Short) was collapsing to `actual_dir = Flat` because the Kelly cap forced
`target_position = 0` at cold start.
Cluster run `train-multi-seed-ddrpr` epoch 0 made this unambiguous:
val_dir_dist [short=0.0000 hold=0.1953 long=0.0001 flat=0.8047]
Boltzmann fired correctly (sum hold + flat ≈ 100% of bars, with Hold ~ 20% =
the share of bars where the policy explicitly picked Hold; the other 80%
were active-direction picks all rerouted to Flat by `target_position = 0`).
Root cause in `trade_physics.cuh::kelly_position_cap`:
warmup_floor = clamp(conviction, 0, 1) // ← can hit 0
effective_kelly = maturity*kelly_f + (1-maturity)*warmup_floor
= 0 + 1*0 = 0 at cold start with low conviction
cap = effective_kelly * max_position * safety = 0 → no exposure permitted
The `safety_multiplier` was already protected by a `health_safety = 0.5 + 0.5×h`
floor, but `warmup_floor` had no such floor. Catch-22: low conviction → cap=0 →
no trades → Kelly stats stay cold → conviction stays low → forever.
The same bootstrap-deadlock pattern as the IQN trunk SAXPY readiness gate
(commit f86353840), and the fix is structurally identical — apply a non-zero
adaptive floor sourced from the same training-stability signal:
warmup_floor = max(conviction, health_floor)
where `health_floor = 0.5 + 0.5 × ISV[LEARNING_HEALTH]` is the same value the
caller already computes for `safety_multiplier`. Both signals are adaptive
and ISV-driven; no tuned constants. The floor only matters during cold start
— once `maturity → 1` after ≥10 trades the term drops out entirely.
Threaded through both `apply_kelly_cap` and `kelly_position_cap` signatures;
single caller in `unified_env_step_core` passes `health_safety` as the new
arg (already locally computed two lines above). Build clean at 11-warning
baseline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>