Files
foxhunt/docs/plans/2026-03-09-forward-port-main-to-ml-split.md
jgrusewski 4709ca8bc2 feat(dqn): enable Branching DQN with 45 factored actions (5×3×3)
Restore 45-action factored space via Branching DQN (Tavakoli 2018),
outputting 11 Q-values (5+3+3) instead of 45. This was reduced to 5
exposure-only actions during debugging and was never intended as permanent.

- Enable use_branching: true by default in DQNConfig and DQNHyperparameters
- Add branching paths to select_action_with_confidence and select_action_inference
- Update agent.rs select_action_factored for branching-aware selection
- Expand CountBonus to per-branch tracking with bonuses_branched()
- Add order_type + urgency distribution tracking in monitoring
- Add DQN_ORDER_ACTIONS=3, DQN_URGENCY_ACTIONS=3, DQN_TOTAL_ACTIONS=45 to CUDA header
- Fix 7 pre-existing clippy doc_markdown errors in regime_conditional.rs
- Fix pre-existing cognitive_complexity in replay_buffer_type.rs (extract helpers)
- Fix flaky GPU test OOM under parallel execution (CPU fallback + test VRAM safety)
- Delete unused flash_attention submodules (block_sparse, causal_masking, etc.)
- Add GPU hot-path guard scripts and ensemble/hyperopt adapter improvements

Tests: ml-dqn 416/0, ml 905/0, clippy 0 errors on both crates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 22:00:13 +01:00

27 KiB

Forward-Port Main onto ML Crate Split Branch — Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Port 24 commits of GPU performance work from main onto feature/ml-crate-split-phase2, adapting file paths to the new sub-crate layout.

Architecture: Cherry-pick + path-rewrite in 8 phases. Commits that only touch sub-crates (ml-core, ml-dqn, etc.) or infra files apply directly. Commits that touch crates/ml/ files which were moved to sub-crates need path-rewritten patches. Deleted files (parquet, smoke_tests) are triaged. Compiler-driven fixup after each phase.

Tech Stack: Rust workspace, git format-patch/apply, sed path rewriting, SQLX_OFFLINE=true cargo check --workspace

Worktree: .worktrees/ml-split-p2 on branch feature/ml-crate-split-phase2

Build command: SQLX_OFFLINE=true cargo check --workspace Test command: SQLX_OFFLINE=true cargo test -p <crate> --lib Clippy: SQLX_OFFLINE=true cargo clippy --workspace -- -D warnings


File Move Mapping (Branch Reality)

These files were moved from crates/ml/src/ to sub-crates on the branch. Main modified them at their OLD paths. Patches must target the NEW paths.

