From 237b3dbfb0535ca21ccfc186038f067f48611732 Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Sun, 3 May 2026 12:45:59 +0200 Subject: [PATCH] =?UTF-8?q?fix(sp7):=20bump=20ISV=5FTOTAL=5FDIM=20294?= =?UTF-8?q?=E2=86=92321=20to=20match=20SP5=5FSLOT=5FEND?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SP7 T1 added 24 ISV slots (LB_DIFF_VAR_CQL_BASE=297 ... LB_C51_ACTIVE through 321) without bumping ISV_TOTAL_DIM, leaving SP7's wiener-state and activation slots out-of-bounds of the allocated pinned buffer (294 * 4 = 1176 bytes; SP7 slots write to bytes 1188..1284). GPU direct-pointer writes silently corrupted memory in the next page; CPU read_isv_signal_at returned garbage in release builds. This explains the SP7 smoke's confusing zero-budget-everywhere observation: the activation flag was reading OOB memory, the consumer saw inconsistent values, and the controller's actual ISV state was never visible. Bumped ISV_TOTAL_DIM to 321 (max valid index 320, covering SP5_SLOT_END-1). Made pub(crate) so the new contract test can reference it from sp5_isv_slots.rs. Updated layout_fingerprint_seed()'s slot entries to include the previously missing D3 and SP7 slots (TRAINING_SHARPE_EMA=294 through LB_C51_ACTIVE_BASE=317) and updated ISV_TOTAL_DIM= literal to 321 in lockstep per feedback_no_partial_refactor. Added contract test all_sp5_slots_fit_within_isv_total_dim to permanently gate this class of bug at cargo test time. The test would have caught SP7 T1 instantly; future slot allocations cannot regress this. Files: gpu_dqn_trainer.rs (ISV_TOTAL_DIM + comment + fingerprint), sp5_isv_slots.rs (test), docs/dqn-wire-up-audit.md (Fix 31 sub-bullet). Cargo check workspace clean. Cargo test ml --lib 935 passed (934 baseline + 1 new contract test); 16 pre-existing GPU-hardware failures unchanged. Co-Authored-By: Claude Sonnet 4.6 --- .../ml/src/cuda_pipeline/gpu_dqn_trainer.rs | 8 +++++-- crates/ml/src/cuda_pipeline/sp5_isv_slots.rs | 11 ++++++++++ docs/dqn-wire-up-audit.md | 21 +++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) 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.