Files
foxhunt/docs/superpowers
jgrusewski 69211af40b phase3(env-unification): full unification — all 3 kernels now call unified_env_step_core
Completes Phase 3 of the env-unification effort: experience_env_step (training),
backtest_env_step (val single-step), and backtest_env_step_batch (val batched)
all call the same __device__ helper `unified_env_step_core` in trade_physics.cuh.
Drift between train/val becomes structurally impossible at compile time — any
change to the canonical step applies atomically to both.

Three structural fixes were required to make the training port work:

1) TWO max_position params in unified_env_step_core
   Training scales `max_position` by Var[Q] (Kelly-from-atoms variance sizing)
   → `effective_max_pos`. The original training kernel used this for
   compute_target_position_4branch but the UNSCALED `max_position` for
   apply_kelly_cap and execute_trade. Initial port collapsed both into one
   helper arg, tightening Kelly cap aggressively → tiny positions → Q-values
   converge → q_gap=0.00 collapse. Split into `max_position_target` (scaled)
   and `max_position_physics` (unscaled). Val passes the same value for both.

2) Remove double hold_time update
   Helper now owns `update_hold_time(...)` inside the step. Training's
   inline `hold_time = update_hold_time(...)` at line ~1574 was a double-
   increment. Fix: capture `saved_hold_time` BEFORE helper for the
   patience-multiplier in segment reward, then trust the helper's in-place
   update of hold_time. Segment-hold-time semantics preserved.

3) Remove triple Kelly-stat update
   Helper calls `record_kelly_trade_outcome(...)`. Training ALSO had Kelly
   stat updates inside the reversing_trade block (line 1558) and the
   exiting_trade block (line 1638). TRIPLE update → win_count/loss_count/
   sum_wins/sum_losses inflated 3× → Kelly cap tightened proportionally →
   positions starved → Q-gap collapse. Fix: only the helper now touches
   Kelly win/loss/sum_wins/sum_losses. Training still updates its own
   sum_returns / sum_sq_returns (continuous-Kelly stats, distinct
   buffers not touched by the helper).

Smoke test result (RTX 3050 Ti, 20 epochs, single-trial):

  Before port:      Best Sharpe ~15-25  (varies, range 10-31)
  After broken port: Best Sharpe 1.17    (q_gap=0.00 collapse)
  After fix:        Best Sharpe 39.25   (HIGHEST ever recorded)
                    q_gap 0.00 → 0.27 (growing, healthy separation)
                    sharpe_ema trajectory: -5.3 → 12.7 → 26.9 (rising)

Multi-trial (partial visible): q_gap rising 0.00 → 0.86 by epoch 6,
sharpe_ema 15-25 consistently positive. Both tests passed.

Training kernel now has the CORE STEP in a single helper call — the
kernel body downstream continues to handle training-only concerns
(counterfactuals, plan_params, reward shaping via shaping_scale,
saboteur via exploration_scale, replay buffer writes).

Net −99 LOC across three kernels replaced by shared helper calls.

Files touched:
  crates/ml/src/cuda_pipeline/trade_physics.cuh      (+15/-5)
  crates/ml/src/cuda_pipeline/backtest_env_kernel.cu (+53/-108 net)
  crates/ml/src/cuda_pipeline/experience_kernels.cu  (+48/-102 net)
  docs/superpowers/specs/2026-04-21-unified-train-val-env-design.md
    (Phase 2 documented as helper-extraction; Phase 3 kernel-deletion
    remains deferred — both kernel entry points kept for their distinct
    threading models; the physics is what's now truly unified.)

Verified: cargo check + smoke test pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 11:04:51 +02:00
..