37 KiB
SP18 Combined-Scope Wire-Up Audit
Audit captured during Phase 1 Task 1.1 of
docs/superpowers/plans/2026-05-08-sp18-reward-shape-hold-attractor.md.Branch:
feat/sp18-combined@0b737ec30(Phase 0 baseline, pre-Phase-1 atomic deletion).Audit script:
scripts/audit_sp18_consumers.sh. Re-run after every commit that touches the SP13/SP16 chain (D-leg) or the TD(λ)q_nextsite (B-leg). Pre-commit hook (Task 1.2) enforces snapshot-vs-current diff via--check.
Outcome of Phase 1 Task 1.1 (audit-first, halt-on-drift)
The pre-dispatch consumer-audit run surfaced multiple consumers NOT in the plan's locked checklist (spec §"Architecture Changes"). Per task input — "If your audit surfaces ANY additional consumer, halt and report — do NOT proceed with deletion ad-hoc" — Phase 1 Tasks 1.3-1.5 (atomic deletion) are HALTED pending plan / spec amendment.
Plan's locked D-leg checklist (8 entries)
| # | Site | Action |
|---|---|---|
| 1 | experience_kernels.cu:3162-3166, 3637-3643, 3710-3716 (3 cost sites) |
DELETE — replace with placeholder comment per Task 1.5 |
| 2 | training_loop.rs:564 (launcher call) + :631-667 (hold_cost_scale_diag emit) + :4171-4226 (host controller block) |
DELETE launcher + diag emit + controller; RETAIN slot-382 min_hold_temp_diag emit at :567-617 |
| 3 | crates/ml/src/cuda_pipeline/hold_cost_scale_update_kernel.cu |
DELETE entire file |
| 4 | crates/ml/src/cuda_pipeline/sp14_isv_slots.rs slots [461..468) (HOLD_COST_SCALE_INDEX + HCS_* Welford) + their MIN/MAX/SENTINEL constants |
DELETE constants + the layout-locked tests for them |
| 5 | crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs launch_hold_cost_scale_update method (line 15837) + HOLD_COST_SCALE_UPDATE_CUBIN static (line 2257) + struct field hold_cost_scale_update_ops (line 7853) + constructor wiring (lines 24433-24434, 25296) |
DELETE all |
| 6 | crates/ml/src/trainers/dqn/state_reset_registry.rs 7 entries (sp16_phase2_hold_cost_scale_adaptive + sp16_t3_hcs_* × 6) |
DELETE entries |
| 7 | crates/ml/build.rs:881 cubin manifest entry hold_cost_scale_update_kernel.cu |
DELETE |
| 8 | crates/ml/src/cuda_pipeline/state_layout.cuh:214 (ISV_HOLD_COST_SCALE_IDX) + :228-233 (ISV_HCS_* mirror) |
DELETE |
Additional consumers surfaced by audit (NOT in plan checklist)
The following consumers must be added to the atomic-deletion commit. They were missed by the plan's spec §"Architecture Changes" and would have left the deletion partial — per feedback_no_partial_refactor.md this would block compile or, worse, leave dangling references that compile but break test/integration paths.
| # | Site | Type | Resolution |
|---|---|---|---|
| A1 | crates/ml/src/cuda_pipeline/gpu_aux_trunk.rs:1240-1323 — HoldCostScaleUpdateOps struct + impl (kernel-load + launcher) |
PRODUCER WRAPPER (Rust-side). The plan said the launcher is in gpu_dqn_trainer.rs but actually only the trainer's thin call site launch_hold_cost_scale_update is there; the real HoldCostScaleUpdateOps::new/launch impl lives in gpu_aux_trunk.rs. |
DELETE the struct + impl block (84 lines). DELETE the HOLD_COST_SCALE_UPDATE_CUBIN import at gpu_aux_trunk.rs:56 (transitively unused after the struct is gone). |
| A2 | crates/ml/tests/sp14_oracle_tests.rs:2174-2920 — 11 GPU oracle tests + the doc-block at the head |
TESTS (75 occurrences of HOLD_COST_SCALE / HCS_TARGET / hold_cost_scale_update across this file). Tests: sp16_phase2_hold_cost_scale_climbs_with_overrun, sp16_phase2_hold_cost_scale_at_target_is_one, sp16_phase2_hold_cost_scale_under_target_is_one, sp16_phase2_hold_cost_scale_bounds_clamp, sp16_phase2_hold_cost_scale_pearl_a_bootstrap, sp16_phase3_alpha_high_during_signal_jump, sp16_phase3_alpha_low_in_steady_state, sp16_phase3_pearl_a_bootstrap_first_obs, sp16_phase3_alpha_naturally_bounded, sp16_phase3_no_hardcoded_alpha, plus the helper recover_alpha/compute_target_scale/run_synthetic_sequence infrastructure (~750 lines). All exercise the now-deleted hold_cost_scale_update kernel via include_bytes!(.../hold_cost_scale_update_kernel.cubin) — without the kernel they fail to compile. |
DELETE the 2 test modules (sp16_phase2_hold_cost_scale_gpu + sp16_t3_hold_cost_scale_alpha_gpu) and the standalone sp16_phase3_no_hardcoded_alpha test. ~750 lines from sp14_oracle_tests.rs:2174-2920. |
| A3 | crates/ml/src/trainers/dqn/trainer/training_loop.rs:8971-9043 — 7 dispatch arms in reset_named_state ("sp16_phase2_hold_cost_scale_adaptive" + "sp16_t3_hcs_*" × 6) |
DISPATCH ARMS. Required atomically with the registry-entry deletion (Task 1.4 expanded scope) — the contract test every_fold_and_soft_reset_entry_has_dispatch_arm (state_reset_registry.rs:2120-2280) fails if the registry has an entry but no dispatch arm OR vice versa. |
DELETE the 7 match arms (slot 461 + HCS slots 462-467) atomically with the registry entries. |
| A4 | crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs:23200-23216 — constructor block writing HOLD_COST_BASE to HOLD_COST_INDEX=380 + HOLD_RATE_TARGET_DEFAULT to slot 381 |
CONSTRUCTOR WRITE of slot 380 (controller output anchor). Per DD7c, slot 380 is RETAINED as part of the observation chain — but it now has no producer. The constructor-write of HOLD_COST_BASE keeps it at 0.005 cold-start. After deletion of the host controller, slot 380 becomes a read-only diagnostic slot (constructor-init only, no per-step rewrites). |
KEEP the constructor-write (DD7c retains slot 380). UPDATE the comment block at lines 23201-23210 to reflect the post-SP18-Phase-1 reality: "After SP18 Phase 1, slot 380 is no longer rewritten by a host-side controller (deleted per DD7=c). The constructor-init is now its only assignment — it serves as a read-only diagnostic anchor surfacing the static base cost. Phase 2 lands the structural Hold opportunity-cost reward at the 3 deleted cost-subtraction sites; that reward does NOT consume slot 380." |
| A5 | crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs:617, 2245-2256 — doc-block prose referring to deleted machinery |
DOC PROSE. References launch_hold_cost_scale_update, gpu_aux_trunk::HoldCostScaleUpdateOps, isv[461], etc. |
UPDATE comments to remove deleted-machinery references; replace with "(deleted SP18 Phase 1, DD7=c — see docs/sp18-wireup-audit.md)" pointer. No code change. |
| A6 | crates/ml/src/cuda_pipeline/sp13_isv_slots.rs:73-77 — HOLD_COST_BASE, HOLD_COST_CONTROLLER_GAIN, HOLD_COST_FLOOR_RATIO, HOLD_COST_CEIL_RATIO constants |
HOST-CONTROLLER CONSTANTS. Used ONLY by the host controller block at training_loop.rs:4213-4223 we're deleting. Per the SP13 v3 P0a comment block at sp13_isv_slots.rs:50-72, HOLD_COST_BASE is also used by the constructor-write at gpu_dqn_trainer.rs:23215. |
KEEP HOLD_COST_BASE (consumed by constructor-init at A4 — slot 380 retained). DELETE HOLD_COST_CONTROLLER_GAIN, HOLD_COST_FLOOR_RATIO, HOLD_COST_CEIL_RATIO (sole consumer is the deleted controller block). |
| A7 | crates/ml/src/cuda_pipeline/gpu_experience_collector.rs:5579-5585 — comment doc-ref to deleted controller |
COMMENT DOC-REF. References training_loop.rs:3604 (stale line — now :4209) and the priced-cost flow. |
UPDATE prose to reflect post-Phase-1 reality. |
| A8 | crates/ml/src/cuda_pipeline/sp5_isv_slots.rs:325-327 — comment doc-ref describing the controller chain |
COMMENT DOC-REF. Documents slot 380/381/382. Slots 381 + 382 retained per DD7c; slot 380 retained but no longer controller-written. | UPDATE prose to reflect post-Phase-1 reality. |
| A9 | crates/ml/src/cuda_pipeline/state_reset_registry.rs:1138-1271 — registry entries already marked RETIRED in PP.4 (commit c66e15f6d) for slots 461 + 462-467 |
REGISTRY ENTRIES. Already marked RETIRED in Pre-Phase PP.4. Phase 1 promotes RETIRED-with-sentinel-0.0-write to FULL DELETION. | DELETE the 7 entries (slot 461 + HCS slots 462-467) from the registry; layout fingerprint must be updated atomically — the fingerprint seed string covers slot allocations [461..468) which become RESERVED-gap (SP14-C.1 pattern). Slots themselves stay allocated (ISV_TOTAL_DIM unchanged) to preserve checkpoint compatibility. |
| A10 | crates/ml/src/trainers/dqn/state_reset_registry.rs:2256-2308 — lock_sp18_v2_pp4_retired_chain test which asserts the 7 retired entries STILL EXIST |
CONTRACT TEST. PP.4 added a lock test asserting the entries exist with sentinel 0.0. After full deletion, this test must be deleted (the entries no longer exist, so asserting their existence panics). | DELETE the lock_sp18_v2_pp4_retired_chain test (or rewrite to assert the slots are NOT registered). |
Out-of-scope mentions (do NOT delete; verify only)
These show up in the audit grep but per DD7c are RETAINED for HEALTH_DIAG continuity OR are independent modules:
| Site | Reason RETAINED |
|---|---|
crates/ml/src/cuda_pipeline/hold_rate_observer_kernel.cu |
Producer of slot 382 (observation chain). DD7c retains entire observation chain. |
crates/ml/src/cuda_pipeline/min_hold_temperature_update_kernel.cu |
Independent SP16-P1 producer of slot 460 (different chain). Reads same slots 381+382 but writes different output. RETAINED. |
crates/ml/src/cuda_pipeline/sp14_isv_slots.rs MHT_* slots [468..474) |
SP16-P1 (MIN_HOLD_TEMPERATURE) Welford accumulators — distinct chain. RETAINED. |
crates/ml/src/cuda_pipeline/sp13_isv_slots.rs:43 HOLD_COST_INDEX = 380 |
Slot constant for the retained observation chain. RETAINED. |
crates/ml/src/cuda_pipeline/state_layout.cuh:200-202 ISV_HOLD_COST_IDX/ISV_HOLD_RATE_TARGET_IDX/ISV_HOLD_RATE_OBSERVED_EMA_IDX (slots 380/381/382) |
Mirror constants for retained observation chain. RETAINED. |
crates/ml/src/trainers/dqn/state_reset_registry.rs:939 sp13_hold_rate_observed_ema registry entry for slot 382 |
FoldReset entry for retained observation chain. RETAINED. |
crates/ml/tests/sp13_phase0_oracle_tests.rs references to hold_rate_observer_kernel |
Tests for retained observer kernel. RETAINED. |
docs/superpowers/specs/2026-05-04-sp13-redefine-success-for-predictive-skill.md and related plan docs |
Historical spec/plan archaeology. NOT modified. |
Path forward
Two options:
-
EXPAND deletion scope to cover all 10 additional consumers (A1-A10) atomically in the same Phase 1 commit. The plan's spec §"Architecture Changes" was incomplete; this audit makes the scope explicit. The atomic commit grows from ~8 sites to ~18 sites but stays within the same conceptual change ("delete SP13/SP16 cost-application chain"). Per
feedback_no_partial_refactor. -
Halt and request human review. The plan author may have intended these consumers to be discovered iteratively (the plan says "trust the script, not the plan author"). But the script's purpose is to surface missed consumers BEFORE the deletion, exactly so the deletion stays atomic. Halting honours that intent; expanding the scope ad-hoc honours the deletion's atomicity.
Recommended: Option 1, with this audit doc serving as the spec-amendment record. The 10 additional consumers all flow from the same chain — none introduce new architectural concerns. They just expand the byte-count of the atomic-deletion commit.
Status
Phase 1 Tasks 1.3-1.6 LANDED (2026-05-09) — atomic deletion of all 18 sites
(8 plan + 10 audit-expanded A1-A10) committed in a single atomic commit per
feedback_no_partial_refactor. The branch was placed in INTERIM STATE: 3 reward
sites (experience_kernels.cu segment_complete + per-bar positioned-Hold +
per-bar flat-Hold) were placeholder-commented but contained no Hold cost.
Phase 2 Tasks 2.1-2.5 LANDED (2026-05-09) — atomic introduction of the
compute_sp18_hold_opportunity_cost device function + 3-site migration +
CPU/GPU oracle tests in a single commit per feedback_no_partial_refactor.
INTERIM STATE LIFTED. L40S dispatch remains FORBIDDEN pending Phase 3 cap
producer + Phase 4-5 B-leg, but the reward function is now structurally
complete (no placeholder sites, no orphaned consumers).
Per DD7c, observation chain preserved: slot 380 (HOLD_COST_INDEX, constructor- init-only diagnostic at HOLD_COST_BASE=0.005), slot 381 (HOLD_RATE_TARGET_INDEX, constructor-init at 0.20), slot 382 (HOLD_RATE_OBSERVED_EMA_INDEX, FoldReset chain still wired). MHT_* slots [468..474) preserved (MIN_HOLD_TEMPERATURE chain RETAINED).
Sites deleted (atomic commit):
- 3 reward subtraction sites in experience_kernels.cu (placeholder comments)
- hold_cost_scale_update_kernel.cu (file deleted)
- HoldCostScaleUpdateOps in gpu_aux_trunk.rs (struct + impl, ~120 lines)
- launch_hold_cost_scale_update + cubin static + struct field + constructor
- field-init in gpu_dqn_trainer.rs (5 sites)
- HOLD_COST_SCALE_INDEX, HCS_* slots, sentinel/bounds, layout-locked tests in sp14_isv_slots.rs
- HOLD_COST_CONTROLLER_GAIN/FLOOR/CEIL constants in sp13_isv_slots.rs
- ISV_HOLD_COST_SCALE_IDX + ISV_HCS_* mirror constants in state_layout.cuh
- build.rs cubin manifest entry
- 7 fold-reset registry entries (slot 461 + HCS_* 462..468)
- 7 dispatch arms in training_loop.rs
- Host controller block in training_loop.rs (lines ~4171-4226)
- launcher call site + hold_cost_scale_diag emit block in training_loop.rs
- 11 GPU oracle tests + helpers in sp14_oracle_tests.rs (~750 lines)
- lock_sp18_v2_pp4_retired_chain test (deleted, no inverse-contract replacement — pointless ceremony per user call)
- Layout fingerprint seed updated (HOLD_COST_SCALE + HCS_* removed; range [461..468) is RESERVED gap; ISV_TOTAL_DIM stays at 507 per checkpoint compatibility — RESERVED-gap pattern from SP14-C.1)
Validation: cargo check --workspace clean; cargo test -p ml --lib passes (state_reset_registry::every_fold_and_soft_reset_entry_has_dispatch_arm, sp16_t3_wiener_welford_slot_layout_locked, sp18_combined_slot_layout_locked all OK).
Phase 2 sites introduced (atomic commit)
crates/ml/src/cuda_pipeline/trade_physics.cuh— new device functioncompute_sp18_hold_opportunity_costaftercompute_lump_sum_opp_cost. Single forward-only function; reusescompute_asymmetric_capped_pnlfor the bilateral cap. No new infrastructure beyond existing primitives.crates/ml/src/cuda_pipeline/experience_kernels.cu— 3 placeholder comments replaced atomically:- Site 1 (segment_complete branch ~3143): unconditional add into
base_rewardBEFORE the SP12 asymmetric cap; preserves the SP12 cap interaction and cap participation per the original SP13 P0a comment contract. - Site 2 (per-bar positioned-Hold ~3641): unconditional add into
r_micro(rc[3]) — same component the deleted subtraction wrote to pre-SP18, preserving SP11 reward-subsystem controller signal attribution.vol_proxyrecomputed locally fromfeatures[bar_idx * market_dim + 9](the per-bar branches are outside the segment_completevol_proxyscope). - Site 3 (per-bar flat-Hold ~3760): unconditional add into
r_opp_cost(rc[4]) — same component the deleted subtraction wrote to pre-SP18.vol_proxyrecomputed locally as in Site 2.
- Site 1 (segment_complete branch ~3143): unconditional add into
crates/ml/src/cuda_pipeline/sp18_hold_opp_test_kernel.cu— new GPU oracle test wrapper (single-thread single-block). Wraps the device fn for the GPU oracle test; mapped-pinned f32 input/output perfeedback_no_htod_htoh_only_mapped_pinned.crates/ml/build.rs— addedsp18_hold_opp_test_kernel.cuto the cubin manifest.crates/ml/tests/sp18_hold_reward_oracle_tests.rs— added T1 (CPU oracle, 6 cases covering positive/negative/saturation/dir-gate/ shaping_scale=0) + T2 (GPU oracle parity with CPU at ε=1e-5) per spec §"Behavioral Test Predictions".
Phase 2 design notes
vol_proxyscoping: the per-bar Hold sites (Sites 2-3) are outside the segment_completevol_proxyscope (declared atexperience_kernels.cu:3124). Each per-bar site recomputesvol_proxylocally from the samefeatures[bar_idx * market_dim + 9]ATR-norm input the segment_complete branch and the B.2/D.4c bonus blocks consume. Thevol_proxy_pb1/vol_proxy_pb2locals mirror the segment_complete computation modulo the× sqrt(hold_time)term (which is 1 for per-bar / hold_time=1 — matches the device fn's contract).- ISV cap fallback: each call site reads
ISV[483](POS_CAP) andISV[484](NEG_CAP) with the same sentinel-or-out-of-bounds guard used at Site 1: if the slot value is within ε of the sentinel OR outside[POS_CAP_MIN_BOUND, POS_CAP_MAX_BOUND], the call falls through to the macro defaults (+5/-10). This is bit-identical to a fixed +5/-10 cap during cold-start and Pre-Phase-3 (when the producer kernelhold_reward_cap_update_kernelhas not yet landed). - Reward-component slot routing: Sites 1/2/3 write to slots 0/3/4 (popart/micro/opp_cost) per the SP15 Wave 2 reward-component layout. Routing matches the deleted SP13/SP16 subtraction destinations exactly, so the SP11 reward-subsystem controller's component-weight estimator sees the same input distribution shape (modulo the structural-counterfactual vs reactive-cost magnitude shift, which is precisely the change the controller is meant to learn).
shaping_scalegating: the device fn multiplies the capped output byshaping_scale. Validation runs atshaping_scale=0→ the device fn returns 0.0 → no Hold opp cost in pure backtest, matching the SP12 lump-sumcompute_lump_sum_opp_costcontract.
Phase 3 sites introduced (atomic commit, 2026-05-09)
D-leg adaptive HOLD_REWARD_POS/NEG_CAP producer kernel + launcher +
per-epoch wiring + GPU oracle tests. Lifts the Phase 2 caps from
"sentinel-driven cold-start" (5.0/-10.0 fixed) to "p99(|step_ret|) over
Long/Short trade closes × 1.5 safety factor" with Wiener-optimal α blend
per pearl_wiener_optimal_adaptive_alpha. The Phase 2 consumer
(compute_sp18_hold_opportunity_cost) sees the producer-driven slots
[483]/[484] from epoch 1 onward; the sentinel branch in the consumer
remains as the cold-start fallback path (zero-trade-close epoch ⇒
producer early-returns ⇒ slots stay at sentinel ⇒ consumer falls
through to the +5/-10 macro defaults — bit-identical to pre-Phase-3
behavior).
Sites introduced (single atomic commit per feedback_no_partial_refactor):
-
Kernel:
crates/ml/src/cuda_pipeline/hold_reward_cap_update_kernel.cu(NEW, ~310 lines). Single-block 256-thread producer; mirrors the SP14 P0-Areward_cap_update_kernelstructural template with three differences:- Filter:
(is_close && step_ret != 0)— both winners AND losers (the consumer needs the magnitude scale of all Long/Short closes, not just winners). Position-side cap filters(is_close && step_ret > 0). - Welford-derived Wiener-α (slots [487..493)) replaces fixed α=0.01.
Floor at WELFORD_ALPHA_MIN=0.4 per
pearl_wiener_alpha_floor_for_nonstationary— the policy-realised Long/Short close magnitude distribution is intrinsically non-stationary as the policy adapts, sharpening tails. - Bounds [HOLD_REWARD_POS_CAP_MIN_BOUND=0.5, HOLD_REWARD_POS_CAP_MAX_BOUND=50.0] (vs. position-side [1.0, 50.0]).
- Filter:
-
Cubin manifest:
crates/ml/build.rs— added kernel entry. Build-rs cubin staticHOLD_REWARD_CAP_UPDATE_CUBINexposed ingpu_dqn_trainer.rs:2190. -
Producer wrapper:
crates/ml/src/cuda_pipeline/gpu_aux_trunk.rs— newHoldRewardCapUpdateOpsstruct (mirrorsRewardCapUpdateOps) withnew/launchimpl; pre-loadsCudaFunctionat construction perpearl_no_host_branches_in_captured_graph.md. -
Trainer wiring:
crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs:- Struct field
hold_reward_cap_update_opsat line 7828. - Constructor instantiation at line 24409.
- Field-init in struct literal at line 25293.
- Method
launch_hold_reward_cap_updateplaced right afterlaunch_reward_cap_update.
- Struct field
-
Per-epoch wire-up:
crates/ml/src/trainers/dqn/trainer/training_loop.rsline 511 —fused.trainer().launch_hold_reward_cap_update(step_ret_ptr, trade_close_ptr, total_samples)placed right afterlaunch_reward_cap_updateso both cap producers share the same source buffers (read-only; independent ISV slot pairs at [452]/[453] and [483]/[484]). -
HEALTH_DIAG:
training_loop.rs(posttd_error_preemit) — new lineHEALTH_DIAG[N]: hold_reward_cap [pos={:.4} neg={:.4} fire_rate={:.4}]. Reads ISV[483]/[484]/[486] viaread_isv_signal_at; slot 486 (HOLD_OPP_COST_FIRE_RATE_EMA) is the engagement-canary diag for Phase 8 to populate. -
GPU oracle tests:
crates/ml/tests/sp18_hold_reward_oracle_tests.rs— three new tests in themod gpublock:hold_reward_cap_producer_drives_slots— T5 from spec: 100 simulated Long/Short closes with jittered ~N(0, 0.01); verifies ISV[483] is bilaterally clamped to MIN_BOUND=0.5 (small p99 ⇒ target_cap << bound), ISV[484] = -2 × ISV[483], filler outliers (close=0) filtered out.hold_reward_cap_pearl_a_first_observation_replace— Pearl-A bootstrap: 4 large closes withstep_ret = [+10, -20, +30, -40], target_cap = max(p99, max) × 1.5 ≈ 76.5, clamped to MAX_BOUND=50, Pearl-A REPLACE on sentinel writes 50.0 directly (NOT blended with sentinel 5.0).hold_reward_cap_no_closes_preserves_isv— no closes (trade_close=0 ∀i): every ISV slot (POS, NEG, all 6 Welford) preserved bit-exactly. Verifies the cold-start fallback path.
-
Audit script:
scripts/audit_sp18_consumers.sh --checkexits 0 (no fingerprint drift in tracked sections — SP18 D-leg/B-leg legacy references unchanged; the new Phase 3 producer is in its own symbol-namespace not tracked by the audit's snapshot, which is by design — the audit tracks the DELETED SP13/SP16 legacy chain).
Validation: cargo check --workspace clean (1m 12s); 3 GPU oracle tests pass on local RTX 3050 Ti (1.93s wallclock).
Phase 3 design notes
-
Why Wiener-α (vs. fixed α=0.01 on the position-side cap): the position-side cap drifts slowly because the underlying alpha distribution is approximately stationary over training. The Hold-side cap distribution is intrinsically NON-stationary as the policy adapts (sizing tightens, trail shifts). Welford-derived α tracks that non-stationarity; the 0.4 floor prevents the canonical "α decays to 0 once policy converges" trap (SP16 T3 regression at train-multi-seed-b5gmp sha
641aa0dfdFold 1: α=0.248 at HD4 → Hold% 52.6% at HD5 — seepearl_wiener_alpha_floor_for_nonstationary). -
Why no separate Long/Short flag buffer:
step_ret_per_sample[i]is non-zero ONLY on close events from positions that had non-zero pre_sign — i.e. Long or Short positions. Hold/Flat positions don't generate non-zero step_ret perexperience_kernels.cu:2855. Filtering by(is_close && step_ret != 0)is the cheapest correct extraction of the Long/Short close distribution; no extra dir_idx_per_sample buffer needed. -
Cold-start fallback behavior: when
total_count == 0(no closes this epoch — Hold-heavy phase), the kernel early-returns at the guard in Phase 2 of its body. ALL slots (POS, NEG, all 6 Welford) remain unchanged. The Phase 2 consumer (compute_sp18_hold_opportunity_cost) sees the cold-start sentinels (5.0/-10.0) and routes through its ownis_sentinelbranch to the macro defaults +5/-10 — bit-identical to pre-Phase-3. -
Coexistence with
reward_cap_update_kernel: both producers read the samestep_ret_per_sample+trade_close_per_samplebuffers (read-only; no shared state). They drive independent ISV slot pairs and use different filters. Per-epoch boundary launches are sequential (position-side first, Hold-side second) so the two_kernel sequence is graph-capturable (single stream, no host branches). The redundant block-tree-reduce on the same input buffers is a micro-cost (≤ 2 × 64K samples × 4 floats per epoch = cheap relative to per-step training cost).
B-leg verification (Phase 1.1 sub-objective)
Per the plan's locked B-leg checklist:
td_lambda_kernellaunch sites: 1 site atgpu_experience_collector.rs:4154(per the plan). Verified.q_nextself-bootstrap: line~4143confirmed (let q_next = rewards). Phase 5 will replace.- PER priority recomputation: 5 sites in
crates/ml-dqn/src/gpu_replay_buffer.rs,crates/ml-dqn/src/replay_buffer_type.rs,crates/ml/src/trainers/dqn/config.rs,crates/ml/src/trainers/dqn/fused_training.rs,crates/ml/tests/gpu_per_integration_test.rs. NONE storeq_next— confirms B-DD4 (no PER migration needed). - C51 loss target-Q origin:
c51_loss_kernel.cublock_bellman_project_falready uses target-net Q at loss time — no migration needed. target_params_buf: 17 references ingpu_dqn_trainer.rs, Polyak-tracked, ready for B-leg consumption.- PopArt slot 63: catalogued; B-DD11 reset is the only PopArt change in scope.
B-leg pre-conditions verified ✓ — no additional B-leg consumers surfaced (Phase 4-5 atomic-refactor scope is unchanged).
Captured audit fingerprint (locked snapshot — pre-commit hook --check diffs against this)
The fingerprint below is a per-section, per-file hit count summary. It is short enough to inline in markdown, sortable for human review, and stable enough to diff cleanly. Catches both new-consumer drift (a file appears in a section where it didn't before) and expanded-consumer drift (a file's hit count changes). It does NOT catch line-by-line content drift within a single file — that's checked by the per-section grep output diffed in PR review.
To regenerate:
scripts/audit_sp18_consumers.sh --fingerprint
# update this section between BEGIN/END markers, then:
git add docs/sp18-wireup-audit.md
# the pre-commit hook check_sp18_consumer_audit fails the next commit if
# scripts/audit_sp18_consumers.sh --check reports drift vs this snapshot.
After Phase 1's atomic deletion lands, this fingerprint will shrink dramatically (slot 461 + HCS_* + hold_cost_scale_update sections drop to zero hits). That re-capture is committed as part of Task 1.6 (close-out).
=== D-leg: Slot 380 (HOLD_COST_INDEX) consumers === total_hits=39 crates/ml/build.rs 1 crates/ml/src/cuda_pipeline/sp5_isv_slots.rs 1 crates/ml/src/cuda_pipeline/hold_rate_observer_kernel.cu 1 crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs 2 crates/ml/src/cuda_pipeline/sp13_isv_slots.rs 1 crates/ml/src/cuda_pipeline/state_layout.cuh 1 crates/ml/src/trainers/dqn/trainer/training_loop.rs 3 crates/ml/src/trainers/dqn/state_reset_registry.rs 1 docs/dqn-wire-up-audit.md 10 docs/superpowers/specs/2026-05-04-sp13-redefine-success-for-predictive-skill.md 4 docs/superpowers/plans/2026-05-08-sp18-reward-shape-hold-attractor.md 4 docs/superpowers/plans/2026-05-04-sp13-redefine-success-for-predictive-skill.md 5 docs/sp18-wireup-audit.md 5 === D-leg: Slot 461 (HOLD_COST_SCALE_INDEX) consumers === total_hits=79 crates/ml/src/cuda_pipeline/sp20_controllers_compute.rs 5 crates/ml/src/cuda_pipeline/sp20_hold_baseline.cuh 2 crates/ml/src/cuda_pipeline/gpu_experience_collector.rs 2 crates/ml/src/cuda_pipeline/sp20_hold_baseline_test_kernel.cu 1 crates/ml/src/cuda_pipeline/sp14_isv_slots.rs 2 crates/ml/src/cuda_pipeline/sp20_controllers_compute_kernel.cu 2 crates/ml/src/trainers/dqn/trainer/training_loop.rs 4 crates/ml/src/trainers/dqn/state_reset_registry.rs 2 crates/ml/tests/sp20_controllers_compute_test.rs 17 crates/ml/tests/sp20_phase1_4_wireup_test.rs 2 docs/dqn-wire-up-audit.md 11 docs/superpowers/specs/2026-05-09-sp19-20-wr-first-design.md 1 docs/superpowers/plans/2026-05-09-sp19-20-wr-first.md 8 docs/superpowers/plans/2026-05-08-sp18-reward-shape-hold-attractor.md 3 docs/superpowers/plans/2026-05-08-sp16-hold-collapse-structural-fix.md 10 docs/sp18-wireup-audit.md 6 docs/isv-slots.md 1 === D-leg: Slots [462..468) (HCS_* Welford) consumers === total_hits=9 docs/dqn-wire-up-audit.md 8 docs/superpowers/plans/2026-05-08-sp18-reward-shape-hold-attractor.md 1 === D-leg: hold_cost_scale_update_kernel references === total_hits=78 crates/ml/src/cuda_pipeline/sp20_controllers_compute.rs 2 crates/ml/src/cuda_pipeline/sp20_controllers_compute_kernel.cu 3 crates/ml/src/trainers/dqn/trainer/training_loop.rs 5 crates/ml/src/trainers/dqn/state_reset_registry.rs 2 crates/ml/tests/sp20_controllers_compute_test.rs 3 docs/plans/2026-05-10-sp21-train-eval-coherence-isv-defrost.md 2 docs/dqn-wire-up-audit.md 23 docs/superpowers/specs/2026-05-08-sp18-reward-shape-hold-attractor-design.md 4 docs/superpowers/plans/2026-05-09-sp19-20-wr-first.md 2 docs/superpowers/plans/2026-05-08-sp18-reward-shape-hold-attractor.md 12 docs/superpowers/plans/2026-05-08-sp16-hold-collapse-structural-fix.md 9 docs/sp18-wireup-audit.md 11 === D-leg: hold_rate_observer_kernel (RETAINED — diag chain stays per DD7=c) === total_hits=164 crates/ml/build.rs 5 crates/ml/src/cuda_pipeline/sp5_isv_slots.rs 3 crates/ml/src/cuda_pipeline/gpu_aux_trunk.rs 5 crates/ml/src/cuda_pipeline/hold_rate_observer_kernel.cu 4 crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs 8 crates/ml/src/cuda_pipeline/gpu_experience_collector.rs 13 crates/ml/src/cuda_pipeline/sp14_isv_slots.rs 5 crates/ml/src/cuda_pipeline/sp20_aggregate_inputs.rs 1 crates/ml/src/cuda_pipeline/sp13_isv_slots.rs 2 crates/ml/src/cuda_pipeline/min_hold_temperature_update_kernel.cu 6 crates/ml/src/cuda_pipeline/sp20_aggregate_inputs_kernel.cu 1 crates/ml/src/cuda_pipeline/state_layout.cuh 1 crates/ml/src/trainers/dqn/trainer/training_loop.rs 13 crates/ml/src/trainers/dqn/state_reset_registry.rs 3 crates/ml/tests/sp13_phase0_oracle_tests.rs 19 crates/ml/tests/sp14_oracle_tests.rs 20 docs/dqn-wire-up-audit.md 12 docs/superpowers/specs/2026-05-08-sp18-reward-shape-hold-attractor-design.md 1 docs/superpowers/specs/2026-05-04-sp13-redefine-success-for-predictive-skill.md 6 docs/superpowers/plans/2026-05-05-sp14-aux-q-wire-earned-gradient-flow.md 5 docs/superpowers/plans/2026-05-08-sp18-reward-shape-hold-attractor.md 3 docs/superpowers/plans/2026-05-04-sp13-redefine-success-for-predictive-skill.md 20 docs/sp18-wireup-audit.md 7 docs/isv-slots.md 1 === D-leg: Per-bar Hold-cost subtraction sites (3 lines we change in Phase 1.5) === total_hits=5 crates/ml/src/cuda_pipeline/experience_kernels.cu 5 === D-leg: state_layout.cuh mirror constants (HOLD_COST + HCS) === total_hits=1 crates/ml/src/cuda_pipeline/state_layout.cuh 1 === D-leg: state_reset_registry slot 461 entries === total_hits=2 crates/ml/src/trainers/dqn/state_reset_registry.rs 2 === D-leg: Cubin manifest references in build.rs === total_hits=2 crates/ml/build.rs 2 === B-leg: td_lambda_kernel launch sites === total_hits=20 crates/ml/src/cuda_pipeline/nstep_kernel.cu 1 crates/ml/src/cuda_pipeline/gpu_experience_collector.rs 11 crates/ml/tests/sp18_td_lambda_q_next_oracle_tests.rs 8 === B-leg: q_next argument origin (the line ~4143 site) === total_hits= === B-leg: rewards_out consumers (downstream of TD(λ) output) === total_hits= === B-leg: PER priority computation sites (verify B-DD4: NO migration needed) === total_hits=18 crates/ml/src/trainers/dqn/fused_training.rs 1 crates/ml/src/trainers/dqn/config.rs 4 crates/ml/tests/gpu_per_integration_test.rs 3 crates/ml-dqn/src/replay_buffer_type.rs 3 crates/ml-dqn/src/gpu_replay_buffer.rs 7 === B-leg: Replay buffer schema — q_next storage check (verify NO migration) === total_hits= === B-leg: c51_loss_kernel target-Q origin (verify already correct at loss time) === total_hits= === B-leg: target_params_buf consumers (verify Polyak-tracked, available at collection time) === total_hits= === B-leg: PopArt slot 63 references (verify B-DD11 reset is the only PopArt change) === total_hits=20 crates/ml/build.rs 8 crates/ml/src/training_profile.rs 8 crates/ml/src/cuda_pipeline/trade_physics.cuh 1 crates/ml/src/cuda_pipeline/dqn_utility_kernels.cu 3 === END === total_hits=
Phase 4 close-out — B-leg target-net forward additive infrastructure (2026-05-09)
Phase 4 lands the additive dead-code state for the B-leg target-Q
DDQN bootstrap that Phase 5 will swap into td_lambda_kernel's q_next
argument at gpu_experience_collector.rs:~4313 (post-Task-4.1 hoist).
Scope landed in this commit:
- Task 4.1 —
next_statesbuild-site relocated. Thebuild_next_states_f32invocation now precedes the TD(λ) launch block (no behavioral change —next_stateshad no observer between its pre-Phase-4 build site and the bottom-of-fn batch assembly). Pure ordering hoist; verified via the audit grepnext_states\bflagging 0 post-TD(λ) consumers. - Task 4.2 —
compute_q_next_target_bootstrapskeleton method added onGpuExperienceCollectorwith full plan-aligned signature:Body is a hard-error early-return perpub fn compute_q_next_target_bootstrap( &mut self, next_states: &CudaSlice<f32>, target_params_ptr: u64, online_params_ptr: u64, batch_size: usize, ) -> Result<CudaSlice<f32>, MLError>feedback_no_stubs— no production caller exists in Phase 4 (the caller is wired in Phase 5 Task 5.1, replacing the self-bootstrap clone at line ~4313). Tasks 4.3-4.5 fill in the body atomically before Phase 5 wires the production call site. Returning Err instead oftodo!()satisfies Invariant 9 (no deferred-work markers); any accidental pre-Phase-5 call site hard-errors with a clear pointer to the open work.
Phase 4 introspection tests (no GPU required, all pass):
| Test | File | Purpose |
|---|---|---|
compute_q_next_target_bootstrap_method_exists |
crates/ml/tests/sp18_td_lambda_q_next_oracle_tests.rs |
Asserts method signature exists with documented parameters |
next_states_built_before_td_lambda |
same | Asserts Task 4.1 ordering invariant via offset-comparison |
td_lambda_still_consumes_self_bootstrap_q_next_in_phase4 |
same | Atomic-refactor guard: Phase 4 STILL uses q_next = rewards_out clone — Phase 5 Task 5.1 swaps |
Atomic-refactor invariant (HARD — feedback_no_partial_refactor):
Phase 4 leaves compute_q_next_target_bootstrap as dead code (no
production caller, only introspection tests). Phase 5.1 makes it live
in lockstep with the q_next argument swap at the TD(λ) launch:
NO L40S DISPATCH between the Phase 4 close-out commit and the Phase 5 Task 5.1 commit that replaces
let q_next = dtod_clone_f32_native(&self.stream, &self.rewards_out, ...)withlet q_next = self.compute_q_next_target_bootstrap(&next_states, ...).
Tasks 4.3-4.5 remaining (deferred to follow-up subagent dispatch):
- Task 4.3 — implement online-net forward on
next_statesfor DDQN per-branch argmax (mirror ofsubmit_forward_ops_ddqninfra: VSN+BN+trunk+branch-head chain viacublas_forward.forward_online_raw, output:[B, 4]i32 per-branch argmax actions). - Task 4.4 — implement target-net forward via
cublas_forward.forward_target_raw, thencompute_expected_qwith target's distributional outputs to produce per-actionE[Q]; gather per-branch argmaxQ_target[b, a*_online_branch]and resolve final scalar form (B-DD8.a: dir-branch only, OR sum across 4 branches). GPU oracle tests T8 + T11 (#[ignore = "requires GPU"]). - Task 4.5 — replace
todo!()body; close-out grep audit assertstodo!absent incompute_q_next_target_bootstrap; Phase 4 ends in additive-dead-code state with full body wired.
These tasks each require substantial new infrastructure on the
collector (target-net trunk forward chain duplicating
gpu_dqn_trainer.rs Pass 2's VSN+BN+OFI+trunk+branch-head sequence)
and are decomposed in the plan §"Phase 4" as 3 separate TDD cycles
with GPU oracle test gates between each. Atomic-refactor rule binds
the entire chain Task 4.5 → Task 5.1: NO L40S DISPATCH between.
Sub-task verification status:
SQLX_OFFLINE=true CUDA_COMPUTE_CAP=86 cargo check -p ml
# → finished `dev` profile [unoptimized + debuginfo] target(s); clean
SQLX_OFFLINE=true CUDA_COMPUTE_CAP=86 \
cargo test -p ml --test sp18_td_lambda_q_next_oracle_tests
# → 3 passed; 0 failed; 0 ignored
Audit script post-Phase-4 fingerprint diff (vs Phase 1 close-out):
td_lambda_kernel launch sitescount rose from 9 → 19 due to the newsp18_td_lambda_q_next_oracle_tests.rsintrospection test file (7 hits via test docstrings + assertions + the build-site ordering check); the sourcegpu_experience_collector.rscount rose by 3 (additive comments referencingtd_lambda_kernelper the Phase-4 wiring narrative). Fingerprint regenerated above.compute_q_next_target_bootstrapintroduced — 1 producer site (the new method ingpu_experience_collector.rs) + 1 introspection test referencing it. No production caller (Phase 4 = additive dead code by design).