# DQN Named Dimensions Registry

**Invariant 8 enforcement.** Every semantic index has a named constant. Raw indices (`[0]`, `[6]`, `[23]`) appear ONLY at definition sites. Consumer code always uses the named constant.

## State vector offsets

Defined in `crates/ml/src/cuda_pipeline/state_layout.cuh` and mirrored in Rust via `ml-core::state_layout`.

| Constant | Value | Meaning |
|---|---|---|
| `SL_STATE_DIM` | 112 | Total state vector length (D.6 Plan 2 Task 6A: was 104) |
| `SL_STATE_DIM_PADDED` | 128 | Padded for cuBLAS K-tile alignment |
| `SL_MARKET_DIM` | 42 | Market-feature block width |
| `SL_OFI_DIM` | 32 | OFI block width |
| `SL_MTF_DIM` | 16 | Multi-timeframe block width |
| `SL_PORTFOLIO_BASE_DIM` | 8 | Portfolio-base block width |
| `SL_PORTFOLIO_PLAN_DIM` | 7 | Plan-ISV block width (D.6 Plan 2 Task 6A: was 6) |
| `SL_MARKET_START` | 0 | |
| `SL_OFI_START` | 42 | |
| `SL_MTF_START` | 74 | |
| `SL_PORTFOLIO_START` | 90 | |
| `SL_PLAN_ISV_START` | 98 | |

## Portfolio state slots (ps[0..PS_STRIDE=38))

Defined in `crates/ml/src/cuda_pipeline/state_layout.cuh` (PS_* constants).
Consumer code uses the named constants; raw `ps[N]` access outside definition
sites is a lint violation (Invariant 8). Slots 3-6 and 22 also carry legacy
named aliases in `experience_kernels.cu` (DSR_A_SLOT etc.) retained for
in-file use; PS_* names are the canonical cross-file form.

| Index | Constant | Meaning |
|---|---|---|
| 0 | `PS_POSITION` | Current contract position (signed) |
| 1 | `PS_CASH` | Cash balance |
| 2 | `PS_PORTFOLIO_VALUE` | Mark-to-market total (cash + pos×price) |
| 3 | `PS_DSR_A` | EMA of realised trade returns (DSR) |
| 4 | `PS_DSR_B` | EMA of squared trade returns (DSR) |
| 5 | `PS_DSR_TRADE_COUNT` | Completed trades counter (DSR warmup) |
| 6 | `PS_PREV_CLOSE` | Prev bar raw_close for DSR per-bar returns |
| 7 | `PS_PEAK_EQUITY` | High-water mark (init to initial_capital) |
| 8 | `PS_FLAT_COUNTER` | Consecutive flat steps |
| 9 | `PS_PREV_EQUITY` | Equity at previous step |
| 10 | `PS_HOLD_TIME` | Consecutive steps with position |
| 11 | `PS_REALIZED_PNL` | Cumulative realised PnL |
| 12 | `PS_ENTRY_PRICE` | Price when trade was entered |
| 13 | `PS_TRADE_START_PNL` | Realised PnL snapshot at trade entry |
| 14 | `PS_KELLY_WIN_COUNT` | Kelly: number of profitable trade exits |
| 15 | `PS_KELLY_LOSS_COUNT` | Kelly: number of losing trade exits |
| 16 | `PS_KELLY_SUM_WINS` | Kelly: cumulative profit from winners |
| 17 | `PS_KELLY_SUM_LOSSES` | Kelly: cumulative \|loss\| from losers |
| 18 | `PS_KELLY_SUM_RETURNS` | Kelly: cumulative net returns (for mu) |
| 19 | `PS_KELLY_SUM_SQ_RETURNS` | Kelly: cumulative squared returns (sigma^2) |
| 20 | `PS_INTRA_TRADE_MAX_DD` | Worst intra-trade drawdown (v8 reward) |
| 21 | `PS_INTRA_TRADE_MAX_PNL` | Best intra-trade unrealised PnL (hindsight) |
| 22 | `PS_PREV_MID` | Prev bar MBP-10 mid-price |
| 23 | `PS_PLAN_TARGET_BARS` | plan: max hold bars (>0.5 = plan active) |
| 24 | `PS_PLAN_PROFIT_TARGET` | plan: raw profit threshold |
| 25 | `PS_PLAN_STOP_LOSS` | plan: raw stop threshold |
| 26 | `PS_PLAN_SCALE_AGGRESSION` | plan: position ramp speed |
| 27 | `PS_PLAN_CONVICTION` | plan: conviction at entry [0,1] |
| 28 | `PS_PLAN_ASYMMETRY` | plan: profit/stop asymmetry |
| 29 | `PS_PLAN_ENTRY_REGIME` | plan: regime_stability at entry |
| 30-37 | `PS_OFI_PREV_BASE + k` | Prev-bar OFI scratch for delta computation |
| 38 | `PS_STRIDE` | Total portfolio state stride (=PORTFOLIO_STRIDE) |

## Plan_isv dimensions (plan_isv[0..7))

D.6 Plan 2 Task 6A landed: 7th dimension `PLAN_ISV_REMAINING_FRACTION` added. These constants live alongside `SL_PLAN_ISV_START` in `state_layout.cuh`.

