feat(sp18 v2 P2.T1-T5): structural Hold opportunity-cost device fn + 3-site migration (INTERIM STATE LIFTED)

Atomic introduction of `compute_sp18_hold_opportunity_cost` device function
in `trade_physics.cuh` + migration of the 3 placeholder-commented per-bar
Hold sites in `experience_kernels.cu` + CPU/GPU oracle tests + test-wrapper
cubin manifest entry — single atomic commit per `feedback_no_partial_refactor`.

Closes the SP18 INTERIM STATE introduced by Phase 1 (3c318953a). The reward
function is now structurally complete: no placeholder sites, no orphaned
consumers. Wire-up audit fingerprint stable; production-call count for the
new device fn = 3 (segment_complete + per-bar positioned-Hold + per-bar
flat-Hold), test-wrapper count = 1, definition count = 1.

Sites:
- trade_physics.cuh: new device fn after `compute_lump_sum_opp_cost`.
  Returns 0 for `dir_idx != DIR_HOLD`. Sign convention DD5(b) MIRRORED:
  positive next_log_return → negative Hold reward (opp cost paid for
  sitting out a winner); negative next_log_return → positive Hold reward
  (correctly avoided a loser). Asymmetric-capped via the existing
  `compute_asymmetric_capped_pnl` primitive against ISV[483]/[484]; output
  multiplied by `shaping_scale` (validation = 0 → returns 0). No new
  infrastructure beyond existing primitives.
- experience_kernels.cu Site 1 (segment_complete ~3143): unconditional
  add into `base_reward` BEFORE the SP12 asymmetric cap; preserves SP12
  cap interaction.
- experience_kernels.cu Site 2 (per-bar positioned-Hold ~3641):
  unconditional add into `r_micro` (rc[3]) — same component the deleted
  SP13/SP16 subtraction wrote to, preserving SP11 reward-subsystem
  controller signal attribution. `vol_proxy` recomputed locally from
  `features[bar_idx * market_dim + 9]` (per-bar branches are outside
  the segment_complete `vol_proxy` scope).
- experience_kernels.cu Site 3 (per-bar flat-Hold ~3760): unconditional
  add into `r_opp_cost` (rc[4]) — same component the deleted subtraction
  wrote to. `vol_proxy` recomputed locally with the same pattern.

Each site reads ISV[HOLD_REWARD_POS_CAP_INDEX=483] /
ISV[HOLD_REWARD_NEG_CAP_INDEX=484] with sentinel-or-out-of-bounds guard
falling through to SENTINEL_HOLD_REWARD_POS/NEG_CAP=+5/-10 macros. Bit-
identical to a fixed +5/-10 cap during cold-start and Pre-Phase-3 (Phase 3
lands the producer kernel `hold_reward_cap_update_kernel`).

Tests:
- T1 CPU oracle (sp18_hold_reward_oracle_tests.rs): 6 cases covering
  positive/negative log-return, dir-gate (early-exit for non-HOLD),
  positive saturation (clamps at +pos_cap), negative saturation (clamps
  at -neg_cap), shaping_scale=0 validation mode (returns 0).
- T2 GPU oracle (sp18_hold_reward_oracle_tests.rs::gpu): same 6 cases via
  `sp18_hold_opp_test_kernel.cu` wrapper; bit-equality to CPU at ε=1e-5.
- sp18_hold_opp_test_kernel.cu: single-thread single-block test wrapper
  mirroring `sp12_reward_math_test_kernel.cu` structure. Mapped-pinned
  f32 input/output per `feedback_no_htod_htoh_only_mapped_pinned`;
  __threadfence_system() for PCIe visibility before host read.
- build.rs: cubin manifest entry for sp18_hold_opp_test_kernel.cu.

Validation:
- SQLX_OFFLINE=true CUDA_COMPUTE_CAP=86 cargo check --workspace clean.
- cargo test -p ml --lib sp18: 4 SP18 lib tests pass (slot layouts,
  fold-reset entries-present).
- cargo test -p ml --lib state_reset_registry: 5 tests pass (including
  every_fold_and_soft_reset_entry_has_dispatch_arm).
