Files
foxhunt/docs/superpowers/plans/2026-06-02-next-session-multi-head-implementation.md
jgrusewski 629ebd667c feat(ml-alpha): deterministic same-seed training + Tier 1.5 fast-dev-cycle
Two same-seed runs now produce bit-equal eval_summary.json, alpha_rl_train_summary.json,
and diag.jsonl (modulo wall-clock elapsed_s). The 5-phase falsification chain landed:

  Phase 2   PER tree-rebuild: __threadfence is NOT a grid-wide barrier; multiple blocks
            raced across sum-tree levels. Fix: Grid=(1) Block=(1024) + __syncthreads
            in rl_per_tree_rebuild.cu.

  Phase 2.3 cuBLAS GEMM_DFALT + TF32 default-math allowed split-K non-deterministic
            accumulation at 3 sites. New crates/ml-alpha/src/cublas_determinism.rs
            applies CUBLAS_PEDANTIC_MATH via FOXHUNT_DETERMINISTIC env toggle
            (0=TF32 prod, 1=PEDANTIC dev default, 2=DEFAULT_MATH control).

  Phase 2.6 Two bugs surfaced sequentially in the backward kernel chain:
            (1) rl_iqn_tau_cos_features had a multi-block r/w race on prng_state[batch]
                — all N_TAU=32 blocks read seed; only tau_idx==0 wrote back; no
                inter-block barrier. Fix: split into READ-ONLY rl_iqn_tau_cos_features
                + new sibling rl_iqn_advance_prng_state launched on same stream
                (kernel-launch ordering = grid-wide barrier).
            (2) OutcomeHead::new called near_zero_xavier without scoped_init_seed,
                falling back to time+thread-id RNG. Stayed dormant until first done
                event activated non-sentinel labels and divergent weights flowed via
                grad_h_t_outcome into encoder gradient. Fix: add seed param + install
                scoped_init_seed(dqn_seed.wrapping_add(0x0CE0)) guard.

Validation (./scripts/determinism-check.sh --quick, RTX 3050, b=128, 200+50 steps):
  - All 200 rows of checksums.* leaves match (rel-tol 1e-5, abs-tol 1e-7)
  - eval_summary.json, alpha_rl_train_summary.json byte-equal between runs
  - diag.jsonl byte-equal modulo elapsed_s
  - Eval pnl identical run-A vs run-B at seed 42

Pre-fix baseline (Phase 2.5 measurement): same-seed eval pnl spread $450k
($187k vs -$261k). Post-fix: $0 spread.

Speed cost: ~1.5ms/step amortised; ~10-15% slower than TF32 production
(PEDANTIC tax — acceptable in dev, toggle to FOXHUNT_DETERMINISTIC=0 for prod).

Mapped-pinned discipline: all 11 NEW memcpy_dtoh sites in diagnostic dump methods
+ per-step checksum readback use a new pub(crate) helper
read_slice_d_into<T: Copy>(stream, src, dst) — MappedRecordBuffer + raw
memcpy_dtod_async + raw_stream_sync + volatile read. Generic over T (f32, f64,
i32, u32, u8). Satisfies feedback_no_htod_htoh_only_mapped_pinned + hook guard.

Bundled Tier 1.5 fast-dev-cycle infrastructure (spec
docs/superpowers/specs/2026-06-02-fast-dev-cycle.md):
  - scripts/local-mid-smoke.sh        b=128, 2000+500, ~10min on RTX 3050
  - scripts/determinism-check.sh      runs mid-smoke twice, diffs checksums
  - scripts/tier1_5_verdict.py        behavioral kill verdict
  - AdamW checkpoint save/load (crates/ml-alpha/src/trainer/optim.rs)
  - IntegratedTrainer checkpoint save/load (resume from checkpoint)
  - 15 Phase 1 checksum leaves in build_diag_value
  - Env-gated dump methods (FOXHUNT_DETERMINISM_DEBUG_PER/MAMBA2/RL/BACKWARD)
    for future divergence-chasing — never run in production