| Index | Constant | Meaning |
|---|---|---|
| 0 | `PLAN_ISV_PROGRESS` | hold_time / target_bars (clamped 2.0) |
| 1 | `PLAN_ISV_PNL_VS_TARGET` | unrealized / (target × equity) |
| 2 | `PLAN_ISV_PNL_VS_STOP` | -unrealized / (stop × equity) |
| 3 | `PLAN_ISV_ENTRY_CONVICTION` | conviction at entry |
| 4 | `PLAN_ISV_CONVICTION_DRIFT` | current conviction − entry conviction |
| 5 | `PLAN_ISV_REGIME_SHIFT` | \|isv[11] − entry_stability\| |
| 6 | `PLAN_ISV_REMAINING_FRACTION` | max(0, min(1, (target_bars − hold_time) / target_bars)) when plan active, else 0 — temporal pressure signal |

## Plan_params output (plan_params[0..6))

From the trade_plan MLP output.

| Index | Constant | Meaning |
|---|---|---|
| 0 | `PLAN_PARAM_TARGET_BARS` | |
| 1 | `PLAN_PARAM_PROFIT_TARGET` | |
| 2 | `PLAN_PARAM_STOP_LOSS` | |
| 3 | `PLAN_PARAM_SCALE_AGGRESSION` | |
| 4 | `PLAN_PARAM_CONVICTION` | |
| 5 | `PLAN_PARAM_ASYMMETRY` | |

## Branch indices (4-branch factored action)

| Index | Constant | Meaning |
|---|---|---|
| 0 | `BRANCH_DIR` | Direction branch (4 actions) |
| 1 | `BRANCH_MAG` | Magnitude branch (3 actions) |
| 2 | `BRANCH_ORD` | Order-type branch (3 actions) |
| 3 | `BRANCH_URG` | Urgency branch (3 actions) |

## Direction action sub-indices

| Index | Constant | Meaning |
|---|---|---|
| 0 | `DIR_SHORT` | Open short (or close long + open short) |
| 1 | `DIR_HOLD` | Keep current position (no-op on position) |
| 2 | `DIR_LONG` | Open long (or close short + open long) |
| 3 | `DIR_FLAT` | Close any position to zero |

## Magnitude action sub-indices

| Index | Constant | Meaning |
|---|---|---|
| 0 | `MAG_QUARTER` | 0.25× max_position |
| 1 | `MAG_HALF` | 0.50× max_position |
| 2 | `MAG_FULL` | 1.00× max_position |

## Branch FC input strides (direction- vs OFI-conditioned)

The four branch FC heads (`w_b{0,1,2,3}fc`) and their gate twins
(`w_gate_{0,1,2,3}`) have different input strides depending on what each
branch concatenates onto the trunk activation:

| Branch (d) | Constant role | Input stride | Conditioning |
|---|---|---|---|
| 0 (Direction) | trunk only | `shared_h2` | none |
| 1 (Magnitude) | mag_concat | `shared_h2 + branch_0_size` | direction-conditioned (concat of `h_s2` + per-direction Q values, 4 in production) |
| 2 (Order) | ord_concat | `shared_h2 + 3` | OFI-conditioned (concat of `vsn_masked` + 3 OFI features) |
| 3 (Urgency) | urg_concat | `shared_h2 + 3` | OFI-conditioned (concat of `vsn_masked` + 3 OFI features) |

**Invariant (off-by-one trap).** The `+3` constant is overloaded: in the OFI
branches it's a literal (3 features per branch from `concat_ofi_features`), but
in the magnitude branch it must derive from `branch_0_size` (since 4-direction
S/H/L/F was added). Buffers, weight tensors, GEMM cache shapes, accumulator
strides, and dX backward dims all share this contract — see commit fixing
mag_concat_qdir OOB (compute-sanitizer caught 1679 errors). Direction-conditioned
sites: `w_b1fc`, `w_gate_1`, `mag_concat_buf`, `d_mag_concat_buf`. OFI-conditioned
sites: `w_b{2,3}fc`, `w_gate_{2,3}`, `{ord,urg}_concat_buf`,
`d_{ord,urg}_concat_buf`.

## Commit history

- Task 4A (ps[0..PS_STRIDE) constants): commit 144c85b85
- Task 4B (plan_isv[0..6) constants): commit 741cb48d5
- Task 4C (plan_params[0..6) constants): commit 0ac83479e
- Task 4D (BRANCH_* constants): commit 11755632b
- Task 4E (DIR_* direction sub-indices): commit c64fde0be
- Task 4F (MAG_* magnitude sub-indices): commit 537e18b84
- Task 4 gap-fix (3 review issues): experience_kernels.cu lines 1148-1152 and 1194 missed by
  sed sweep (ps_base_plan+N syntax), replaced with PS_PLAN_TARGET_BARS/PS_POSITION/DIR_LONG/
  DIR_SHORT/DIR_FLAT; ml-core state_layout.rs Rust mirror added for all Task 4 constants
  (PS_*/PLAN_ISV_*/PLAN_PARAM_*/BRANCH_*/DIR_*/MAG_*, 46 constants total).
