Commit Graph

1286 Commits

Author SHA1 Message Date
jgrusewski
ab9c7e43a8 delete(dqn): D.7 liquid_mod audit — ODE identity, remove from c51_grad + experience kernels
The liquid_tau_rk4_step kernel modelled f(x) = (1/tau)*(1-x), which has
fixed point x=1.0 for any tau. liquid_mod_buf was initialised to [1.0;4]
and the dynamics could never move it away from that fixed point since every
kernel call reduces to f(1.0)=0 (no perturbation path exists). The
velocity_mod multiplier in c51_grad_kernel was therefore always 1.0 — a
mathematical identity with zero measurable effect on spread_scale.

Additionally, no ISV slot existed for liquid_mod (violates §4.C.6
GPU-drives spec), and the associated LiquidTrainableAdapter supervised
path was never wired into the DQN backward pass.

Decision C (delete): remove liquid_tau_rk4_step kernel (experience_kernels.cu),
liquid_mod param + velocity_mod line (c51_grad_kernel.cu), liquid_mod_buf
allocation, liquid_tau_rk4_kernel field, update_liquid_tau() method and its
call site in update_eval_v_range() (gpu_dqn_trainer.rs). per_branch_q_gap_ema_buf
is retained — it is used independently for trajectory backtracking state
snapshot/restore. Supervised LiquidTrainableAdapter unchanged.

cargo check -p ml: 8 warnings (baseline), 0 errors.
Audit docs updated: dqn-wire-up-audit.md + ml-supervised-to-dqn-concept-audit.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 20:18:59 +02:00
jgrusewski
f7a91d906d feat(dqn-v2): D.5 soft fold-boundary transitions via bootstrap-write + EMA
SoftReset registry category (isv_grad_balance_targets, isv_grad_scale_limit)
implemented via bootstrap-write + existing kernel's EMA. CPU writes bootstrap
values (1.0 for targets, 2.0 for limit) at fold boundary; grad_balance_isv_update
kernel's adaptive-rate EMA (alpha in [0.01, 0.30]) converges from bootstrap
toward observed values over subsequent epochs (implicit decay_bars via EMA time
constant).

Option A chosen (minimal): the existing grad_balance_isv_update kernel already
uses adaptive-rate EMA — not a hard-write — so bootstrap-at-fold-boundary is
sufficient. The EMA time constant ~1/alpha provides the decay, satisfying the
decay_bars=500 intent without a new ISV slot (Option B rejected as over-
engineered: new ISV slot + kernel change for no material behavioral improvement).

Changes:
- training_loop.rs::reset_named_state: add dispatch arms for both SoftReset
  entries, writing bootstrap constants (CPU-born input, not adaptive output;
  complies with spec §4.C.6 GPU-drives-CPU-reads)
- trainer/mod.rs: fold-boundary reset loop now calls both fold_reset_entries()
  and soft_reset_entries(); stale "handled separately (Plan 2)" comment removed
- smoke_tests/soft_reset.rs: 4 CPU-only tests verify registry classification,
  entry count, mutual exclusivity from fold_reset, and bootstrap constant invariants
- docs/dqn-wire-up-audit.md: D.5 SoftReset dispatch row added

No CPU-side adaptive computation. No new ISV slot. No behavioral change to
training beyond writing known-good bootstrap values at fold boundaries.

Plan 2 Task 4. Spec §4.D.5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:08:36 +02:00
jgrusewski
d071501e44 feat(dqn-v2): D.2 per-branch gamma — GPU kernel + monitor (spec §4.D.2 + §4.C.6)
Replaces scalar GAMMA_EFF_INDEX=43 with 4 per-branch slots at 43-46
(DIR, MAG, ORD, URG). New per_branch_gamma_update_kernel.cu reads
per-branch v-range + health and writes 4 slots. Deletes
gamma_update_kernel.cu per feedback_no_partial_refactor.md.

Per-branch base/max: direction [0.92, 0.99] (trend horizon),
magnitude [0.88, 0.95], order [0.85, 0.93], urgency [0.80, 0.90]
(execution horizon). Kernel diverges each branch's γ from uniform
bootstrap based on its branch's Q-stats.

ISV slots downstream of 43 shift +3: KELLY (44→47), CQL_ALPHA (45→48),
PLAN_THRESHOLD (46→49), Q_P05_* (47→50..54), Q_P95_* (51→54..58),
fingerprint (55-56 → 58-59). ISV_TOTAL_DIM grows 57 → 60.
Layout fingerprint auto-recomputes from updated seed bytes.

c51_loss_kernel reads per-branch γ from ISV[43+d] inside the branch loop;
iql_compute_per_sample_support reads ISV[43+d] per-branch for half_w.
kelly_cap_update_kernel write index updated 44→47.
q_quantile_kernel slot bases updated 47/51 → 50/54.
state_layout.cuh ISV_PLAN_THRESHOLD_IDX updated 46→49.

CPU-side PerBranchGammaMonitor (gamma_monitor.rs) is read-only observer
of all 4 slots; read() returns mean, diagnose() exposes individual γ values
+ spread + health. No CPU-side γ computation (spec §4.C.6 GPU-drives).

Plan 2 Task 3. Spec §4.D.2 + §4.C.6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:01:38 +02:00
jgrusewski
345867c599 test(dqn-v2): D.1 Mamba2 backward — grad-check validation (already wired)
Plan 1 A.5 audit found mamba2_scan_projected_bwd kernel + host call at
gpu_dqn_trainer.rs::mamba2_backward are ALREADY fully wired in the
adam_grad CUDA-graph child. Plan 2 Task 2 narrows from "implement" to
"validate".

Two smoke tests confirm correctness:
- mamba2_backward_gradients_propagate: grad Frobenius norm 0.25 after 3
  epochs (>> 1e-8 threshold), ruling out silent no-op like compute_iqr
  had pre-Task-A.6.
- mamba2_backward_grad_check: kernel-level reference check (B=2 K=2
  SH2=4 STATE_D=4); max rel_err d_gate=2.6e-7, d_x_out=2.6e-7,
  d_context=6.7e-8 — all well within 15% threshold (near machine
  epsilon, confirming bit-identical host/GPU results).

No production code change — test-only accessors exposed via #[cfg(test)]
impl blocks on GpuDqnTrainer and DQNTrainer. Audit doc updated.

Plan 2 Task 2. Spec §4.D.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:40:52 +02:00
jgrusewski
2cf9927647 feat(dqn-v2): C.1 quantile-based atom support (Plan 2 Task 1)
Replaces half = max(10*q_gap, 3*std_ema, floor) tuned constants with
quantile-based: half = max(|q_p95 - v_center|, |v_center - q_p5|)
clamped to [min_half_floor, abs_half]. Covers observed Q distribution
directly.

New GPU kernel q_quantile_reduce reads per-sample Q-values from q_out_buf,
sorts per branch (bitonic for power-of-2 N, quickselect otherwise), writes
ISV[Q_P05_*=47..51) and ISV[Q_P95_*=51..55). Cold-path per-epoch (4 blocks,
1 thread each). No atomicAdd.

ISV slots 47-54 added at tail (8 total). Fingerprint shifted to 55-56.
ISV_TOTAL_DIM grows 49 -> 57. Fingerprint seed updated; recomputes hash
at compile time (new value: 0xbf6c400c026d77e3).

Launch order: reduce_current_q_stats (populates q_out_buf) ->
q_quantile_reduce (writes ISV P5/P95) -> reduce_current_q_stats_per_branch
-> update_eval_v_range (reads ISV P5/P95 for half-width).

Bootstrap: q_p05 = v_min, q_p95 = v_max (matches current atom range at
cold start). FoldReset: isv_q_quantiles dispatch arm resets to bootstrap
values at fold boundary.

StateResetRegistry: isv_q_quantiles as FoldReset; dispatch arm added in
training_loop.rs::reset_named_state.

Docs: isv-slots.md rows [47..57) updated, fingerprint tail reference
corrected to [55..57). dqn-wire-up-audit.md: q_quantile_kernel.cu added.

Plan 2 Task 1. Spec §4.C.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:29:26 +02:00
jgrusewski
79b7d3fc8b plan(dqn-v2): Plan 2 revision — GPU-drives architecture + Mamba2 validation scope
Revises Plan 2 to match the post-Plan-1 reality:

1. AdaptiveController → AdaptiveMonitor throughout (read-only observers
   per spec §4.C.6 2026-04-24 revision; GPU kernels compute, CPU reads).
   Applies to Task 3 (per-branch gamma) and Task 5 (liquid_mod audit).

2. Task 2 (D.1 Mamba2 backward) scope narrowed from "implement" to
   "validate existing". Plan 1 A.5 audit confirmed
   mamba2_scan_projected_bwd kernel + mamba2_backward host call are
   already fully wired. Task 2 now adds a finite-difference grad-check
   smoke + non-zero grad-propagation smoke; escalates if either fails.