Documentation:
  - docs/superpowers/specs/2026-06-02-determinism-foundation.md
  - docs/superpowers/specs/2026-06-02-fast-dev-cycle.md
  - docs/superpowers/plans/2026-06-02-determinism-foundation-implementation.md
  - docs/superpowers/notes/2026-06-02-determinism-phase{1,2,2.2,2.5,2.6}-*.md
  - Adjacent specs/plans/notes from the analytical chain that surfaced determinism
    as the load-bearing blocker (eval-summary, eval-boundary, regime-observer,
    multi-head policy, regime-invariance, Phase 3 IQN-complement post-mortem)

Unlocks: every controller / architecture / reward-shaping A/B from this commit
onward attributes outcome differences to the change, not random-init kernel-race
drift cascading through training x eval LOB-sim trajectories. The eval-collapse
investigation (pearl_reward_signal_anti_aligned_with_pnl, multi-head spec,
regime-invariance spec) is now testable with trustworthy verdicts.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 17:56:00 +02:00

10 KiB
Raw Blame History

Next Session — Multi-Head Policy Implementation Plan

Date: 2026-06-02 (handoff document) Status: Ready-to-execute plan for next session Linked specs:

  • Primary: docs/superpowers/specs/2026-06-02-multi-head-policy-with-r-multiple.md
  • Parent: docs/superpowers/specs/2026-06-02-regime-invariance-four-interventions.md
  • Existing #1 plan: docs/superpowers/plans/2026-06-02-regime-invariance-implementation.md Branch at handoff: ml-alpha-regime-observer at SHA 63fc16f17 (v5.2) Memory snapshot: pearl_grwwh_eval_catastrophic_collapse (just saved)

§0. State at handoff

alpha-rl-grwwh verdict (just landed):

  • train +$481M wr 0.312 hold 15.1 — strong train signal
  • eval $160.5M wr 0.264 pf 0.83 sharpe 1.70 — catastrophic generalization failure
  • 3rd consecutive eval-collapse on single-policy + Phase 5 hold-bonus architecture

Empirically validated: the scaffold-fade approach (v4/v5/v5.1/v5.2) cannot fix the eval generalization gap because the gap is architectural — single policy network with Phase 5 reward shape over-fits to train regime.

Direction confirmed: ship multi-head policy + R-multiple reward together per the spec.

§1. Pre-flight before any code

§1.1 sp-critical-reviewer on the multi-head spec FIRST

The spec at 2026-06-02-multi-head-policy-with-r-multiple.md has been written with the FIVE BLOCKERs from the previous v6 spec review explicitly addressed. Before any implementation:

  1. Dispatch sp-critical-reviewer agent on the multi-head spec
  2. Reviewer should especially verify:
    • PolicyHead struct layout in crates/ml-alpha/src/networks/policy_head.rs (referenced but not personally verified in spec §9)
    • EXPECTED_LEAVES = 679 value referenced in §3.4 still matches current test
    • No prior MoE attempts in foxhunt (grep for existing multi-head code)
    • The aux-prior-loss math (§1.4) doesn't break PPO surrogate convexity
  3. If reviewer BLOCKS, fix and re-review BEFORE implementation
  4. If reviewer FIX-AND-PROCEED, apply edits then start §2

§1.2 Verify the per-step diag / eval_summary discrepancy bug

Per pearl_grwwh_eval_catastrophic_collapse: at eval step 4863 the per-step diag showed +$74M but eval_summary showed $160M. This is a $234M discrepancy. Investigate BEFORE shipping a new run that relies on per-step diag for verdict tracking:

  1. Pull diag.jsonl rows 22000-22826 from PVC for grwwh (/feature-cache/alpha-rl-runs/63fc16f17/fold1/diag.jsonl)
  2. Check if the last ~137 eval steps show a catastrophic per-step pnl collapse — if YES, per-step is honest, the agent really did crash at the end
  3. If NO collapse visible in per-step → the per-step diag's realized_pnl_cum_usd accumulator differs from eval_summary's total_pnl_usd calculation — file a separate bug/diagnostic spec

This investigation gates whether per-step diag can be trusted for kill-criteria in §3.1 falsification gates. The reviewer-mandated kill gates rely on per-step signal being authoritative.

