diff --git a/crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs b/crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs index ed4f7bffc..b61844e31 100644 --- a/crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs +++ b/crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs @@ -825,7 +825,7 @@ const ISV_NETWORK_DIM: usize = 23; /// (shifted 112→116 in Plan 4 Task 6 Commit A). /// Written by the constructor; checked at checkpoint load. Fail-fast only — no migration /// path exists. See spec §4.A.2 and `LAYOUT_FINGERPRINT_CURRENT` for structural-hash rationale. -const ISV_TOTAL_DIM: usize = 294; // SP5 + Layer D D1+D2: 173 + 121 (118 SP5 slots @ 174..278/280..294, with 2-slot gap before cross-fold-persistent Kelly block; Layer D D1 PnL outputs at [286..290); Layer D D2 health composition outputs at [290..294)) +pub(crate) const ISV_TOTAL_DIM: usize = 321; // SP5 + Layer D D1+D2+D3 + SP7: 173 + 148 (145 SP5 slots @ 174..278/280..321, with 2-slot gap before cross-fold-persistent Kelly block; Layer D D1 PnL outputs at [286..290); Layer D D2 health composition outputs at [290..294); Layer D D3 training metrics EMA at [294..297); SP7 T1 loss-balance Wiener stats at [297..313); SP7 activation-flag fix per-(head,branch) flags at [313..321)) /// Legacy alias preserved for call sites that haven't been audited for the /// network-vs-total split. New code should pick `ISV_NETWORK_DIM` (for weight /// tensor sizing) or `ISV_TOTAL_DIM` (for the broadcast bus buffer). @@ -1699,7 +1699,11 @@ const fn layout_fingerprint_seed() -> &'static [u8] { KELLY_SAMPLE_COUNT=283;WIN_RATE_SMOOTH=284;LOSS_RATE_SMOOTH=285;\ PNL_TOTAL=286;PNL_MEAN=287;PNL_VAR=288;PNL_MAX_DD=289;\ HEALTH_SCORE=290;Q_GAP_NORM=291;Q_VAR_NORM=292;GRAD_NORM_NORM=293;\ - ISV_TOTAL_DIM=294;\ + TRAINING_SHARPE_EMA=294;MAX_DD_EMA=295;LOW_DD_RATIO=296;\ + LB_DIFF_VAR_CQL_BASE=297;LB_SAMPLE_VAR_CQL_BASE=301;\ + LB_DIFF_VAR_C51_BASE=305;LB_SAMPLE_VAR_C51_BASE=309;\ + LB_CQL_ACTIVE_BASE=313;LB_C51_ACTIVE_BASE=317;\ + ISV_TOTAL_DIM=321;\ PARAM_W_A_H_S1=0;PARAM_B_A_H_S1=1;PARAM_W_B_H_S1=2;PARAM_B_B_H_S1=3;\ PARAM_W_RESIDUAL_H_S1=4;PARAM_GAMMA_H_S1=5;PARAM_BETA_H_S1=6;\ PARAM_W_A_H_S2=7;PARAM_B_A_H_S2=8;PARAM_W_B_H_S2=9;PARAM_B_B_H_S2=10;\ diff --git a/crates/ml/src/cuda_pipeline/sp5_isv_slots.rs b/crates/ml/src/cuda_pipeline/sp5_isv_slots.rs index 178b4c511..829565a01 100644 --- a/crates/ml/src/cuda_pipeline/sp5_isv_slots.rs +++ b/crates/ml/src/cuda_pipeline/sp5_isv_slots.rs @@ -506,4 +506,15 @@ mod tests { assert_eq!(SP5_SLOT_END, 321); assert_eq!(SP5_PRODUCER_COUNT, SP5_SLOT_END - SP5_SLOT_BASE); } + + #[test] + fn all_sp5_slots_fit_within_isv_total_dim() { + use crate::cuda_pipeline::gpu_dqn_trainer::ISV_TOTAL_DIM; + assert!( + SP5_SLOT_END <= ISV_TOTAL_DIM, + "SP5_SLOT_END={} exceeds ISV_TOTAL_DIM={} — bus too small for SP5/SP7 slots; \ + bump ISV_TOTAL_DIM in gpu_dqn_trainer.rs (and update layout_fingerprint_seed()).", + SP5_SLOT_END, ISV_TOTAL_DIM, + ); + } } diff --git a/docs/dqn-wire-up-audit.md b/docs/dqn-wire-up-audit.md index 7481ee9e7..735e3c71b 100644 --- a/docs/dqn-wire-up-audit.md +++ b/docs/dqn-wire-up-audit.md @@ -3995,6 +3995,27 @@ extended. No producer kernel yet — that arrives in the next commit. gate (was_active=1, grads back to 0) → flags hold at 1, prior budget held verbatim. No CPU reference oracle per `feedback_no_cpu_test_fallbacks.md`. Passes on local RTX 3050 Ti. +- ISV_TOTAL_DIM OOB fix (commit on wt/sp7-observability, 2026-05-03): + **Discovery**: `ISV_TOTAL_DIM=294` but `SP5_SLOT_END=321`; the pinned + ISV buffer was allocated at `294×4=1176 bytes`. SP7 T1 added 24 slots + (ISV[297..321)) and the activation-flag fix added 8 more (ISV[313..321)) + without bumping `ISV_TOTAL_DIM`. All SP7 ISV writes/reads from indices + 297..320 were out-of-bounds: GPU direct-pointer writes silently + corrupted memory in the next page-aligned region; CPU + `write_isv_signal_at` silently no-oped for `index >= ISV_TOTAL_DIM`; + CPU `read_isv_signal_at` returned garbage in release builds. + **Root cause**: T1 allocated slots in `sp5_isv_slots.rs` but did not + bump `ISV_TOTAL_DIM` in `gpu_dqn_trainer.rs`; the constant lived in a + different file with no compile-time linkage to `SP5_SLOT_END`. + **Fix**: `ISV_TOTAL_DIM` bumped `294→321` and made `pub(crate)`; + `layout_fingerprint_seed()` extended with the missing D3 and SP7 slot + entries (TRAINING_SHARPE_EMA=294 through LB_C51_ACTIVE_BASE=317) and + `ISV_TOTAL_DIM=321` in lockstep per `feedback_no_partial_refactor`. + **Contract test**: `all_sp5_slots_fit_within_isv_total_dim` added to + `sp5_isv_slots.rs` test module; asserts `SP5_SLOT_END <= ISV_TOTAL_DIM` + at `cargo test -p ml --lib`. This test would have caught the SP7 T1 + miss instantly; future slot allocations cannot silently regress the bus + size without breaking CI. - T8 (out-of-tree): memory pearl `pearl_loss_balance_controller.md` + MEMORY.md index entry. Captures the two-layer (signal-modulated target × outcome-driven α) pattern for future controller designs.