3. Task 3 (D.2 per-branch gamma) rewritten for GPU-drives compliance:
   - Replace scalar GAMMA_EFF_INDEX=43 with 4 per-branch slots at
     43-46 (DIR/MAG/ORD/URG).
   - New per_branch_gamma_update_kernel.cu reads v-range + health,
     writes 4 slots. Deletes the Plan 1 gamma_update_kernel.cu.
   - 4 read-only monitors (or one consolidated PerBranchGammaMonitor).
   - ISV slots 44-48 shift downstream; fingerprint re-tails at 50-51;
     ISV_TOTAL_DIM grows 49 → 52.
   - c51_loss_kernel and iql_value_kernel read per-branch γ from ISV.
   - No CPU-side γ computation anywhere.

4. Header architecture block updated: new ISV slot count target,
   GPU-drives principle explicit, current Plan-1 layout table inline
   for reference.

5. Pre-plan verification updated: expects AdaptiveMonitor trait (not
   AdaptiveController); checks for 6 Plan-1 monitor files.

6. Removed "schema version bump 1 → 2" language — layout fingerprint
   auto-recomputes from seed bytes; no integer version space exists.

Task 4 (D.5 soft fold transitions), Task 5 content (D.7 liquid audit),
Task 6 (D.3+D.6+D.8 coordinated state-layout migration), Task 7
(validation run) structure preserved. Internal slot numbers in those
tasks will reconcile to the new layout when they land (no pre-emptive
edits since those indices depend on whether Task 1 quantile slots or
Task 3 per-branch gamma slots land first — re-compute at exec time).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:04:56 +02:00
jgrusewski
536eea20bf fix(dqn-v2): local-smoke fallout — StateResetRegistry dispatch arms, controller_activity thresholds, example hp_f32 helper
Local smoke-test run after Plan 1 C.6 completion surfaced three issues:

1. StateResetRegistry missing dispatch arms for the 8 ISV slots
   pre-allocated in ac9bcab94 (isv_epoch_idx, isv_epsilon_eff, isv_tau_eff,
   isv_gamma_eff, isv_kelly_cap_eff, + 3 SchemaContract which already no-op).
   Fold boundary would error "unknown name 'isv_epoch_idx'". Added dispatch
   arms in training_loop.rs::reset_named_state — reset each to 0.0; GPU
   kernels repopulate on next epoch.

2. controller_activity smoke test's single 50% threshold was designed for
   reactive CPU-compute controllers. Under GPU-drives-CPU-reads, tau is a
   Polyak-EMA cosine schedule that fires every epoch by design (95%),
   gamma is health-coupled monotonic (may fire every epoch as health
   drifts). Split threshold per-controller: reactive (anti_lr, grad_clip,
   cql_alpha, cost_anneal) = 0.50; schedule-based (tau, gamma) = 1.00.

3. examples/train_baseline_rl was broken since the f64→f32 ABI refactor
   (d64adc14f) — hp_f64 returning f64 assigned to f32 fields. Added
   hp_f32 helper that narrows JSON-born f64→f32 at ingest boundary. Use
   hp_f32 for f32 fields, hp_f64 for f64 fields (learning_rate,
   entropy_coefficient, weight_decay). No more "as f32" casts at call
   sites. Also fixed replay_buffer_vram_fraction + bars_per_day f64→f32.

Local smoke tests now pass:
- controller_activity: ok (1 passed, 29.5s)
- multi_fold_convergence: ok (1 passed, 3 folds x 20 epochs, 534.9s)
- 24 new monitor + registry unit tests: all passing

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:58:13 +02:00
jgrusewski
0aec55dd70 fix(dqn-v2): atoms_monitor fire_rate test initial-value bug
The test observed `mon.observe(0.0)` first but the monitor's initial
last_value is also 0.0, so the first observation didn't fire. Test
expected 2/3 but got 1/3.

Changed observe sequence to (1.0, 1.0, 1.5) mirroring grad_balancer's
correct pattern: first differs from initial 0.0 → fires, second matches
→ no fire, third differs → fires = 2/3.

No production code change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:32:11 +02:00
jgrusewski
d7f1b9e9ae plan(dqn-v2): Plan 1 substrate + refactor complete (Tasks 9-17)
Appends Plan 1 completion note to the substrate plan doc, recording
all 10 commit SHAs from Tasks 8-17 and the exit criteria status.
Tasks 8-17 fully implemented: AdaptiveMonitor trait, 6 CPU-side read-only
monitors (atoms, gamma, kelly_cap, tau, epsilon, grad_balancer), 5 cold-path
GPU kernels (atoms_update, gamma_update, kelly_cap_update, tau_update,
epsilon_update), 3 static ISV constructor writes (cql_alpha, conviction_floor,
plan_threshold). All adaptive computation is now GPU-driven; CPU never
computes adaptive values (§4.C.6). Tasks 18.1-18.7 (validation run) pending.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:29:34 +02:00
jgrusewski
048c78d5f7 feat(dqn): Plan 1 Task 9 — atoms_update GPU kernel + AtomsMonitor + delete CPU paths
atoms_update_kernel.cu: 4-block kernel (grid=(4,1,1), block=(256,1,1)) replaces
the CPU loop that launched adaptive_atom_positions 4× per branch. Each block reads
ISV v-range slots [23..31) and spacing_raw params[52..56); computes softmax +
cumsum; writes atom_positions_buf[branch * num_atoms]. Same formula as the
existing adaptive_atom_positions kernel in experience_kernels.cu.

Deleted recompute_atom_positions and warm_start_atom_positions from GpuDqnTrainer
and their fused_training.rs delegates. step_atom_positions now calls
launch_atoms_update. training_loop.rs: recompute_atom_positions → launch_atoms_update;
warm_start_atom_positions + compute_reward_quantiles block removed.

AtomsMonitor reads ISV[V_CENTER_DIR_INDEX=23] as representative scalar;
diagnose() exposes all 8 v-range slots [23..31). state_reset_registry.rs
"follow-up task" descriptions updated for all 4 adaptive slots.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:28:19 +02:00
jgrusewski
1e90f74c8f feat(dqn): Plan 1 Task 11 — kelly_cap_update GPU kernel + KellyCapMonitor
kelly_cap_update_kernel.cu: single-thread cold-path kernel aggregates Kelly
win/loss stats across all n_envs from portfolio_states[n_envs, PS_STRIDE].
Computes mean half-Kelly × health-coupled safety multiplier (0.5+0.5*health)
and writes ISV[KELLY_CAP_EFF_INDEX=44]. Matching Bayesian priors from
trade_physics.cuh (prior_wins=2, prior_losses=2) prevent cold-start collapse.

GpuExperienceCollector::portfolio_states_dev_ptr() added — exposes device
pointer for the portfolio_states buffer without a GPU→CPU roundtrip.
GpuDqnTrainer::launch_kelly_cap_update takes ps_dev_ptr + n_envs.
FusedTrainingCtx delegates to trainer. training_loop.rs launches at epoch
boundary after gamma_update (requires both fused_ctx and gpu_experience_collector).

KellyCapMonitor reads ISV[44] for HEALTH_DIAG via AdaptiveMonitor trait.
state_reset_registry.rs description updated; audit docs updated (Invariant 7).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:19:45 +02:00
jgrusewski
c1b6848c21 feat(dqn-v2): C.6 Task 10 — gamma GPU kernel + CPU monitor (discount factor)
gamma_update kernel computes health-coupled gamma from ISV[LEARNING_HEALTH=12]:
gamma_eff = gamma_min + (gamma_base - gamma_min) * health. Single-thread
cold-path kernel writes ISV[GAMMA_EFF_INDEX=43].

GammaMonitor is a read-only observer exposing gamma_eff, health, fire_rate.

Consumer migration: fill_gamma_buf and IQL gamma computation now read
ISV[GAMMA_EFF_INDEX] via read_isv_signal_at (pinned, zero-copy). Training
loop passes hyperparams.gamma as gamma_base to the kernel.

Deleted: apply_adaptive_gamma method (GpuDqnTrainer + FusedTrainingCtx
delegates), set_adaptive_gamma method, adaptive_gamma field (GpuDqnTrainer
+ DQNTrainer), last_gamma_eff cached field + last_gamma_eff() delegate.
StateResetRegistry entry for adaptive_gamma removed (field gone).

Smoke test generalization.rs updated to check config gamma_base instead
of deleted adaptive_gamma field.

Tests: 3 monitor unit tests pass. cargo check -p ml at 8-warning baseline.

Plan 1 Task 10. Spec §4.C.6 (2026-04-24 revision).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:05:44 +02:00
jgrusewski
d346e03d48 feat(dqn-v2): C.6 Task 14 — epsilon GPU kernel + CPU monitor (exploration)
epsilon_update kernel computes effective epsilon from ISV[EPOCH_IDX=39,
TOTAL_EPOCHS=40, LEARNING_HEALTH=12] with cosine schedule (eps_start ->
eps_end) plus health-coupled boost (up to +0.1 at collapse). Single-thread
cold-path kernel writes ISV[EPSILON_EFF_INDEX=41].

EpsilonMonitor is a read-only observer exposing eps_eff, epoch_idx,
total_ep, health, progress, fire_rate in DiagSnapshot.

Wires epsilon kernel launch at epoch boundary alongside tau kernel. Consumer
migration: log_training_config ISV-adaptive epsilon block now reads
ISV[EPSILON_EFF_INDEX] instead of computing `base_floor * (0.5 + volatility)`.

Tests: 3 monitor unit tests pass. cargo check -p ml at 8-warning baseline.