- cargo test -p ml --test sp18_hold_reward_oracle_tests --features cuda
  -- --include-ignored: 7 tests pass (2 CPU + 5 GPU on local RTX 3050 Ti),
  including the new compute_sp18_hold_opportunity_cost_cpu_oracle and
  compute_sp18_hold_opportunity_cost_gpu_oracle.
- bash scripts/audit_sp18_consumers.sh --check: exit 0 (post-Phase-2
  fingerprint snapshotted in docs/sp18-wireup-audit.md).
- Wire-up audit: `grep -rn "compute_sp18_hold_opportunity_cost" crates/ml/`
  shows 1 device-fn definition + 3 production call sites + 1 test-wrapper
  kernel + CPU/GPU oracle tests. No orphans.

Audit fingerprint diff (Phase 1 → Phase 2):
- `Slot 380 (HOLD_COST_INDEX) consumers`: experience_kernels.cu hit count
  1 → 0 (placeholder-comment reference to HOLD_COST_INDEX removed; new
  Phase 2 calls reference HOLD_REWARD_POS/NEG_CAP_INDEX=483/484 instead);
  doc references in dqn-wire-up-audit.md grew by 2 lines (Phase 2
  close-out narrative). Total section count 42 → 43.
- All other sections unchanged — Phase 2 introduces forward-tracking
  references on the SP18-bound side (slots 483/484, new device fn), not
  the deleted-chain side.

Discipline:
- feedback_no_partial_refactor: device fn + 3 call sites + tests + cubin
  manifest in same commit; INTERIM STATE LIFTED in same commit.
- feedback_no_atomicadd: device fn is per-thread sample-local register
  arithmetic; test wrapper is single-thread single-block, no shared mem.
- feedback_no_htod_htoh_only_mapped_pinned: GPU oracle test uses
  MappedF32Buffer.
- feedback_isv_for_adaptive_bounds: pos/neg caps read from ISV[483]/[484]
  with sentinel fallback; Phase 3 producer `hold_reward_cap_update_kernel`
  drives them adaptively.
- feedback_wire_everything_up: device fn + 3 call sites + test wrapper +
  CPU/GPU oracle tests + cubin manifest all in same commit.
- pearl_one_unbounded_signal_per_reward: only `vol_normalized_return` is
  unbounded; everything else in [neg_cap, pos_cap] × shaping_scale.
- pearl_first_observation_bootstrap: sentinels (5.0/-10.0) match SP14
  P0-A REWARD_POS/NEG_CAP_ADAPTIVE for bit-identical cold-start.

Plan: docs/superpowers/plans/2026-05-08-sp18-reward-shape-hold-attractor.md
Phase 2 Tasks 2.1-2.5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-05-09 10:16:49 +02:00
parent 3c318953a7
commit 8da8e2e584
7 changed files with 783 additions and 44 deletions

View File

@@ -896,6 +896,20 @@ fn main() {
// Pure observability: pre-fix baseline for the B-DD9 ratio
// gate. Plan: SP18 Phase 0 Task 0.2.
"td_error_mag_ema_kernel.cu",
// SP18 v2 Phase 2 (2026-05-09): D-leg structural Hold
// opportunity-cost device-fn GPU oracle test wrapper. Single
// thread × single block; wraps `compute_sp18_hold_opportunity_cost`
// from `trade_physics.cuh`. Mapped-pinned f32 input/output (no
// htod/dtoh per `feedback_no_htod_htoh_only_mapped_pinned`); host
// reads via `read_volatile` after stream sync (the
// `__threadfence_system()` at thread exit guarantees PCIe
// visibility before the sync returns). Same structural pattern
// as `sp12_reward_math_test_kernel.cu`. Test-only; no production
// callers. Drives `compute_sp18_hold_opportunity_cost`'s closed-
// form math contract from the Rust GPU oracle in
// `tests/sp18_hold_reward_oracle_tests.rs` against the CPU
// oracle in the same file (ε=1e-5). Plan: SP18 Phase 2 Task 2.2.
"sp18_hold_opp_test_kernel.cu",
// SP14 Layer C Phase C.6 (2026-05-08): h_s2_aux RMS EMA producer.
// Single-block 256-thread kernel computing RMS = sqrt(mean(x²))
// over `h_s2_aux [B, SH2]` (aux trunk final output, no activation)