OLD PATH (on main)                              → NEW PATH (on branch)
─────────────────────────────────────────────────────────────────────────
crates/ml/src/cuda_pipeline/*                   → crates/ml-core/src/cuda_pipeline/*
crates/ml/src/features/extraction.rs            → crates/ml-features/src/extraction.rs
crates/ml/src/features/production_adapter.rs    → crates/ml-features/src/production_adapter.rs
crates/ml/src/features/unified.rs               → crates/ml-features/src/unified.rs
crates/ml/src/features/mod.rs                   → crates/ml-features/src/lib.rs (merged into)
crates/ml/src/trainers/dqn/trainer.rs           → crates/ml-dqn/src/trainer/trainer.rs
crates/ml/src/trainers/dqn/config.rs            → crates/ml-dqn/src/trainer/config.rs
crates/ml/src/trainers/dqn/data_loading.rs      → crates/ml-dqn/src/trainer/data_loading.rs
crates/ml/src/trainers/dqn/features.rs          → crates/ml-dqn/src/trainer/features.rs
crates/ml/src/trainers/dqn/mod.rs               → crates/ml-dqn/src/trainer/mod.rs
crates/ml/src/trainers/dqn/monitoring.rs        → crates/ml-dqn/src/trainer/monitoring.rs
crates/ml/src/trainers/ppo.rs                   → crates/ml-ppo/src/trainer.rs
crates/ml/src/hyperopt/adapters/dqn.rs          → crates/ml-hyperopt/src/adapters/dqn.rs
crates/ml/src/hyperopt/adapters/ppo.rs          → crates/ml-hyperopt/src/adapters/ppo.rs
crates/ml/src/hyperopt/adapters/continuous_ppo.rs → crates/ml-hyperopt/src/adapters/continuous_ppo.rs
crates/ml/src/hyperopt/adapters/mamba2.rs       → crates/ml-hyperopt/src/adapters/mamba2.rs
crates/ml/src/hyperopt/shared_data.rs           → crates/ml-hyperopt/src/shared_data.rs
crates/ml/src/ensemble/model_adapter.rs         → crates/ml-ensemble/src/model_adapter.rs

STILL IN crates/ml/ (no rewrite needed):
  src/data_loaders/*, src/walk_forward.rs, src/hyperopt/adapters/tft.rs,
  examples/*, tests/*, Cargo.toml

DELETED on branch (main's changes are OBSOLETE):
  src/data_loaders/parquet_utils.rs   — branch intentionally removed all parquet code
  src/feature_cache.rs                — branch deleted (superseded by ml-features)
  src/trainers/tft_parquet.rs         — branch deleted (parquet removal)
  src/trainers/dqn/smoke_tests/*      — branch deleted entire directory (5 files, ~900 LOC)

Phase 0: Preparation

Task 0a: Create backup tag

Step 1: Tag current branch state

cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git tag backup/ml-split-p2-before-forward-port

Step 2: Verify clean working tree

git status

Expected: nothing to commit, working tree clean

Step 3: Record baseline test counts

SQLX_OFFLINE=true cargo test --workspace --lib 2>&1 | grep "test result" | sort

Save output for comparison after all phases.


Phase 1: Infrastructure-Only Commits (3 commits, clean cherry-pick)

These commits touch only infra/ files — no crates/ conflicts at all.

Task 1a: Cherry-pick infra commits

Commits:

  • 47d49595 fix(train): correct --output to --output-dir arg, add Prometheus annotations
  • 9d6a04ab fix(grafana): add 20 missing metric panels to training dashboard
  • 356f1d29 fix(train): remove duplicate annotations block from rebase artifact

Step 1: Cherry-pick all three (oldest first)

cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git cherry-pick 47d49595 9d6a04ab 356f1d29

Expected: clean apply, no conflicts.

Step 2: Verify

SQLX_OFFLINE=true cargo check --workspace

Step 3: If any conflict: resolve, git add, git cherry-pick --continue


Phase 2: Sub-Crate-Only Commits (5 commits, clean cherry-pick)

These commits ONLY modify files inside crates/ml-core/, crates/ml-dqn/, crates/ml-ppo/, or crates/common/ — files that exist at the same paths on both branches.

Task 2a: Cherry-pick sub-crate performance commits

Commits (chronological):

  • 0355fb17 perf(metrics): expose step-level DQN metrics to Prometheus gauges
  • db65eb56 perf(gpu): eliminate GPU→CPU sync barriers from training hot path
  • ee12f1d9 perf(gpu): zero-sync gradient clipping — eliminate all GPU→CPU barriers
  • 379c0bee fix(dqn): cast F32 constants to input dtype for BF16 mixed precision on H100
  • 1b9f09fc perf(dqn): eliminate GPU→CPU roundtrip in branching action decomposition

Step 1: Cherry-pick all five

git cherry-pick 0355fb17 db65eb56 ee12f1d9 379c0bee 1b9f09fc

Step 2: If 1b9f09fc fails (branching.rs may not exist yet — it's added by 89c3fb89 which comes in Phase 5): skip it for now, cherry-pick it after Phase 5.

git cherry-pick --abort  # if it failed
# We'll retry 1b9f09fc after Phase 5

Step 3: Verify

SQLX_OFFLINE=true cargo check --workspace

Step 4: Commit checkpoint message if needed


Phase 3: Mixed Commits — Sub-Crate + Simple ML Path Rewrites (7 commits)

These commits touch sub-crate files (apply cleanly) PLUS crates/ml/ files that were moved. Strategy: cherry-pick, let sub-crate parts apply, then manually fix the crates/ml/ parts using path-rewritten patches.

Task 3a: GPU PER hot path (e35ead5f)

Main's changes: ml-dqn/dqn.rs, ml-dqn/quantile_regression.rs (clean), + ml/src/cuda_pipeline/gpu_experience_collector.rs (→ ml-core), ml/src/trainers/dqn/config.rs (→ ml-dqn/trainer), ml/src/trainers/dqn/trainer.rs (→ ml-dqn/trainer)

Step 1: Generate and rewrite patch

cd /home/jgrusewski/Work/foxhunt
git format-patch -1 e35ead5f --stdout > /tmp/e35ead5f.patch

# Rewrite moved file paths
sed -i 's|a/crates/ml/src/cuda_pipeline/|a/crates/ml-core/src/cuda_pipeline/|g' /tmp/e35ead5f.patch
sed -i 's|b/crates/ml/src/cuda_pipeline/|b/crates/ml-core/src/cuda_pipeline/|g' /tmp/e35ead5f.patch
sed -i 's|a/crates/ml/src/trainers/dqn/|a/crates/ml-dqn/src/trainer/|g' /tmp/e35ead5f.patch
sed -i 's|b/crates/ml/src/trainers/dqn/|b/crates/ml-dqn/src/trainer/|g' /tmp/e35ead5f.patch

Step 2: Apply with reject mode

cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git apply --reject /tmp/e35ead5f.patch

Step 3: Resolve any .rej files. Check for use crate:: paths that need updating:

  • In ml-core files: use crate:: is already correct (they're in ml-core)
  • In ml-dqn/trainer files: use crate::cuda_pipeline::use ml_core::cuda_pipeline::

Step 4: Compiler-driven fixup

SQLX_OFFLINE=true cargo check -p ml-core -p ml-dqn

Fix any remaining import errors.

Step 5: Commit

git add -A
git commit -m "forward-port(e35ead5f): wire GPU PER into hot path (path-adapted)"

Task 3b: IQN GPU PER weights + CUDA default (b616d024)

Main's changes: 10 sub-crate Cargo.tomls (cuda default feature) + ml/Cargo.toml + ml/src/trainers/dqn/config.rs (→ ml-dqn/trainer)

Step 1: Generate and rewrite patch

cd /home/jgrusewski/Work/foxhunt
git format-patch -1 b616d024 --stdout > /tmp/b616d024.patch
sed -i 's|a/crates/ml/src/trainers/dqn/|a/crates/ml-dqn/src/trainer/|g' /tmp/b616d024.patch
sed -i 's|b/crates/ml/src/trainers/dqn/|b/crates/ml-dqn/src/trainer/|g' /tmp/b616d024.patch

Step 2: Apply

cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git apply --reject /tmp/b616d024.patch

Step 3: Cargo.toml conflicts are likely — the sub-crate Cargo.tomls have different dependency lists on the branch. For each .rej file in a Cargo.toml:

  • Main added default-features = ["cuda"] — manually add this to the branch's version
  • Main may have version bumps — adopt them

Step 4: Also add default-features = ["cuda"] to any NEW sub-crate Cargo.tomls the branch created (ml-data, ml-benchmark, ml-deployment) that main didn't know about.

Step 5: Verify + commit

SQLX_OFFLINE=true cargo check --workspace
git add -A
git commit -m "forward-port(b616d024): IQN GPU PER weights, CUDA default features (path-adapted)"

Task 3c: Eliminate CPU tensor ops (801781a4)

Main's changes: ml-core/gradient_accumulation.rs, ml-dqn/dqn.rs, ml-dqn/gpu_replay_buffer.rs (clean) + ml/src/trainers/dqn/config.rs (→ ml-dqn/trainer)

Step 1-5: Same pattern as 3a — format-patch, sed trainer path, apply, fix, commit.

cd /home/jgrusewski/Work/foxhunt
git format-patch -1 801781a4 --stdout > /tmp/801781a4.patch
sed -i 's|a/crates/ml/src/trainers/dqn/|a/crates/ml-dqn/src/trainer/|g' /tmp/801781a4.patch
sed -i 's|b/crates/ml/src/trainers/dqn/|b/crates/ml-dqn/src/trainer/|g' /tmp/801781a4.patch
cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git apply --reject /tmp/801781a4.patch
# Fix .rej files, cargo check, commit

Task 3d: Replace remaining CPU tensor allocations (5191fa02)

Main's changes: ml-dqn/distributional.rs, ml-dqn/dqn.rs, ml-dqn/quantile_regression.rs, ml-ppo/ppo.rs — ALL sub-crate files, no path rewrite needed.

cd /home/jgrusewski/Work/foxhunt
git format-patch -1 5191fa02 --stdout > /tmp/5191fa02.patch
cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git apply --reject /tmp/5191fa02.patch
# Should apply cleanly. Verify + commit.

Task 3e: GPU searchsorted kernel (0f45537e)

Main's changes: ml-core/mixed_precision.rs, ml-dqn/gpu_replay_buffer.rs, ml-dqn/searchsorted_kernel.cu — all sub-crate.

cd /home/jgrusewski/Work/foxhunt
git format-patch -1 0f45537e --stdout > /tmp/0f45537e.patch
cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git apply --reject /tmp/0f45537e.patch

Note: searchsorted_kernel.cu is a new file — verify it's created in crates/ml-dqn/src/.

Task 3f: BF16 mixed precision default (7f3066e6)

Main's changes: ml-core/mixed_precision.rs, ml-dqn/curiosity.rs, ml-dqn/network.rs, ml-hyperopt/traits.rs (all clean) + ml/src/hyperopt/adapters/dqn.rs (→ ml-hyperopt/adapters)

cd /home/jgrusewski/Work/foxhunt
git format-patch -1 7f3066e6 --stdout > /tmp/7f3066e6.patch
sed -i 's|a/crates/ml/src/hyperopt/adapters/|a/crates/ml-hyperopt/src/adapters/|g' /tmp/7f3066e6.patch
sed -i 's|b/crates/ml/src/hyperopt/adapters/|b/crates/ml-hyperopt/src/adapters/|g' /tmp/7f3066e6.patch
cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git apply --reject /tmp/7f3066e6.patch

Task 3g: BF16 softmax overflow (fb18e0f1)

Main's changes: ml-dqn/distributional_dueling.rs, ml-dqn/rainbow_network.rs (clean) + ml/src/trainers/dqn/config.rs (→ ml-dqn/trainer)

cd /home/jgrusewski/Work/foxhunt
git format-patch -1 fb18e0f1 --stdout > /tmp/fb18e0f1.patch
sed -i 's|a/crates/ml/src/trainers/dqn/|a/crates/ml-dqn/src/trainer/|g' /tmp/fb18e0f1.patch
sed -i 's|b/crates/ml/src/trainers/dqn/|b/crates/ml-dqn/src/trainer/|g' /tmp/fb18e0f1.patch
cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git apply --reject /tmp/fb18e0f1.patch

Task 3-checkpoint: Verify Phase 3

SQLX_OFFLINE=true cargo check --workspace
SQLX_OFFLINE=true cargo clippy --workspace -- -D warnings 2>&1 | head -30

Commit any remaining fixups.


Phase 4: The Monster Commit — GPU-Native Regime Classification (c0c44a5f)

This single commit touches 42 files (+821/-514 lines) spanning cuda_pipeline, features, data_loaders, ensemble, hyperopt, trainers, and tests. It must be decomposed into per-destination patches.

Task 4a: Extract and split the patch

Step 1: Generate the full patch

cd /home/jgrusewski/Work/foxhunt
git format-patch -1 c0c44a5f --stdout > /tmp/c0c44a5f-full.patch

Step 2: Create per-destination copies with path rewrites

# Copy A: cuda_pipeline changes → ml-core
cp /tmp/c0c44a5f-full.patch /tmp/c0c44a5f-cuda.patch
sed -i 's|a/crates/ml/src/cuda_pipeline/|a/crates/ml-core/src/cuda_pipeline/|g' /tmp/c0c44a5f-cuda.patch
sed -i 's|b/crates/ml/src/cuda_pipeline/|b/crates/ml-core/src/cuda_pipeline/|g' /tmp/c0c44a5f-cuda.patch

# Copy B: features changes → ml-features
cp /tmp/c0c44a5f-full.patch /tmp/c0c44a5f-features.patch
sed -i 's|a/crates/ml/src/features/|a/crates/ml-features/src/|g' /tmp/c0c44a5f-features.patch
sed -i 's|b/crates/ml/src/features/|b/crates/ml-features/src/|g' /tmp/c0c44a5f-features.patch

# Copy C: trainers/dqn changes → ml-dqn/trainer
cp /tmp/c0c44a5f-full.patch /tmp/c0c44a5f-trainer.patch
sed -i 's|a/crates/ml/src/trainers/dqn/|a/crates/ml-dqn/src/trainer/|g' /tmp/c0c44a5f-trainer.patch
sed -i 's|b/crates/ml/src/trainers/dqn/|b/crates/ml-dqn/src/trainer/|g' /tmp/c0c44a5f-trainer.patch

# Copy D: hyperopt/adapters changes → ml-hyperopt/adapters
cp /tmp/c0c44a5f-full.patch /tmp/c0c44a5f-hyperopt.patch
sed -i 's|a/crates/ml/src/hyperopt/adapters/|a/crates/ml-hyperopt/src/adapters/|g' /tmp/c0c44a5f-hyperopt.patch
sed -i 's|b/crates/ml/src/hyperopt/adapters/|b/crates/ml-hyperopt/src/adapters/|g' /tmp/c0c44a5f-hyperopt.patch

# Copy E: ensemble changes → ml-ensemble
cp /tmp/c0c44a5f-full.patch /tmp/c0c44a5f-ensemble.patch
sed -i 's|a/crates/ml/src/ensemble/|a/crates/ml-ensemble/src/|g' /tmp/c0c44a5f-ensemble.patch
sed -i 's|b/crates/ml/src/ensemble/|b/crates/ml-ensemble/src/|g' /tmp/c0c44a5f-ensemble.patch

# Copy F: trainers/ppo changes → ml-ppo/trainer
cp /tmp/c0c44a5f-full.patch /tmp/c0c44a5f-ppo.patch
sed -i 's|a/crates/ml/src/trainers/ppo.rs|a/crates/ml-ppo/src/trainer.rs|g' /tmp/c0c44a5f-ppo.patch
sed -i 's|b/crates/ml/src/trainers/ppo.rs|b/crates/ml-ppo/src/trainer.rs|g' /tmp/c0c44a5f-ppo.patch

Task 4b: Apply each sub-patch

Step 1: Apply in dependency order. Use --reject throughout.

cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2

# A: ml-core cuda_pipeline
git apply --reject /tmp/c0c44a5f-cuda.patch 2>&1 | grep -E "Applied|Rejected"

# B: ml-features
git apply --reject /tmp/c0c44a5f-features.patch 2>&1 | grep -E "Applied|Rejected"

# C: ml-dqn trainer
git apply --reject /tmp/c0c44a5f-trainer.patch 2>&1 | grep -E "Applied|Rejected"

# D: ml-hyperopt adapters
git apply --reject /tmp/c0c44a5f-hyperopt.patch 2>&1 | grep -E "Applied|Rejected"

# E: ml-ensemble
git apply --reject /tmp/c0c44a5f-ensemble.patch 2>&1 | grep -E "Applied|Rejected"

# F: ml-ppo
git apply --reject /tmp/c0c44a5f-ppo.patch 2>&1 | grep -E "Applied|Rejected"

# G: The original patch for files that DIDN'T move (data_loaders, tests, examples, etc.)
git apply --reject /tmp/c0c44a5f-full.patch 2>&1 | grep -E "Applied|Rejected"

Step 2: Each patch will partially apply and partially reject (since each copy has ALL hunks but only some paths match). Resolve .rej files one by one.

Step 3: Handle special cases:

  • features/mod.rs on main → merged into ml-features/src/lib.rs on branch. Manually read the diff hunk and apply to lib.rs.
  • trainers/dqn/smoke_tests/* — main CREATED these files; branch DELETED the directory. Decision: Skip. These smoke tests tested the monolithic trainer; they need to be rewritten for ml-dqn's trainer structure. Create a TODO in the commit message.
  • trainers/tft_parquet.rs — DELETED on branch. Skip (parquet removal intentional).
  • data_loaders/parquet_utils.rs — DELETED on branch. Skip.
  • feature_cache.rs — DELETED on branch. Skip.
  • hyperopt/shared_data.rsml-hyperopt/src/shared_data.rs — apply shared_data changes with path rewrite.

Step 4: Clean up reject files

find . -name "*.rej" -type f
# Review each, apply manually or confirm skip, then delete
find . -name "*.rej" -type f -delete

Task 4c: Compiler-driven fixup for regime classification

This is the most intensive fixup step. The regime classification commit changed function signatures and data flow across crate boundaries.

Step 1: Run compiler

SQLX_OFFLINE=true cargo check --workspace 2>&1 | head -100

Step 2: Fix import paths. Common patterns:

# In ml-core/cuda_pipeline files:
use crate::mixed_precision::training_dtype  → already correct (in ml-core)

# In ml-dqn/trainer files:
use crate::cuda_pipeline::  → use ml_core::cuda_pipeline::
use crate::features::       → use ml_features::

# In ml-hyperopt/adapters files:
use crate::trainers::dqn::  → use ml_dqn::trainer::
use crate::features::       → use ml_features::

# In ml-ensemble files:
use crate::features::       → use ml_features::

Step 3: Add missing crate dependencies to Cargo.toml files if needed (e.g., if ml-hyperopt now needs ml-features).

Step 4: Iterate: cargo check, fix, cargo check, fix... until clean.

Step 5: Commit

git add -A
git commit -m "forward-port(c0c44a5f): GPU-native regime classification with 42-dim features (decomposed + path-adapted)

Skipped: smoke_tests/ (deleted on branch), parquet_utils.rs, feature_cache.rs, tft_parquet.rs
TODO: Recreate DQN trainer smoke tests in crates/ml-dqn/tests/"

Phase 5: Heavy Commits (4 commits)

Task 5a: GPU PER monitoring (d12273e1, 17 files, +1148/-115)

Main's changes span: cuda_pipeline (→ ml-core), trainers/dqn (→ ml-dqn/trainer), hyperopt/adapters/dqn (→ ml-hyperopt), smoke_tests (→ DELETED), + ml-dqn sub-crate files, ml-core files.

Step 1: Full rewrite-and-apply

cd /home/jgrusewski/Work/foxhunt
git format-patch -1 d12273e1 --stdout > /tmp/d12273e1.patch

# Apply all path rewrites
sed -i 's|a/crates/ml/src/cuda_pipeline/|a/crates/ml-core/src/cuda_pipeline/|g' /tmp/d12273e1.patch
sed -i 's|b/crates/ml/src/cuda_pipeline/|b/crates/ml-core/src/cuda_pipeline/|g' /tmp/d12273e1.patch
sed -i 's|a/crates/ml/src/trainers/dqn/|a/crates/ml-dqn/src/trainer/|g' /tmp/d12273e1.patch
sed -i 's|b/crates/ml/src/trainers/dqn/|b/crates/ml-dqn/src/trainer/|g' /tmp/d12273e1.patch
sed -i 's|a/crates/ml/src/hyperopt/adapters/|a/crates/ml-hyperopt/src/adapters/|g' /tmp/d12273e1.patch
sed -i 's|b/crates/ml/src/hyperopt/adapters/|b/crates/ml-hyperopt/src/adapters/|g' /tmp/d12273e1.patch

cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git apply --reject /tmp/d12273e1.patch

Step 2: The smoke_tests/ hunks will fail (directory doesn't exist on branch). This is expected — these 5 files (~900 LOC) were deleted on the branch. The .rej files for smoke_tests can be safely deleted.

Step 3: Fix imports, cargo check, commit.

find . -name "*.rej" -delete
SQLX_OFFLINE=true cargo check --workspace
git add -A
git commit -m "forward-port(d12273e1): GPU PER monitoring fix (path-adapted, smoke_tests skipped)"

Task 5b: Branching DQN with GPU Rainbow parity (89c3fb89, 10 files, +3207/-125)

Main's changes: ml-core/fill_simulator.rs, ml-core/lib.rs, ml-dqn/branching.rs (NEW), ml-dqn/dqn.rs, ml-dqn/lib.rs (all sub-crate — clean) + ml/src/hyperopt/adapters/dqn.rs (→ ml-hyperopt), ml/src/trainers/dqn/config.rs (→ ml-dqn/trainer), ml/src/trainers/dqn/trainer.rs (→ ml-dqn/trainer), infra/scripts/train.sh, docs/

cd /home/jgrusewski/Work/foxhunt
git format-patch -1 89c3fb89 --stdout > /tmp/89c3fb89.patch
sed -i 's|a/crates/ml/src/trainers/dqn/|a/crates/ml-dqn/src/trainer/|g' /tmp/89c3fb89.patch
sed -i 's|b/crates/ml/src/trainers/dqn/|b/crates/ml-dqn/src/trainer/|g' /tmp/89c3fb89.patch
sed -i 's|a/crates/ml/src/hyperopt/adapters/|a/crates/ml-hyperopt/src/adapters/|g' /tmp/89c3fb89.patch
sed -i 's|b/crates/ml/src/hyperopt/adapters/|b/crates/ml-hyperopt/src/adapters/|g' /tmp/89c3fb89.patch

cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git apply --reject /tmp/89c3fb89.patch

Note: branching.rs is a NEW file that goes into ml-dqn/src/ — should apply cleanly. Verify ml-dqn/src/lib.rs includes pub mod branching;.

Fix imports, cargo check, commit.

Task 5c: Eliminate OOM in hyperopt (fe223b38, 9 files, +477/-129)

Main's changes: ml-dqn/dqn.rs, ml-dqn/noisy_layers.rs, ml-hyperopt/traits.rs (clean) + ml/src/cuda_pipeline/gpu_experience_collector.rs (→ ml-core), ml/src/hyperopt/adapters/dqn.rs (→ ml-hyperopt), ml/src/trainers/dqn/config.rs (→ ml-dqn/trainer), ml/src/trainers/dqn/trainer.rs (→ ml-dqn/trainer), ml/tests/* (stay in ml)

cd /home/jgrusewski/Work/foxhunt
git format-patch -1 fe223b38 --stdout > /tmp/fe223b38.patch
sed -i 's|a/crates/ml/src/cuda_pipeline/|a/crates/ml-core/src/cuda_pipeline/|g' /tmp/fe223b38.patch
sed -i 's|b/crates/ml/src/cuda_pipeline/|b/crates/ml-core/src/cuda_pipeline/|g' /tmp/fe223b38.patch
sed -i 's|a/crates/ml/src/trainers/dqn/|a/crates/ml-dqn/src/trainer/|g' /tmp/fe223b38.patch
sed -i 's|b/crates/ml/src/trainers/dqn/|b/crates/ml-dqn/src/trainer/|g' /tmp/fe223b38.patch
sed -i 's|a/crates/ml/src/hyperopt/adapters/|a/crates/ml-hyperopt/src/adapters/|g' /tmp/fe223b38.patch
sed -i 's|b/crates/ml/src/hyperopt/adapters/|b/crates/ml-hyperopt/src/adapters/|g' /tmp/fe223b38.patch

cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git apply --reject /tmp/fe223b38.patch

Fix, check, commit.

Task 5d: Retry branching action decomposition (1b9f09fc)

If this was skipped in Phase 2 because branching.rs didn't exist yet:

git cherry-pick 1b9f09fc

Should apply cleanly now that 89c3fb89 added branching.rs.


Phase 6: CI/Metrics Mixed Commits (2 commits)

Task 6a: Unblock CPU service builds (7751f761)

Main's changes: Cargo.toml (root), ml-dqn/regime_conditional.rs (clean), ml/src/trainers/dqn/trainer.rs (→ ml-dqn/trainer), infra/*

cd /home/jgrusewski/Work/foxhunt
git format-patch -1 7751f761 --stdout > /tmp/7751f761.patch
sed -i 's|a/crates/ml/src/trainers/dqn/|a/crates/ml-dqn/src/trainer/|g' /tmp/7751f761.patch
sed -i 's|b/crates/ml/src/trainers/dqn/|b/crates/ml-dqn/src/trainer/|g' /tmp/7751f761.patch

cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git apply --reject /tmp/7751f761.patch

Task 6b: Wire training pod scraping (e89fbc2b)

Main's changes: ml/src/trainers/dqn/trainer.rs (→ ml-dqn/trainer), ml/src/trainers/ppo.rs (→ ml-ppo/trainer.rs), infra/*

cd /home/jgrusewski/Work/foxhunt
git format-patch -1 e89fbc2b --stdout > /tmp/e89fbc2b.patch
sed -i 's|a/crates/ml/src/trainers/dqn/|a/crates/ml-dqn/src/trainer/|g' /tmp/e89fbc2b.patch
sed -i 's|b/crates/ml/src/trainers/dqn/|b/crates/ml-dqn/src/trainer/|g' /tmp/e89fbc2b.patch
sed -i 's|a/crates/ml/src/trainers/ppo.rs|a/crates/ml-ppo/src/trainer.rs|g' /tmp/e89fbc2b.patch
sed -i 's|b/crates/ml/src/trainers/ppo.rs|b/crates/ml-ppo/src/trainer.rs|g' /tmp/e89fbc2b.patch

cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git apply --reject /tmp/e89fbc2b.patch

Fix, check, commit.


Phase 7: Visibility & Import Fixup Pass

After all patches are applied, some items may have pub(crate) visibility that's now insufficient across crate boundaries.

Task 7a: Full compiler sweep

cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
SQLX_OFFLINE=true cargo check --workspace 2>&1 | tee /tmp/forward-port-errors.log
grep "error\[" /tmp/forward-port-errors.log | sort -u | head -50

Task 7b: Fix visibility errors

Common patterns:

  • pub(crate) fn foo() in ml-core that ml-dqn now calls → change to pub fn foo()
  • pub(crate) struct Bar in ml-core used by ml-hyperopt → change to pub struct Bar

Task 7c: Fix missing dependency declarations

If ml-hyperopt now imports ml-features but it's not in Cargo.toml:

[dependencies]
ml-features = { workspace = true }

Task 7d: Clippy clean

SQLX_OFFLINE=true cargo clippy --workspace -- -D warnings 2>&1 | head -50

Fix any new clippy warnings introduced by forward-ported code.

Task 7e: Commit fixup

git add -A
git commit -m "fix: resolve visibility and import issues from forward-port"

Phase 8: Final Verification

Task 8a: Full workspace build

SQLX_OFFLINE=true cargo check --workspace

Expected: success, 0 errors.

Task 8b: Full clippy

SQLX_OFFLINE=true cargo clippy --workspace -- -D warnings

Expected: 0 errors, 0 warnings (excluding known ml-dqn dead field warnings from branch).

Task 8c: Test count comparison

# Run all ml-* tests
for crate in crates/ml-*/; do
  name=$(basename "$crate")
  echo -n "$name: "
  SQLX_OFFLINE=true cargo test -p "$name" --lib 2>&1 | grep "test result" | tail -1