Plan 1 Task 14. Spec §4.C.6 (2026-04-24 revision).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 17:53:30 +02:00
jgrusewski
1032be3897 feat(dqn-v2): C.6 Task 13 — tau GPU kernel + CPU monitor (Polyak EMA)
tau_update kernel computes Polyak-EMA tau from ISV[EPOCH_IDX=39,
TOTAL_EPOCHS=40, LEARNING_HEALTH=12] with cosine schedule + health-coupled
floor (rises to 0.01 at full collapse). Single-thread cold-path kernel
writes ISV[TAU_EFF_INDEX=42].

TauMonitor is a read-only observer exposing tau_eff, epoch_idx, total_ep,
health, progress, fire_rate in DiagSnapshot.

Wires EPOCH_IDX_INDEX CPU-born input write at epoch start via existing
write_isv_signal_at accessor (pinned-memory zero-copy). Also launches
tau_update kernel at epoch boundary before any tau consumer.

Adds read_isv_signal_at() to GpuDqnTrainer (symmetric counterpart to
write_isv_signal_at).

Deleted: compute_cosine_annealed_tau free function (fused_training.rs),
apply_health_coupled_tau_floor method (GpuDqnTrainer), last_tau_eff
cached field + last_tau_eff() delegate. 4 consumer sites in
fused_training.rs now read ISV[TAU_EFF_INDEX] directly.

Tests: 3 monitor unit tests pass. cargo check -p ml at 8-warning baseline.

Plan 1 Task 13. Spec §4.C.6 (2026-04-24 revision).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 17:49:04 +02:00
jgrusewski
4a48e99a48 feat(dqn-v2): C.6 Task 17 — grad_balancer CPU monitor
Adds CPU-side read-only GradBalancerMonitor for the existing
`grad_balance_isv_update` GPU kernel. Monitor reads ISV slots 31-35
(GRAD_NORM_TARGET_{DIR,MAG,ORD,URG}_INDEX + GRAD_SCALE_LIMIT_INDEX);
returns mean-of-4-targets as the representative scalar; exposes all 5
slots + mean + fire-rate via diagnose().

Creates the `trainers/dqn/monitors/` module directory. Subsequent tasks
add kernels + monitors for atoms, gamma, kelly_cap, tau, epsilon.

Tests: 3 unit tests pass (read returns mean, diagnose exposes 7 fields,
observe tracks fire-rate with 1e-6 epsilon).

No behavioural change — kernel unchanged, monitor is pure observer.

Plan 1 Task 17. Spec §4.C.6 (2026-04-24 revision).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 17:23:42 +02:00
jgrusewski
ac9bcab94a feat(dqn-v2): C.6 static ISV writes (Tasks 12, 15, 16) + slot pre-allocation
Lands the static-configuration branch of Plan 1 C.6: cql_alpha,
conviction_floor (no-op if IQL_BRANCH_SCALE_FLOOR already serves),
plan_threshold written to dedicated ISV slots at constructor. Consumer
kernels (CQL, backtest_plan, experience) read from ISV instead of
config fields / hardcoded literals.

Also pre-allocates ISV slots for the 6 upcoming GPU-kernel tasks
(atoms, gamma, kelly_cap, tau, epsilon outputs + CPU-born epoch inputs).
Those slots start at 0; GPU kernels in follow-up commits fill them.

New ISV slots:
- EPOCH_IDX_INDEX=39, TOTAL_EPOCHS_INDEX=40 (CPU-born inputs)
- EPSILON_EFF_INDEX=41, TAU_EFF_INDEX=42, GAMMA_EFF_INDEX=43,
  KELLY_CAP_EFF_INDEX=44 (GPU-written in follow-up tasks)
- CQL_ALPHA_INDEX=45, PLAN_THRESHOLD_INDEX=46 (static config; this commit)
- Task 15 confirmed no-op: IQL_BRANCH_SCALE_FLOOR_INDEX=36 already
  serves conviction-floor role (constructor + ISV read in iql kernel).

Layout fingerprint auto-updated via seed-byte edits; fingerprint
re-tail at [47..49). ISV_TOTAL_DIM 39 -> 49.

GpuDqnTrainConfig gains total_epochs field; fused_training.rs passes
hyperparams.epochs at construction; default 0 for smoke tests.

write_isv_signal_at bound extended from ISV_DIM(23) to ISV_TOTAL_DIM(49)
so tail slots are writable by CPU.

cql_alpha consumer: compute_cql_logit_gradients reads base from
ISV[CQL_ALPHA_INDEX] instead of config.cql_alpha; adaptive formula
(base x health x (1-regime_stability)) unchanged.

plan_threshold consumers: experience_kernels.cu (experience_state_gather,
experience_action_select, experience_env_step) and backtest_plan_kernel.cu
(backtest_plan_state_isv) read plan_thr from ISV[ISV_PLAN_THRESHOLD_IDX=46]
via isv_signals pointer already present in both kernels; null-guard defaults
to 0.5f for smoke-scale runs without ISV warmup.

ISV_PLAN_THRESHOLD_IDX=46 defined in state_layout.cuh (included by both
plan kernel files); value must match PLAN_THRESHOLD_INDEX in gpu_dqn_trainer.rs.

StateResetRegistry entries added for all new slots: SchemaContract for
TOTAL_EPOCHS/CQL_ALPHA/PLAN_THRESHOLD; FoldReset for EPOCH_IDX and
GPU-written per-fold slots.

No behavioural change: all threshold/base values remain at their prior
defaults; consumers now read adaptive values from ISV instead of baked-in
config or hardcoded literals.

Plan 1 Tasks 12, 15, 16 + pre-allocation for 9, 10, 11, 13, 14.
Spec §4.C.6 (2026-04-24 GPU-drives-CPU-reads revision).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 16:45:46 +02:00
jgrusewski
6cd00ab022 feat(dqn-v2): C.6 revise Task 8 — AdaptiveMonitor replaces AdaptiveController
Renames adaptive_controller.rs → adaptive_monitor.rs. Replaces the old
CPU-compute AdaptiveController trait with read-only AdaptiveMonitor per
spec §4.C.6 (2026-04-24 revision: GPU drives, CPU reads).

Trait changes:
- Remove update() — CPU does not compute adaptive values.
- Remove write_output() — CPU does not write adaptive values to ISV.
- Remove Signal/Control associated types.
- Add read(&IsvBus) -> f32 — pure read from ISV.
- Add observe(f32) — drives fire-rate tracking.
- Keep diagnose, fire_rate, name.

Type refinements carried forward:
- FireRateStats uses u32 counters (sufficient for per-fold tracking).
- DiagSnapshot uses &'static str keys + f32 values (zero allocation).
- IsvBus adds read/write/len; write reserved for CPU-born inputs.
- All types pub(crate) (consumed within the ml crate only).

Tests: 3 unit tests (FireRateStats arithmetic, DiagSnapshot builder,
IsvBus read/write).

Plan 1 Task 8 (revised). Spec §4.C.6 (2026-04-24 revision).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 16:31:46 +02:00
jgrusewski
0357c03918 revert(dqn-v2): Batch A+B CPU-compute controller migrations
Reverts commits d76849f31 (Batch A: atoms, gamma, kelly_cap, cql_alpha)
and 4189da563 (Batch B: tau, epsilon, conviction_floor, plan_threshold).

The reverted commits implemented 8 controllers under the old
AdaptiveController trait which had CPU-side update() that computed
adaptive values and write_output() that pushed them to ISV. This
violates the architectural principle codified in the §4.C.6 revision
(commit cf36091ee): GPU kernels compute all adaptive decisions; CPU is
pure observation.

The 8 migrations will be re-implemented under the new AdaptiveMonitor
pattern:
- 6 reactive mechanisms (atoms, gamma, kelly_cap, tau, epsilon,
  grad_balancer) get new GPU kernels + read-only CPU monitors.
- 3 static mechanisms (cql_alpha, conviction_floor, plan_threshold)
  get ISV constructor-writes (no kernel, no monitor).

