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

706 lines
27 KiB
Markdown

# 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
```bash
cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git tag backup/ml-split-p2-before-forward-port
```
**Step 2:** Verify clean working tree
```bash
git status
```
Expected: nothing to commit, working tree clean
**Step 3:** Record baseline test counts
```bash
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)
```bash
cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git cherry-pick 47d49595 9d6a04ab 356f1d29
```
Expected: clean apply, no conflicts.
**Step 2:** Verify
```bash
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
```bash
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.
```bash
git cherry-pick --abort # if it failed
# We'll retry 1b9f09fc after Phase 5
```
**Step 3:** Verify
```bash
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
```bash
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
```bash
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
```bash
SQLX_OFFLINE=true cargo check -p ml-core -p ml-dqn
```
Fix any remaining import errors.
**Step 5:** Commit
```bash
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
```bash
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
```bash
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
```bash
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.
```bash
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.
```bash
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.
```bash
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)
```bash
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)
```bash
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
```bash
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
```bash
cd /home/jgrusewski/Work/foxhunt
git format-patch -1 c0c44a5f --stdout > /tmp/c0c44a5f-full.patch
```
**Step 2:** Create per-destination copies with path rewrites
```bash
# 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.
```bash
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.rs``ml-hyperopt/src/shared_data.rs` — apply shared_data changes with path rewrite.
**Step 4:** Clean up reject files
```bash
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
```bash
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
```bash
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
```bash
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.
```bash
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/
```bash
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)
```bash
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:
```bash
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/*
```bash
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/*
```bash
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
```bash
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:
```toml
[dependencies]
ml-features = { workspace = true }
```
### Task 7d: Clippy clean
```bash
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
```bash
git add -A
git commit -m "fix: resolve visibility and import issues from forward-port"
```
---
## Phase 8: Final Verification
### Task 8a: Full workspace build
```bash
SQLX_OFFLINE=true cargo check --workspace
```
Expected: success, 0 errors.
### Task 8b: Full clippy
```bash
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
```bash
# 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:
```bash
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:
```bash
cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-split-p2
git reset --hard backup/ml-split-p2-before-forward-port
```