§2. Decide bundling: ship #1 + multi-head together OR sequentially

Option A: Bundle (single commit, v6.0)

  • R-multiple reward + multi-head policy in one commit
  • Faster to verdict (one cluster smoke instead of two)
  • Higher risk: if eval fails, can't isolate which intervention failed
  • Aligns with the §0.5 finding that #1 alone is insufficient

Option B: Sequential (#1 first, then multi-head)

  • Ship #1 R-multiple first, get cluster verdict
  • IF #1 eval positive: stop (cheapest win)
  • IF #1 eval negative: ship multi-head on top
  • More cluster smokes burned
  • Cleaner attribution

Recommendation: Option A (bundle)

The grwwh result rules out #1-alone as sufficient. v5.2 with Phase 5 still active failed eval; #1 just removes the contamination (R-multiple replaces hold-bonus). A clean single policy on R-multiple alone would likely still over-fit to fold-1 trend regime — same failure mode, different reward shape.

Multi-head is the architectural fix. Bundle.

§3. Implementation ordering (assuming Option A bundle)

Phase A: Foundation (sequential, atomic commit)

  1. Slot retire + reallocate (crates/ml-alpha/src/rl/isv_slots.rs)

    • Delete: RL_SURFER_SCAFFOLD_WEIGHT_INDEX (753), RL_SURFER_BREAK_EVEN_WR_INDEX (754), RL_SURFER_K_SHARPNESS_INDEX (755), RL_SURFER_WARMUP_TRADES_INDEX (756)
    • Allocate (R-multiple, per existing #1 plan): RL_ATR_EMA_INDEX (753), RL_R_MULTIPLE_CLAMP (754), RL_ATR_EMA_ALPHA (755), RL_ATR_FLOOR (756)
    • Allocate (multi-head, per multi-head spec §2): RL_POLICY_NUM_HEADS_INDEX (757), RL_POLICY_GATING_ENTROPY_FLOOR_INDEX (758), RL_POLICY_HEAD_ENTROPY_FLOOR_INDEX (759), RL_POLICY_AUX_PRIOR_BETA_INDEX (760)
    • RL_SLOTS_END = 761
  2. Delete obsolete code

    • Delete: crates/ml-alpha/cuda/rl_surfer_scaffold_controller.cu
    • Delete: surfer-scaffold launch + module load in crates/ml-alpha/src/trainer/integrated.rs (6 touchpoints per coder analysis)
    • Delete: surfer-scaffold bootstrap entries in with_controllers_bootstrapped
    • Delete: surfer-scaffold registration in crates/ml-alpha/build.rs
  3. New kernels (5 total)

    • rl_atr_ema_update.cu — ATR EMA producer (R-multiple input)
    • rl_policy_mixture_forward.cu — K head logits + gate → mixture logits via LogSumExp
    • rl_policy_mixture_backward.cu — backward through mixture
    • rl_policy_aux_prior_loss.cu — Σ_k g_k × β × KL(p_k ∥ p_prior_k) + gradient
    • Modify: rl_fused_reward_pipeline.cu Phase 5 — replace hold-bonus with r = clamp(Δpnl / max(atr_at_entry, floor), ±5)

Phase B: Rust integration

  1. Network refactor (crates/ml-alpha/src/networks/policy_head.rs)

    • PolicyHead struct: single Linear → Vec for K heads + Linear for gating
    • Adam optimizer states per tensor
    • Forward pass through new mixture kernels
    • Backward pass with per-head + gate gradients
  2. Trainer integration (crates/ml-alpha/src/trainer/integrated.rs)

    • Allocate K head weights at construction (init with action-bias per spec §1.3)
    • Allocate gating weights
    • Add unit_atr_at_entry_d per-batch buffer (R-multiple capture)
    • Wire 5 new kernel launches in step_with_lobsim
    • Bootstrap new ISV slots (β=0.05, entropy floors per spec)

Phase C: Diag + tests

  1. Diag emission (build_diag_value)

    • policy.head_count = K
    • policy.head_k_entropy[k] for k in 0..K
    • policy.gate_entropy
    • policy.gate_probs[k] for k in 0..K
    • policy.head_pairwise_kl[i,j] for diagnostic
    • rewards.atr_ema_usd
    • rewards.r_multiple_last
    • EXPECTED_LEAVES: 679 → 679 + 7 (multi-head) + 2 (R-multiple) 1 (drop pure_pnl_mode/surfer_scaffold_weight) = 687
  2. Test migration

    • tests/reward_alignment_invariants.rs: I1-I3 → assert rewards.atr_ema_usd ≥ 0 and rewards.r_multiple_last ∈ [-5, +5]. Delete surfer_scaffold_weight assertions.
    • New test: tests/multi_head_policy_invariants.rs
      • I1: gate_probs[k] in [0, 1] for all k, Σ = 1
      • I2: gate_entropy ≥ entropy_floor (with epsilon)
      • I3: head_pairwise_kl > 0 (at least one pair distinct)
      • I4: each head's entropy ≥ head_entropy_floor

Phase D: Local validation + cluster

  1. Local validation

    • cargo check + cargo build --release
    • cargo test reward_alignment_invariants multi_head_policy_invariants --ignored --nocapture
    • 200-step local smoke (matches existing test pattern)
    • GPU compute-sanitizer racecheck on the new kernels
  2. Cluster smoke

    • Pin SHA via argo-alpha-rl.sh --sha
    • L40S, fold-1, b=1024, 20k train + 5k eval
    • Apply §4.1 per-step kill criteria from multi-head spec
    • Apply §4.2 verdict gates

§4. Failure-mode adaptive replanning

If multi-head + R-multiple cluster smoke ALSO produces negative eval:

Diagnostic step 1: per-regime eval stratification per spec §4.3. If specialization spread < 0.02 → heads didn't diverge → aux prior β too low. Boost β to 0.15 and retry.

Diagnostic step 2: if specialization shows but eval still negative → the heads' priors are wrong (e.g., trend+reversion+flat doesn't cover the actual eval-period regime). Try different prior set (e.g., add momentum-breakout head).

Diagnostic step 3: if priors are diverse and eval still negative → architectural ceiling hit. Pivot to:

  • Cross-fold training (#3 from parent spec) — train on mixed folds 0/1/2
  • Regime features wired into encoder (#2 from parent spec)
  • Behavior cloning warm-start from a profitable rule-based bot

Termination: if 3 multi-head variants all fail eval, the conclusion is the architecture cannot extract alpha from foxhunt's MBP-10 dataset at b=1024 with 20k step budget. Different data scale or different model architecture required.

§5. Discipline reminders (DO NOT VIOLATE)

  • feedback_no_feature_flags — surfer-scaffold deletion in SAME commit as new code, no toggles
  • feedback_single_source_of_truth_no_duplicates — slot reuse 753-756 with rename, no v2_ suffixes
  • feedback_local_smoke_before_cluster — Phase D step 8 BEFORE step 9, no exceptions
  • feedback_kill_runs_on_anomaly_quickly — kill at first anomaly per spec §4.1, don't wait for verdict
  • feedback_no_atomicadd — all new kernels use block-tree-reduce
  • pearl_argo_branch_ref_trap_strikes_again — cluster command pins exact SHA, NOT branch tip
  • feedback_going_in_circles_pattern — if 2 multi-head variants in a row fail with new failure modes, STOP iterating and reconsider foundation

§6. Session opening checklist (for the next session)

  1. Read pearl_grwwh_eval_catastrophic_collapse (just saved)
  2. Read docs/superpowers/specs/2026-06-02-multi-head-policy-with-r-multiple.md
  3. Read docs/superpowers/specs/2026-06-02-regime-invariance-four-interventions.md (parent)
  4. Read this plan
  5. Dispatch sp-critical-reviewer on multi-head spec (per §1.1)
  6. Resolve reviewer findings
  7. Investigate per-step / eval_summary discrepancy (per §1.2)
  8. Decide bundling (per §2) — likely Option A
  9. Begin Phase A implementation (per §3)

Total estimated session time: 4-6 hours for full bundle, cluster smoke runs overnight if needed.