After this revert:
- AdaptiveController trait is back on main (from Task 8's 419c24b4f).
  It will be replaced with AdaptiveMonitor in the next commit per the
  revised Plan 1 Task 8.
- StateResetRegistry (from Task 2's b688827d6) stays intact.
- Tasks 1-7 completed work unchanged.

Tests: cargo check -p ml at 8-warning baseline after revert.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 16:21:03 +02:00
jgrusewski
cf36091eeb spec+plan(dqn-v2): GPU drives, CPU reads (§4.C.6 revision)
User raised that the CPU-controller pattern (where CPU computes adaptive
values from ISV and writes back to ISV) violates the architectural
principle: GPU kernels should compute adaptive decisions; CPU-side code
is pure observation.

Replaces the AdaptiveController trait with read-only AdaptiveMonitor:
- No update() — CPU doesn't compute adaptive values.
- No write_output() — CPU doesn't write adaptive values to ISV.
- read() returns the GPU-computed value from ISV.
- diagnose() emits HEALTH_DIAG snapshot.
- observe() + fire_rate() track how often the GPU-computed value changes.

Reclassifies the 9 adaptive mechanisms:
- 6 reactive get GPU kernel + CPU monitor: atoms, gamma, kelly_cap, tau
  (Polyak EMA), epsilon, grad_balancer (last is already GPU-driven).
- 3 static get ISV constructor-write, no monitor: cql_alpha,
  conviction_floor, plan_threshold.

New ISV slots for GPU-written adaptive outputs (EPSILON_EFF, TAU_EFF,
GAMMA_EFF, KELLY_CAP_EFF) and CPU-born inputs (EPOCH_IDX, TOTAL_EPOCHS),
plus slots for the 3 static configs.

Rationale:
- Unified adaptive machinery, no CPU-side special cases.
- Per-sample granularity available (Expected SARSA τ in c51_loss_kernel
  is already the exemplar — reads ISV q_gap + health per sample).
- Zero CPU→GPU config transfer in any path.
- ISV is single source of truth for every adaptive value.

Plan 1 Tasks 8-17 restructured:
- Task 8 creates AdaptiveMonitor trait (not AdaptiveController).
- Tasks 9, 10, 11, 13, 14, 17: GPU kernel + monitor pairs.
- Tasks 12, 15, 16: static ISV writes only.

Follow-up commits will revert Batch A (d76849f31) and Batch B (4189da563)
which implemented the old CPU-compute pattern, then re-implement under
this design.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 16:20:24 +02:00
jgrusewski
4189da563f feat(dqn-v2): C.6 migrate 4 more controllers to AdaptiveController (Tasks 13-16)
Tasks 13-16 of Plan 1 §4.C.6 — Batch B controller migrations:

- Task 13 (TauController): cosine-annealed Polyak EMA coefficient with
  health-coupled floor.  Mirrors compute_cosine_annealed_tau +
  apply_health_coupled_tau_floor from fused_training.rs/gpu_dqn_trainer.rs.
  Wired at epoch-boundary B2/G3 block in training_loop.rs; per-step
  actuators (set_tau_host, target_ema_update) remain in fused_training.rs.
  8 unit tests.

- Task 14 (EpsilonController): ISV-adaptive exploration epsilon.
  Replaces inline base_floor*(0.5+volatility) block (~10 lines) in
  initialize_epoch_state.  Volatility from ISV[2]; agent.set_epsilon()
  actuator call preserved.  8 unit tests.

- Task 15 (ConvictionFloorController): IQL branch_scales per-sample floor
  scaffolding.  Static 0.1 (SchemaContract slot ISV[36]).  write_output
  writes to ISV[36].  Never fires.  Plan 3 adds ramp logic.  7 unit tests.

- Task 16 (PlanThresholdController): plan activation threshold scaffolding.
  Static 0.5, mirrors the > 0.5f literal in experience_kernels.cu and
  backtest_plan_kernel.cu.  Never fires.  Plan 3 §B.4 wires ISV slot.
  6 unit tests.

All 29 new tests pass.  Cargo check: 8 warnings (unchanged from baseline).
Audit doc updated per Invariant 7.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 15:03:19 +02:00
jgrusewski
d76849f31a feat(dqn-v2): C.6 migrate 4 ad-hoc controllers to AdaptiveController (Tasks 9-12)
Establishes the AdaptiveController trait (Plan 1 §4.C.6 prerequisite) and
migrates all four epoch-boundary controllers in a single atomic commit per
feedback_no_partial_refactor.md — shared consumers (training_loop.rs,
trainer/mod.rs, constructor.rs) cannot be partially migrated.

Task 9 — AtomsController: ISV v-range → AtomControl; gates
recompute_atom_positions(). Old direct fused.recompute_atom_positions() call
replaced by controller.update() + actuator.

Task 10 — GammaController: ISV[6] atom_util_ema → GammaControl.gamma;
inline ~18-line G4 block removed; gamma_controller.update() replaces it.

Task 11 — KellyCapController: TradeStats counters → KellyCapControl.kelly_half;
inline ~20-line kelly_f_mean block removed; kelly_cap_controller.update() replaces it.

Task 12 — CqlAlphaController: scaffolding that returns static config alpha;
Plan 3 adds seed-phase coupling. No prior ad-hoc function; establishes the
trait slot.

Tests: 26 new unit tests covering update/diagnose/fire_rate for all 4
controllers. All pass. Cargo check -p ml: 8 warnings (baseline unchanged).

Plan 1 Tasks 9-12. Spec §4.C.6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 14:48:19 +02:00
jgrusewski
419c24b4f3 feat(dqn-v2): C.6 AdaptiveController trait + test harness
Unified protocol for every adaptive controller in the DQN trainer.
FireRateStats for controller_activity smoke. DiagSnapshot for
standardised HEALTH_DIAG emission. IsvBus abstraction over pinned
device-mapped memory (&mut [f32] wrapper).

No concrete controller migration yet — Tasks 9-17 migrate existing
controllers (atoms → gamma → Kelly → cql_alpha → tau → epsilon →
conviction_floor → plan_threshold → balancer) one per sub-commit.
Old scaffolding for each controller is removed in the SAME commit
that migrates its last consumer to the new protocol, per
feedback_no_partial_refactor.md.

Tests: 2 unit tests on FireRateStats and DiagSnapshot.

Plan 1 Task 8. Spec §4.C.6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 14:15:12 +02:00
jgrusewski
daadae042e audit+fix(dqn): Task A.6 hot-path purity — 4 MIGRATE sites eliminated, 0 remaining
Enumerated every DtoH/HtoD/memcpy call in the DQN hot path
(run_full_step → child graphs).  Classified 55 call sites:
31 OK-pinned, 20 COLD-PATH, 4 MIGRATED.

Fix 1 (gpu_dqn_trainer.rs): removed dead cuMemcpyDtoHAsync_v2 in
run_causal_intervention_unconditional — result (causal_mean_scratch)
was never consumed; now stays on device.

Fix 2 (fused_training.rs + training_loop.rs): compute_iqr() was
called inside submit_aux_ops (captured aux_child graph).  Its sync
cuMemcpyDtoH_v2 cannot be graph-captured — silently ran only during
capture, then HtoD replayed stale IQR data on every step.  Removed
from submit_aux_ops; added refresh_iqn_iqr() called once per epoch
in process_epoch_boundary.

Fix 3 (gpu_iqn_head.rs): tau_buf (CudaSlice<f32>) + tau_host (f32) +
cuMemcpyHtoDAsync_v2 each step replaced by tau_pinned (*mut f32) +
tau_dev_ptr (u64) via cuMemAllocHost_v2(DEVICEMAP) +
cuMemHostGetDevicePointer_v2.  CPU writes *tau_pinned = tau; EMA
kernel reads via tau_dev_ptr — zero PCIe overhead.  Drop updated.

Audit table populated in docs/dqn-gpu-hot-path-audit.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 13:57:52 +02:00
jgrusewski
fb6d0f40f9 audit(dqn-v2): A.5 orphan audit complete
Populated docs/dqn-wire-up-audit.md with every pub module and CUDA
kernel in the DQN path. Each entry classified Wired / Partial / Orphan /
Ghost / OUT-of-DQN-scope with the action plan linking to the plan+task
that resolves any non-Wired status.

No Orphan left unclassified. Orphans fall into three buckets:
1. Scheduled for wiring by a later Plan (gpu_statistics → Plan 2 D.2;
   tlob_loader → Plan 2 D.8).
2. OUT-of-DQN-scope because supervised consumers exist (PPO kernels,
   xLSTM, KAN trainable adapter, flash_attention, benchmarks).
3. Genuinely unused — escalated to user review in the task output,
   not deleted autonomously (streaming_dbn_loader, unified_data_loader,
   training/orchestrator, training_pipeline, inference_validator,
   model_loader_integration, paper_trading/mod.rs,
   portfolio_transformer, regime_detection/mod.rs).

Summary: 109 total modules/kernels, 74 wired, 7 partial, 11 orphan,
0 ghost, 17 OUT-of-DQN-scope.

Plan 1 Task 6. Spec §4.A.5, Invariant 2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 13:10:49 +02:00
jgrusewski
fbb8694a0b feat(dqn-v2): A.2 ISV layout fingerprint at ISV[37..39) (tail placement)
Implements spec §4.A.2 structural layout fingerprint with tail placement
rather than head placement (spec alternative: §4.A.2 Step 5.3 alt).

Head placement (ISV[0..2)) was rejected because isv_signals[0] and [1]
are actively written by the isv_signal_update kernel (Q-drift EMA and
gradient-norm EMA). Shifting those would require updating every literal
reference in experience_kernels.cu — a larger change than warranted for
pure contract enforcement. Tail placement leaves all existing indices
intact, touches zero kernel .cu files, and fulfils the same design contract.

Key changes:
- ISV_LAYOUT_FINGERPRINT_LO_INDEX = 37, HI_INDEX = 38 (u64 across 2×f32).
- LAYOUT_FINGERPRINT_CURRENT: u64 = FNV-1a of slot-list seed bytes.
  Value: 0x85d4d76b578a7c17. Any slot change updates seed bytes,
  which updates the hash automatically.
- Constructor writes fingerprint after zero-init; calls
  check_layout_fingerprint() to self-verify before returning.
- check_layout_fingerprint(): reads pinned slots [37..39), recomposes u64,
  fails-fast on mismatch with "retrain required" message.
- Error message does NOT mention migration as an option.
- Pre-commit hook rejects `fn migrate_isv|upgrade_isv` names — makes the
  no-migration rule structurally enforced (check_no_isv_migrations).
- ISV_TOTAL_DIM: 37 → 39.
- Zero existing index shifts (no kernel literal sites affected).
- StateResetRegistry entry renamed ISV_SCHEMA_VERSION → ISV_LAYOUT_FINGERPRINT.
- ResetCategory::SchemaContract docstring updated to remove "migration" framing.
- docs/isv-slots.md: updated table + design note for tail placement.

Tests: state_reset_registry 3 unit tests pass with renamed entry.
       cargo check -p ml clean (pre-existing warnings only).

Plan 1 Task 5. Spec §4.A.2 (tail-placement alternative).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:40:59 +02:00
jgrusewski
1353e71ae6 spec+plan(dqn-v2): layout fingerprint replaces schema version (§4.A.2)
User raised a backward-compat concern: integer "schema version"
semantically implies a family of coexisting versions with upgrade
paths between them, inviting the forbidden pattern
(feedback_no_legacy_aliases.md, feedback_no_partial_refactor.md).

Replace with a compile-time structural fingerprint:
- ISV[0..2) stores a u64 FNV-1a hash of the slot layout (split across
  two f32 lanes preserving raw bits).
- The hash is computed by a const fn over the slot list; any slot
  change automatically updates the fingerprint. No human decides
  "what version is this now".
- Checkpoint load is fail-fast only. Error message does NOT mention
  migration as an option.
- Pre-commit hook rejects any `fn migrate_isv|upgrade_isv` to make
  the no-migration rule structurally enforced (landed as part of
  Plan 1 Task 5 hook extension).
- StateResetRegistry entry renamed ISV_SCHEMA_VERSION →
  ISV_LAYOUT_FINGERPRINT (Task 2's landed code touched in Task 5's
  commit per no-partial-refactor).

Updated:
- spec §4.A.2 (fingerprint design + rationale)
- spec §5 landing-order note (fingerprint auto-updates on layout change)
- Plan 1 architecture line
- Plan 1 Task 2 StateResetRegistry test + entry naming
- Plan 1 Task 5 — full rewrite of implementation steps
- Plan 1 exit criteria #6
- Plan 2 pre-plan verification (grep fingerprint constants, not version==0)
- Plan 2 Task 6D.1 (update fingerprint seed, not bump version)
- Plan 2 Task 6D exit criteria #7
- Plan 3 pre-plan verification
- docs/isv-slots.md ISV[0..2) row

No code changed. Plan 1 Task 5 is not yet implemented — this commit
realigns the spec + plans so the implementer subagent works from the
corrected design.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:47:07 +02:00
jgrusewski
d6e8131d66 refactor(dqn-v2): Task 4 gap-fix — missed sites + Rust mirror
Follow-up to Plan 1 Task 4 sub-commits 4A-4F. Addresses 3 issues
surfaced by code review:

1. experience_kernels.cu:1148-1152 — 4 raw literals missed by Task 4A/4E
   sed sweep (the block uses portfolio_states[ps_base_plan + N] syntax
   rather than ps[N], so sed targeting \bps\[ didn't match).
   Fixes: ps_base_plan + 23 -> PS_PLAN_TARGET_BARS, + 0 -> PS_POSITION,
          dir_idx = 2 -> DIR_LONG, dir_idx = 0 -> DIR_SHORT.

2. experience_kernels.cu:1194 — flat_idx = 3 replaced with DIR_FLAT.

3. crates/ml-core/src/state_layout.rs — Rust mirror added for every
   Task 4 constant (PS_*, PLAN_ISV_*, PLAN_PARAM_*, BRANCH_*, DIR_*,
   MAG_*) matching cuh byte-for-byte. Closes the half-applied Invariant
   8 gap for the Rust side.

No behavioural change. Pure refactor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:37:00 +02:00
jgrusewski
7ace16de2f docs(dqn): update dqn-named-dims.md with 4F commit SHA
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 11:37:00 +02:00
jgrusewski
a7c4bc9a90 refactor(dqn): Plan 1 Task 4F — MAG_* magnitude sub-index named constants (Invariant 8)
Define MAG_QUARTER=0, MAG_HALF=1, MAG_FULL=2, NUM_MAGNITUDES=3 in
state_layout.cuh. Migrate the one unambiguous semantic comparison in
trade_physics.cuh (compute_target_position_4branch magnitude scaling).
Arithmetic operations on mag_idx (mag_idx±1, 2-mag_idx, mag_idx<2) are
runtime values, not semantic comparisons, and are not migrated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 11:37:00 +02:00
jgrusewski
2ecf36b94c refactor(dqn): Plan 1 Task 4E — DIR_* direction sub-index named constants (Invariant 8)
Define DIR_SHORT=0, DIR_HOLD=1, DIR_LONG=2, DIR_FLAT=3, NUM_DIRECTIONS=4 in
state_layout.cuh. Migrate all literal dir_idx/raw_dir comparisons to named
constants across experience_kernels.cu (15 sites), trade_physics.cuh (3 sites),
and backtest_metrics_kernel.cu (2 sites). Raw integer comparisons (== 0/1/2/3)
eliminated from all direction-branch consumers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 11:37:00 +02:00
jgrusewski
fc2d65c1a3 refactor(dqn-v2): Invariant 8 — named constants for branch indices
Add BRANCH_DIR/BRANCH_MAG/BRANCH_ORD/BRANCH_URG/NUM_BRANCHES to
state_layout.cuh. Migrate the 4 clear literal branch-index accesses
in branch_grad_balance_kernel.cu (branch_norms_dev[0..3] in
grad_balance_isv_update).

Other branch-keyed arrays (branch_starts, branch_lens, branch_norms,
branch_scales) use the runtime `branch = blockIdx.y` variable or loop
counter — no raw literal index, so no migration needed. Rust call sites
use struct fields (branch_0_size etc.) or loop vars — not literals.

No behavioural change; pure refactor.

Plan 1 Task 4D. Spec §3 Invariant 8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:37:00 +02:00
jgrusewski
a5a67ad19a refactor(dqn-v2): Invariant 8 — named constants for plan_params[0..6)
Add PLAN_PARAM_* constants to state_layout.cuh and replace raw
plan_params/pp slot accesses in experience_kernels.cu and
backtest_plan_kernel.cu.

Sites migrated: pp[0..5] in both files (12 sites), plan_params_ptr
indexed accesses at slots 0, 1, 4 in experience_kernels.cu (4 sites),
plan_params[w*6+4] in backtest_plan_kernel.cu (1 site). Loop variable
pp[p] in noisy-plan path left as-is (p is a runtime loop counter, not
a literal slot index).

No behavioural change; pure refactor.

Plan 1 Task 4C. Spec §3 Invariant 8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:37:00 +02:00
jgrusewski
f36b1bde7a refactor(dqn-v2): Invariant 8 — named constants for plan_isv[0..6)
Add PLAN_ISV_* constants to state_layout.cuh and replace raw
plan_isv[N] and pisv[N] accesses in experience_kernels.cu and
backtest_plan_kernel.cu. backtest_plan_kernel.cu gains an
#include "state_layout.cuh" so the constants are visible.

6 code sites migrated (plan_isv[0..5] in experience_kernels.cu),
plus 6 pisv[N] local-array accesses in backtest_plan_kernel.cu
(same semantic slots).

No behavioural change; pure refactor.

Plan 1 Task 4B. Spec §3 Invariant 8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:37:00 +02:00
jgrusewski
7a2adeb374 refactor(dqn-v2): Invariant 8 — named constants for portfolio state ps[0..PS_STRIDE=38)
Replace raw ps[N] access across all CUDA kernels with PS_* named
constants defined in state_layout.cuh. 32 constants total covering
the full 38-slot portfolio state: position/cash/value, DSR stats,
Kelly stats, plan fields, and OFI scratch range.

Notable deviations from pre-written plan mapping: the plan's
PS_VALUE/PS_POSITION/PS_CASH values (0,1,2) had the wrong semantics.
Code wins (feedback_trust_code_not_docs): ps[0]=position, ps[1]=cash,
ps[2]=portfolio_value. All 148 raw ps[digit] accesses in
experience_kernels.cu and trade_stats_kernel.cu migrated. In-comment
range references (e.g. "ps[30..37]") left as documentation.

trade_stats_kernel.cu: migrated base+14 offset to PS_KELLY_WIN_COUNT.
docs/dqn-named-dims.md: PS_* table corrected to match actual code layout.

No behavioural change; pure refactor.

Plan 1 Task 4A. Spec §3 Invariant 8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:37:00 +02:00
jgrusewski
46e99c5ccc feat(dqn-v2): A.1 wire StateResetRegistry into fold-boundary reset
Replaces the ad-hoc scattered fold-boundary reset calls with a single
registry-driven iteration. Adding new fold-reset state now requires
adding a registry entry AND a dispatch arm in reset_named_state in the
same commit (Invariant 2 Wire-It-Up).

Step 3.4 correction: plan_state entry removed from the registry —
plan_state_buf exists only in GpuBacktestEvaluator (val path), not in
the training-path fused ctx. No training-side fold reset is applicable.

New behaviour: isv_learning_health, isv_sharpe_ema, isv_q_means are
now properly reset to baseline at each fold boundary (previously unset,
which allowed signals from fold N to bias fold N+1 initialisation).

Tests: 3 registry unit tests pass; cargo check -p ml clean (8 pre-existing
warnings only, no new).

Authority: spec §4.A.1. Plan 1 Task 3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:10:11 +02:00
jgrusewski
b688827d66 feat(dqn-v2): A.1 state reset registry
Typed classification of every piece of training state by reset
lifecycle: FoldReset, WindowReset, SoftReset(decay_bars),
TrainingPersist, SchemaContract.

Replaces the previous vibes-driven fold-boundary reset logic with an
explicit registry. Adding new state requires adding an entry in the
same commit (Invariant 2 Wire-It-Up).

Consumer wiring (reset_fold_state, reset_window_state helpers that
iterate the registry) follows in Task 3 of Plan 1.

Tests: 3 unit tests covering category lookup, fold-reset filtering,
unknown-name handling.

Authority: docs/superpowers/specs/2026-04-24-dqn-v2-unified-design.md §4.A.1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:38:15 +02:00
jgrusewski
06989cfdf9 infra(dqn-v2): audit doc scaffolding + pre-commit enforcement
Plan 1 Task 1. Creates the five audit docs plus config/metric-bands.toml
that track Invariants 2, 7, 8 per the DQN v2 spec, and extends the
pre-commit hook with two checks:

  - component-adding commits must touch an audit doc (Invariant 7)
  - added code may not contain TODO/FIXME/XXX/HACK/TBD/unimplemented!/
    todo! markers (Invariant 9)

Tests: manually verified by staging a TODO-marked file; commit
rejected with the correct error message.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:25:27 +02:00
jgrusewski
3ab87d7672 plan(dqn-v2): Plan 3 self-review fix — remove unimplemented!() from example
Self-review caught that the CqlAlphaSeedCoupledController read_signals
example used unimplemented!("plumbed by caller"), which violates the very
Invariant 9 the plan enforces. Replaced with a concrete implementation
that reads SEED_FRACTION_EMA_INDEX from the ISV bus (a Plan 1 reserved
controller-signal slot).

No other plan had placeholder violations. The remaining TBD/TODO/FIXME
grep hits across Plans 1-5 are either the pre-commit hook's rejection
patterns (correctly quoted) or enforcement scans that DETECT the markers
in audit docs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:07:10 +02:00
jgrusewski
74071af908 plan(dqn-v2): Plan 5 — validation substrate + final pass implementation plan
Fifth and final plan decomposing the DQN v2 unified spec
(docs/superpowers/specs/2026-04-24-dqn-v2-unified-design.md §2, §4.A.3,
§4.A.4, §4.A.4.1). No new policy mechanisms — orchestration, automation,
regression detection, and the final sign-off run.

Covers spec sections:
- §4.A.3 Multi-seed × multi-fold Argo harness (N=5, K=6 defaults via
  --multi-seed / --folds flags on argo-train.sh)
- §4.A.4 Regression detection hard-stop (2N consecutive error-band
  metrics → self-SIGTERM with TerminationReason)
- §4.A.4.1 nsys profile harness with --profile flag and
  compare-nsys-profiles.py for 20% per-epoch regression detection
- §2 Tier 1 + Tier 2 + Tier 3 final exit criteria via scripts/validation/
  suite (tiered tier1/tier2/tier3 check scripts + wrapper)
- §2 DQN v2 rollout close-out (spec marked LANDED, retrospective,
  plan-level validation docs archived)

Plan structure: 6 tasks — 4 infrastructure (harness, regression-detect,
nsys, validation-scripts) + final-run task + rollout-closeout task. The
final-run task is the sole exit-gate for the DQN v2 rollout: ALL THREE
tiers must PASS on the first final-validation run (per stop-on-anomaly:
retrying with different seeds hoping for a different outcome is anti-
pattern).

Preserves all 9 invariants. Every mechanism wired, no stubs, no TODO/FIXME.
Retrospective written from actual implementation experience after Plans
1-5 all land.

This is the terminal plan. Plans 1-5 together constitute the complete
DQN v2 rollout. After Plan 5's final-run exit passes, the spec is marked
LANDED and the rollout is declared complete.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:06:25 +02:00
jgrusewski
691d769bb3 plan(dqn-v2): Plan 4 — supervised→DQN concept adoption implementation plan
Fourth of five sequential plans decomposing the DQN v2 unified spec
(docs/superpowers/specs/2026-04-24-dqn-v2-unified-design.md §4.E).

Covers the six Part E IN decisions:
- §4.E.1 TFT Variable Selection Network across 6 feature groups
  (market/OFI/TLOB/MTF/portfolio/plan_isv) with vsn_feature_selection kernel
- §4.E.2 Gated Residual Network — CONDITIONAL branch (ADOPT vs CANONICALISE)
  based on docs/ml-supervised-to-dqn-concept-audit.md row
- §4.E.3 Multi-quantile IQN heads (5/25/50/75/95) replacing single CVaR output
- §4.E.4 Encoder-Decoder separation — explicit StateEncoder + per-branch
  ValueDecoder with D.3 horizon-decomposed V_short/V_long sub-heads
- §4.E.5 Attention-weight interpretability — 7 new ISV slots [65..72) for
  per-group attention focus EMAs + Mamba2 retention proxy
- §4.E.6 Multi-task auxiliary heads — next-bar return MSE + 5-bar regime CE,
  ISV-coupled aux-weight schedule (sharpe-reactive)

ISV_TOTAL_DIM seals at 72 with this plan's allocations. Part E audit doc
closes out all rows (zero TBD/evaluate remain per Invariant 9).

Plan structure: 8 tasks (6 feature tasks + audit close-out + validation).
TDD-disciplined steps. Task 2 documents the CONDITIONAL branch decision
pathway explicitly (ADOPT vs CANONICALISE Branch A/B structure).

Plan 4 exit gate: Tier 1 convergence RETAINED (no regression vs Plan 3
baseline) + aux heads produce measurable signal. Tier 2 + Tier 3 checked
by Plan 5.

Preserves all 9 invariants. Zero stubs. Zero TODO/FIXME. Every Part E item
either lands fully or is explicit OUT (xLSTM, KAN); no third path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:03:13 +02:00
jgrusewski
c70bbd5d15 plan(dqn-v2): Plan 3 — behavioural core implementation plan
Third of five sequential plans decomposing the DQN v2 unified spec
(docs/superpowers/specs/2026-04-24-dqn-v2-unified-design.md).

Covers spec sections:
- §4.C.2 Reward-component attribution (6 ISV slots [52..58), reward_split[..]
  in HEALTH_DIAG) — lands first as diagnostic substrate for the rest
- §4.B.1 ISV-driven continuous Flat opp-cost (scales by isv[Q_DIR_ABS_REF])
- §4.B.2 Novelty-weighted trade-attempt bonus (2 ISV slots [50, 51])
- §4.B.4 Adaptive plan-MLP activation threshold (1 ISV slot [49] +
  PlanMlpThresholdController via the AdaptiveController trait)
- §4.C.4 Temporal timing bonus (trade-exit, bars_early / hold_time)
- §4.D.4 Generalised temporal reward coupling (3 ISV slots [59..62):
  persistence credit, regime-shift penalty, conviction consistency)
- §4.C.3 State-distribution divergence signal (3 ISV slots [58, 63, 64])
- §4.B.3 Replay buffer seeded warm-start (4 scripted policies,
  ≥100K experiences, CQL α decoupled from seed phase)
- §4.C.5 CQL alpha schedule coupled to seed-phase decay via
  CqlAlphaSeedCoupledController

Plan structure: 9 implementation tasks + pre-plan verification + exit-gate
validation task (Task 10). Each task TDD-disciplined with bite-sized
steps. Task 1 (C.2 diagnostic audit) ordered first so subsequent
reward-shape tasks can verify contributions rather than log-archaeology.

Plan 3 exit gate: multi-seed (N=5) × multi-fold (K=6) run passes Tier 1
(convergence) + Tier 2 behavioural subset (val trades/bar ≥ 0.005,
val_active_frac > 0.2). Tier 3 profitability deferred to Plan 5.

Preserves all 9 invariants. Zero stubs. Zero TODO/FIXME. Every new module
wired to its consumer in the same task it lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:59:32 +02:00
jgrusewski
a5101eb2f8 plan(dqn-v2): Plan 2 — temporal core implementation plan
Second of five sequential plans decomposing the DQN v2 unified spec
(docs/superpowers/specs/2026-04-24-dqn-v2-unified-design.md).

Covers spec sections:
- §4.C.1 Quantile-based atom support (8 new ISV slots, q_quantile_reduce kernel)
- §4.D.1 Mamba2 backward pipeline completion (no atomicAdd — per-sample arrays + host reduce)
- §4.D.2 Per-branch gamma via AdaptiveController (4 new ISV slots)
- §4.D.5 Soft fold-boundary transitions (extends StateResetRegistry with SoftReset category)
- §4.D.7 Liquid Time-constant audit (trace fire rate, decide wire-or-delete)
- §4.D.3 + §4.D.6 + §4.D.8 coordinated state-layout migration (horizon-decomposed V +
  plan_isv[6] + TLOB integration with atomic ISV schema version bump 1→2)

Plan structure: 7 tasks + pre-plan verification, all TDD-disciplined with
bite-sized steps (test → fail → implement → pass → commit). Task 6 is
explicitly atomic to preserve Invariant 5 (state-layout consistency).

Plan 2 prerequisites: Plan 1 landed (StateResetRegistry, AdaptiveController
trait, ISV schema version, audit docs). Plan 2 exit: all 9 invariants
preserved; convergence-scaffolding run passes with 16 new ISV slots
populated; ISV schema version == 2.

Preserves all 9 invariants. No stubs. No TODO/FIXME. Every new module
wired to production path in the same task it lands in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:53:11 +02:00
jgrusewski
e5f6afca8d plan(dqn-v2): Plan 1 — substrate & refactor implementation plan
First of 5 implementation plans for the DQN v2 unified integrated
policy system spec (d13b53586, 336ee40b9). Plan 1 covers:

  Task 1: Audit doc scaffolding + pre-commit hook (Invariant 7, 9)
  Task 2: StateResetRegistry definition (A.1)
  Task 3: Wire StateResetRegistry into fold-boundary reset (A.1)
  Task 4: Named-dimension refactor — ps[], plan_isv[], plan_params[],
          branch indices, dir/mag sub-indices (Invariant 8)
  Task 5: ISV schema version at ISV[0], fail-fast on checkpoint
          mismatch (A.2)
  Task 6: Orphan audit populated (A.5)
  Task 7: Hot-path purity audit populated; MIGRATE calls fixed (A.6)
  Task 8: AdaptiveController trait + harness (C.6)
  Tasks 9–17: Migrate each adaptive controller to the trait in spec-
            specified order (atoms → gamma → Kelly → cql_alpha → tau
            → epsilon → conviction_floor → plan_threshold → balancer)
  Task 18: Plan 1 validation run (smoke + 3-epoch L40S)

Plan 1 landing criteria: all 9 invariants preserved across every
commit, all smoke tests pass, audit docs fully populated. Plan 2
(temporal core) starts only after Plan 1 passes exit criteria.

Plans 2–5 cover Parts B, C (minus C.6 already in Plan 1), D, E, and
final validation. Planned as separate documents in docs/superpowers/
plans/2026-04-24-dqn-v2-plan-{2..5}-*.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:41:22 +02:00
jgrusewski
336ee40b9d spec(dqn): add Invariant 9 — no deferred work, no stubs, concrete E decisions
User-mandated addition:

Invariant 9 — No deferred work, no stubs, no TODO/FIXME
Every commit lands complete. No stubs (placeholder return values), no
TODO/FIXME/XXX/HACK/TBD markers, no half-finished implementations. If
it can't finish in this commit, it doesn't start. Stubs and deferrals
train the network on semantic emptiness — invisible in convergence
metrics but burn GPU time optimising against partly-fake signal.
Authority: feedback_no_stubs.md, feedback_no_todo_fixme.md,
feedback_no_quickfixes.md elevated to first-class spec enforcement.
Pre-commit hook greps for forbidden markers.

Part E decisions made concrete (per Invariant 9):
- E.1 TFT VSN: IN (full VSN extension)
- E.2 GRN: IN if A.5 audit finds absent (otherwise mark existing as canonical)
- E.3 Multi-quantile heads: IN (5/25/50/75/95 quantile decomposition)
- E.4 Encoder-decoder: IN (extends D.3 to full trunk/head separation)
- E.5 Interpretability: IN (attention-weight ISV diagnostics)
- E.6 Auxiliary heads: IN (next-return + regime-classification)
- xLSTM: OUT (redundant with Mamba2+TLOB; YAGNI)
- KAN: OUT (function approx not a bottleneck)
No "evaluate later" items. Every concept either lands or explicitly
doesn't, with rationale.

§8 decisions tightened:
- D.8 TLOB mode: decision criterion = per-step cost benchmark ≤ 5ms
- C.6 controller order: fixed in spec (atoms→gamma→Kelly→cql_alpha→
  tau→epsilon→conviction_floor→plan_threshold→balancer)
- A.3 resource plan: auto-switch L40S→H100 at 12 GPU-hour threshold
- A.4 metric band init: [mean − 3σ, mean + 3σ] from last 3 good runs

No discretionary "we'll see" remain. All decisions data/order/budget/
statistic-driven.

Counter updates: "seven invariants" → "nine" in §3 header and §5
cleanup contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:32:22 +02:00
jgrusewski
3a62e23056 spec(dqn): review fixes — clarify hot-path scope, add Invariant 8 named dims
Self-review pass on the DQN v2 unified design spec (d13b53586). Fixes
applied inline:

Ambiguity fixes:
- §3 Invariant 3: explicit definition of "training step loop" (per-step
  code in fused_training::step_fused and captured graph children; NOT
  per-epoch / per-fold / per-checkpoint code).
- §2 Tier 1: "stable epochs" defined as epochs [warmup_end..run_end)
  with warmup_end from B.3's seed-phase decay or explicit flag.
- §4 A.2: migration mechanism clarified as fail-fast initially; helpers
  added only when a specific migration need arises (no speculative
  scaffolding).
- §4 A.3: N=5, K=6 stated as DEFAULTS with ≥ MINIMUMS per Invariant 6;
  resource budget flagged (~8-10 GPU-hours per validation pass).
- §4 B.3: "≥ 100K experiences" (minimum), warm-restart clarified as
  runtime condition not feature flag.
- §4 C.3: adaptive KL threshold mechanism made explicit (second ISV
  slot for threshold EMA, third for amplification multiplier).
- §4 C.6: cleanup timing explicit — old scaffolding removed in the SAME
  commit that migrates its last consumer (no deferred pass).
- §4 D.1: DQN-path-specific scope clarified; validation via grad-norm
  smoke + integration test.
- §4 D.6: plan_isv index naming made consistent with existing layout;
  coordinated state-layout migration commit called out.

New Invariant 8 — Named dimensions, not indices:
Every dimension, slot, offset, or semantic position in a multi-field
buffer has a named constant. Raw numeric indices (`[0]`, `[6]`, `[23]`)
appear ONLY in the definition site. Named constants specified for ISV
slots (existing), portfolio state ps[0..30), plan_isv[0..7), plan_params
[0..6), state-vector offsets, branch indices (BRANCH_DIR/MAG/ORD/URG),
action sub-indices (DIR_SHORT/HOLD/LONG/FLAT, MAG_QUARTER/HALF/FULL).
Enforcement: audit pass during A.1/A.2, lint via grep for raw-index
access outside definition modules.

Landing order revision:
- Dependency graph explicit (A.2 before new ISV slots, D.1 before
  D.2-4-8, state-layout changes in ONE coordinated commit).
- Spec decomposition note added — writing-plans may produce multiple
  sequential plans rather than one monolithic plan.

New doc tracked by pre-commit: docs/dqn-named-dims.md (Invariant 8).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:28:45 +02:00
jgrusewski
d13b535862 spec(dqn): DQN v2 unified integrated policy system design
Design spec consolidating every hard lesson from the val-Flat-collapse
investigation, the task #92 gradient-pathology triad, the ISV v-range
unification work, the f64→f32 ABI cleanup, and many sessions of
fold-1 grad explosions and orphan-feature accumulation.

Seven non-negotiable invariants:
  1. ISV-driven adaptive bounds (no tuned constants)
  2. Wire-It-Up (no orphans)
  3. GPU-only hot path (zero memcpy DtoH; pinned-zero-copy only)
  4. Pinned-only host memory
  5. Diagnostic-first
  6. Convergence discipline (primary guardrail, multi-seed × multi-fold)
  7. Wire-up + diagnostic audit per-commit

Five Parts, landing as one coordinated rollout:
  A. Foundation — reset registry, ISV contract, multi-seed harness,
     regression detection, orphan audit, hot-path purity audit.
  B. Flat-trap escape — ISV-driven reward shaping, trade-attempt bonus,
     replay warm-start via scripted-policy portfolio, adaptive plan
     threshold.
  C. Refinement — quantile atom support, reward attribution, state-dist
     divergence signal, temporal timing bonus, CQL-seed coupling,
     adaptive controller unification refactor.
  D. Temporal architecture — Mamba2 backward completion, per-branch
     gamma, horizon-decomposed value function, generalised temporal
     reward, soft fold transitions, plan temporal dynamics, liquid
     audit, TLOB-DQN integration.
  E. Supervised→DQN concept audit — TFT VSN, GRN, multi-quantile,
     xLSTM, KAN, encoder-decoder, interpretability, auxiliary heads.

Tiered success criteria:
  Tier 1 — convergence discipline (must pass first).
  Tier 2 — behavioural parity (val trades escape Flat-trap).
  Tier 3 — profitability (val Sharpe > 1.0 on window).

ISV slots grow from 37 to 72. Five new audit docs tracked by
pre-commit hook.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:22:43 +02:00
jgrusewski
9deda5f65b feat(dqn): ISV-unified per-branch Q-support range (spec 2026-04-23)
Unifies the Q-support range source across atom grid / warm-start quantile
clamp consumers via the ISV signal bus. One broadcast written at epoch
boundary from per-branch Q-stats EMAs, read by the two consumers that
previously held disagreeing ranges. Target observation: atom utilisation
≥40% (up from 11-15% on train-fpxnw).

Phase 0 — per-branch Q-stats kernel Rust plumbing:
* Load q_stats_per_branch_reduce alongside legacy q_stats_reduce
* Add per_branch_q_stats_pinned (28 f32 = 4 × 7, device-mapped)
* PerBranchQValueStats struct: [QValueStatsResult; 4]
* reduce_current_q_stats_per_branch launches the new kernel with the
  four branch (off, size) pairs derived from config.branch_N_size

Phase 1 — ISV v-range plumbing (zero behavioural change at epoch 1):
* ISV_NETWORK_DIM=23 preserved for w_isv_fc1 sizing; ISV_TOTAL_DIM=31
  allocates 8 additional slots for per-branch (centre, half-width)
* Slot constants V_CENTER_DIR..V_HALF_URG covering slots 23..30
* eval_q_mean_ema / eval_q_std_ema / eval_ema_initialized promoted
  to [f32; 4] / [bool; 4]; scalar setters preserved for trajectory
  backtracking (broadcast same value to all branches)
* Bootstrap at construction: centre=0, half=(v_max-v_min)/2 → the
  byte-identical [config.v_min, config.v_max] span per branch before
  any Q observations arrive
* reset_eval_v_range_state resets the 4 per-branch EMAs AND the 8 ISV
  slots to bootstrap values; legacy eval_v_range_pinned[2] still reset
  (deferred removal — spec Phase 3)
* update_eval_v_range reworked: signature takes PerBranchQValueStats and
  per_branch_q_gaps. Maintains 4 independent adaptive-rate EMAs,
  computes (centre, half) per branch with min_half_floor=0.1×(v_max-v_min)
  and clamps to config bounds, writes 8 ISV slots. Branch-0 (direction)
  centre±half is also mirrored into the legacy eval_v_range_pinned for
  consumers that have not yet migrated to the per-branch bus.

Phase 2a/2b — atom grid per-branch v-range:
* adaptive_atom_positions kernel signature changed from
  (v_min: float, v_max: float) to (branch_idx: int, isv_signals: float*);
  reads centre/half from ISV slots 23+2·b, 24+2·b. Eliminates the f64→f32
  ABI trap (spec Phase 2 side-effect) since the only per-branch range
  path is now pointer-based.
* recompute_atom_positions passes branch_idx + isv_signals_dev_ptr per
  branch; no scalar v_min/v_max arg remains.

Phase 2c — warm-start quantile clamp per-branch from ISV:
* warm_start_atom_positions reads per-branch (centre, half) from pinned
  ISV host memory, clamps shared reward-quantile vector into each
  branch's adaptive range before tiling into atom_positions_buf.
  Bootstrap makes this equivalent to the pre-spec config.v_{min,max}
  clamp until the first Q observation lands.

Deviations from spec:
* Phase 2d (per_sample_support_buf → [N, 4, 3]) NOT implemented. The
  spec's premise was that per_sample_support is host-tiled from
  eval_v_range, but the active path in this codebase has it filled by
  iql_compute_per_sample_support (V(s)-centered, per-sample, already
  adaptive) — orthogonal to the ISV bus. Migrating that kernel to
  per-branch output would require rewriting iql_value_kernel +
  iql_support_floor + C51/MSE loss kernel indexing in lockstep, which
  the "no unrelated refactoring" constraint disallows. The loss-kernel
  Bellman projection today uses V-centered bounds that are themselves
  adaptive; the ISV v-range fix still lands the primary win (atom grid
  + warm-start agreement) without touching IQL.

Compile verified: cargo check -p ml + --workspace pass (SQLX_OFFLINE,
CARGO_INCREMENTAL=0, sccache). No TODO/FIXME/XXX introduced.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 21:18:19 +02:00
jgrusewski
baa4151fab docs(isv): signal quality audit for DQN ISV bus — L40S oscillation diagnosis
Diagnostic-only audit of all 22 ISV slots using train-mdh86 HEALTH_DIAG
(20 epochs, same oscillation pattern as train-rq6n8). Identifies three
dead writers (slots 2, 3, 4), one strongly anti-correlated signal
(slot 12 learning_health, r=-0.765 vs Sharpe), sample-0 bias in four
regime slots (8-11), and EMA-pollution risk on the Q-scale EMAs
(16, 21) that feed Kelly conviction + C51 bin weighting.

Produces a per-slot table with writer/reader citations and an ordered
fix list. Hypothesises the observed +34 → -67 Sharpe swing is driven
by health mis-reporting "improving" while policy diverges, q_dir_abs_ref
contamination collapsing Kelly, and the zero-writer TD-error EMA
disabling micro-reward regime awareness.

No code changes — reconnaissance only.
2026-04-23 10:15:16 +02:00
jgrusewski
746b8b6754 docs(tlob): feature-diff doc for TLOB 51 vs OFI 20 — Phase A
Compares the 51-feature TLOBFeatureExtractor (crates/ml-supervised/src/tlob/
features.rs + mbp10_feature_extractor.rs) against Foxhunt's 20-slot OFI
vector persisted in .fxcache.

Findings: of TLOB's 51 slots, 23 are placeholders (sine waves / hardcoded
constants), 12 duplicate existing OFI or 42-dim market features, and only
~10 carry genuinely novel information. Meanwhile the existing
MicrostructureState struct in ml-features already computes 10 features
(realized variance, Hawkes intensity, weighted book pressure, spread
dynamics, aggression ratio, queue-depletion asymmetry, order-count flux,
intra-bar momentum, regime score, OFI trajectory) that are dropped before
reaching fxcache — these dominate the TLOB novel candidates and require
zero new math.

Phase B plan (drafted inline): bump OFI_DIM 20→28, bump FXCACHE_VERSION
4→5, prioritize persisting the 10 Foxhunt-internal MicrostructureState
features before any TLOB-derived additions, sweep all hard-coded 18/20
constants across CUDA kernels + gpu_dqn_trainer.rs, and rely on the
ensure-fxcache Argo step for cache regeneration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 09:12:01 +02:00
jgrusewski
b8cd4e1d94 diag+docs(dqn): trunk-slice grad decomposition + stale IQN-trunk doc fix
Extends grad_decomp_kernel to snapshot the trunk tensor slice (tensors
0..4 = w_s1, b_s1, w_s2, b_s2) in addition to the existing direction +
magnitude branch slices (8..12 / 12..16). Adds a new HEALTH_DIAG group:

  grad_trunk [iqn=<abs> ens=<abs> c51=<abs> cql=<abs> distill=<abs>
              rec=<abs> pred=<abs> cql_sx=<abs> c51_bs=<abs>]

Prior grad_split_bwd / grad_split_aux groups report mag_norm / dir_norm
ratios per loss component, computed over branch-head tensors only. That
measurement range structurally reports 0.0000 for any loss component
that writes exclusively to the trunk — IQN and Ens in particular. This
caused the persistent misdiagnosis that IQN-to-trunk was not wired; the
prior scoping in /tmp/foxhunt_research/iqn-to-trunk-wiring-scoping.md
confirmed the wiring is live (apply_iqn_trunk_gradient at
gpu_dqn_trainer.rs:4882) and that the zero reading was a blind spot in
the measurement pipeline.

Smoke confirms the diagnostic: after iqn_readiness ramps up (late
epochs), grad_trunk reports iqn=100..381 (real trunk SAXPY amplitude),
ens=0.07..3.57, c51=2.46..8.91 (value-head dueling path contributes
through trunk), while cql/cql_sx/distill/rec/pred stay near-zero — a
clean diagnostic baseline.

Also fixes stale documentation at dual-distributional-c51-iqn-design.md
that claimed "IQN trains in isolation — its gradients don't flow back
to the shared trunk": reworded to reflect current wired state with
file:function citation and explicit iqn_readiness gating note. Updated
the "What Changes" table ("IQN training") and "Implementation Order"
(Phase 1 marked DONE) with the same citation.

Changes:
  - grad_decomp_kernel.cu: per-component result slot 2 → 3 floats
    (mag_norm, dir_norm, trunk_norm); extra __shared__ sum_trunk +
    tree reduction; new grad_trunk_start/trunk_len kernel args.
  - gpu_dqn_trainer.rs: pinned result buffer 18 → 27 floats; snapshot
    now does two copy_f32 passes (trunk → dst[0..trunk_len), branch →
    dst[trunk_len..]); per-component slot offsets 0/2/… → 0/3/…;
    grad_component_norms_trunk cached field + accessor; compute trunk
    range from padded_byte_offset(&param_sizes, 0..4).
  - fused_training.rs: grad_trunk_norms_by_component() + per-component
    grad_trunk_*_abs() accessors.
  - training_loop.rs: HEALTH_DIAG emits new grad_trunk group ordered
    [iqn ens c51 cql distill rec pred cql_sx c51_bs]; extended doc
    comment explaining the three groups' roles.
  - design spec (Problem #1 + What Changes row + Implementation Order):
    stale "IQN trains in isolation" replaced by current wired-state
    description, cites gpu_dqn_trainer.rs:4882 and readiness ramp at
    gpu_dqn_trainer.rs:4228-4243.

Pure diagnostic — no training dynamics change, no atomicAdd, no tuning
knobs, no TF32 changes. Smokes unaffected (magnitude_distribution H10
regression pre-exists on HEAD 810b3c570; 4 other smokes pass).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:03:20 +02:00