done

# Run ml orchestration tests
echo -n "ml: "
SQLX_OFFLINE=true cargo test -p ml --lib 2>&1 | grep "test result" | tail -1

Compare totals with Phase 0 baseline. Total should be ≥ baseline (main added tests).

Task 8d: Squash or keep history

Option A (recommended): Keep granular forward-port commits for traceability:

forward-port(e35ead5f): wire GPU PER into hot path (path-adapted)
forward-port(b616d024): IQN GPU PER weights, CUDA default features (path-adapted)
...

Option B: Squash all forward-port commits into one:

git rebase -i backup/ml-split-p2-before-forward-port
# Mark all forward-port commits as "squash"

Task 8e: Update plan document

Add a completion note to docs/plans/2026-03-08-ml-crate-split-phase2.md noting that main's GPU changes have been forward-ported.


Skip List (Merge Commits)

These are merge commits on main — skip them entirely, their content is included in the non-merge commits:

  • 06d0a8d6 Merge branch 'feature/dqn-branching'
  • 3a37454e Merge branch 'feature/dqn-branching'
  • 3c3812d9 Merge branch 'worktree-gpu-hotpath-audit'

Deleted File Triage Summary

File Decision Reason
parquet_utils.rs Skip Branch intentionally deleted all parquet code (-3K LOC)
feature_cache.rs Skip Superseded by ml-features architecture
tft_parquet.rs Skip Parquet removal
smoke_tests/feature_coverage.rs TODO 156 LOC — recreate in ml-dqn/tests/
smoke_tests/gpu_residency.rs TODO 272 LOC — recreate in ml-dqn/tests/
smoke_tests/helpers.rs TODO 85 LOC — recreate in ml-dqn/tests/
smoke_tests/training_stability.rs TODO 254 LOC — recreate in ml-dqn/tests/
smoke_tests/performance.rs TODO 146 LOC — recreate in ml-dqn/tests/

Smoke tests total ~900 LOC. They tested the monolithic DQN trainer. After the forward-port, create a follow-up task to rewrite them as integration tests in crates/ml-dqn/tests/.


Rollback

If anything goes catastrophically wrong:

cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git reset --hard backup/ml-split-p2-before-forward-port