# ML Crate Split Phase 2 — Implementation Plan

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

**Goal:** Reduce ml crate from 91K to ~12K LOC by extracting trainers, hyperopt adapters, model impls, and infrastructure into existing sub-crates.

**Architecture:** Extract in dependency order: foundational modules first (cuda_pipeline, features, flash_attention), then model impls, then trainers (which depend on foundations), then hyperopt/ensemble adapters (which depend on trainers). `ml` becomes an orchestration layer owning inference, model factory, and training pipeline.

**Tech Stack:** Rust workspace, Cargo.toml workspace deps, `pub use` re-exports for backward compat

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

**Build command:** `SQLX_OFFLINE=true cargo check --workspace` (no DB required)
**Test command:** `SQLX_OFFLINE=true cargo test -p <crate> --lib`
**Clippy:** `SQLX_OFFLINE=true cargo clippy --workspace -- -D warnings`

---

## Dependency Order & Parallelism

```
[Parallel Group A]          [Parallel Group B]       [Parallel Group C]
Task 1: cuda_pipeline→core  Task 3: model impls→     Task 4: benchmark→
Task 1b: flash_attn→core       sub-crates               ml-benchmark
Task 1c: features→ml-feat   Task 3b: dqn/→ml-dqn     Task 4b: deployment→
Task 1d: microstructure→     Task 3c: ppo/→ml-ppo        ml-deployment
    ml-features              Task 3d: model stubs→
                                ml-supervised
         │                        │
         ▼                        ▼
[Sequential: Task 2]     [Sequential: Task 5]
data_loaders→ml-data      trainers→model sub-crates
data_pipeline→ml-data      (depends on A + B)
training/→ml-data               │
                                ▼
                        [Sequential: Task 6]
                        hyperopt adapters→ml-hyperopt
                        ensemble adapters→ml-ensemble
                        (depends on Task 5)
                                │
                                ▼
                        [Final: Task 7]
                        Cleanup ml lib.rs,
                        update services, fix all imports
```

---

### Task 1: Foundation Extractions → ml-core & ml-features

**Parallel subtasks — no interdependencies.**

#### Task 1a: cuda_pipeline/ → ml-core (4,084 LOC)

**Files to move:**
- `crates/ml/src/cuda_pipeline/mod.rs` (1,122)
- `crates/ml/src/cuda_pipeline/gpu_experience_collector.rs` (795)
- `crates/ml/src/cuda_pipeline/gpu_ppo_collector.rs` (662)
- `crates/ml/src/cuda_pipeline/gpu_weights.rs` (887)
- `crates/ml/src/cuda_pipeline/gpu_portfolio.rs` (254)
- `crates/ml/src/cuda_pipeline/double_buffer.rs` (161)
- `crates/ml/src/cuda_pipeline/multi_gpu.rs` (203)

**Steps:**
1. Create `crates/ml-core/src/cuda_pipeline/` directory
2. Move all 7 files, update `mod.rs` module paths
3. Replace `use crate::MLError` → `use crate::error::MLError` (already in ml-core)
4. Replace `use crate::dqn::mixed_precision::training_dtype` → `use crate::mixed_precision::training_dtype` (already in ml-core)
5. Add `pub mod cuda_pipeline;` to `crates/ml-core/src/lib.rs`
6. In `crates/ml/src/lib.rs`: replace `pub mod cuda_pipeline;` with `pub use ml_core::cuda_pipeline;`
7. Run: `SQLX_OFFLINE=true cargo check -p ml-core -p ml`
8. Commit: `refactor(ml): move cuda_pipeline to ml-core`

#### Task 1b: flash_attention/ → ml-core (1,225 LOC)

**Files to move:**
- `crates/ml/src/flash_attention/*.rs`

**Steps:**
1. Create `crates/ml-core/src/flash_attention/`
2. Move files, fix `crate::` → appropriate ml-core paths
3. Add `pub mod flash_attention;` to ml-core lib.rs
4. In ml lib.rs: replace `pub mod flash_attention;` with `pub use ml_core::flash_attention;`
5. Run: `SQLX_OFFLINE=true cargo check -p ml-core -p ml`
6. Commit: `refactor(ml): move flash_attention to ml-core`

#### Task 1c: features/ remainder → ml-features (4,348 LOC)

**Files to move:**
- `crates/ml/src/features/extraction.rs` (1,285)
- `crates/ml/src/features/regime_adaptive.rs` (673)
- `crates/ml/src/features/multi_timeframe.rs` (630)
- `crates/ml/src/features/sample_weights.rs` (362)
- `crates/ml/src/features/unified.rs` (532)
- `crates/ml/src/features/regime_cusum.rs` (372)
- `crates/ml/src/features/regime_transition.rs` (333)
- `crates/ml/src/features/production_adapter.rs` (123)
- `crates/ml/src/features/mod.rs` (38)

**Steps:**
1. Check what `ml-features` already has vs. what's in `ml/src/features/`
2. Move non-duplicate files into `crates/ml-features/src/`
3. Fix imports: `use crate::` → `use crate::` (within ml-features) or add deps
4. Update ml-features Cargo.toml if new deps needed (e.g., ml-regime)
5. In ml lib.rs: replace `pub mod features;` with `pub use ml_features as features;` (or re-export)
6. Run: `SQLX_OFFLINE=true cargo check -p ml-features -p ml`
7. Commit: `refactor(ml): merge remaining features into ml-features`

#### Task 1d: microstructure/ → ml-features (682 LOC)

**Files to move:**
- `crates/ml/src/microstructure/*.rs`

**Steps:**
1. Move to `crates/ml-features/src/microstructure/`
2. Fix imports
3. Re-export from ml: `pub use ml_features::microstructure;`
4. Run: `SQLX_OFFLINE=true cargo check -p ml-features -p ml`
5. Commit: `refactor(ml): move microstructure to ml-features`

---

### Task 2: Data Modules → ml-data (7,041 LOC)

**Depends on:** Task 1c (features in ml-features)

**Files to move:**
- `crates/ml/src/data_loaders/*.rs` (4,365 — 7 files)
- `crates/ml/src/data_pipeline/*.rs` (1,500)
- `crates/ml/src/training/unified_data_loader.rs` + other training/ files (1,176)

**Steps:**
1. Add `ml-features` dependency to `crates/ml-data/Cargo.toml`
2. Add `dbn`, `zstd` dependencies if not already present
3. Move `data_loaders/` to `crates/ml-data/src/data_loaders/`
4. Move `data_pipeline/` to `crates/ml-data/src/data_pipeline/`
5. Move `training/` to `crates/ml-data/src/training/`
6. Fix imports: `use crate::features::` → `use ml_features::`, `use crate::types::` → `use ml_core::types::`
7. Fix `ensemble::MarketRegime` import in dbn_sequence_loader (may need re-export from ml-core)
8. In ml lib.rs: replace module declarations with re-exports
9. Run: `SQLX_OFFLINE=true cargo check -p ml-data -p ml`
10. Run: `SQLX_OFFLINE=true cargo test -p ml-data --lib`
11. Commit: `refactor(ml): move data_loaders, data_pipeline, training to ml-data`

---

### Task 3: Model Implementation Remnants → Model Sub-crates (~5,700 LOC)

**Parallel subtasks — independent per model.**

#### Task 3a: dqn/ → ml-dqn (959 LOC)

**Files:** `crates/ml/src/dqn/*.rs` (action_space, circuit_breaker, curiosity, logging, mixed_precision duplicate check, order_routing, portfolio_tracker, regime_conditional, reward, target_update)

**Steps:**
1. Check for duplicates: compare `ml/src/dqn/` contents with `crates/ml-dqn/src/`
2. Move non-duplicate files to `crates/ml-dqn/src/`
3. For duplicates: verify ml-dqn version is canonical, delete from ml/src/dqn/
4. Re-export from ml: `pub use ml_dqn as dqn;` (already done partially)
5. Run: `SQLX_OFFLINE=true cargo check -p ml-dqn -p ml`
6. Commit: `refactor(ml): merge dqn remnants into ml-dqn`

#### Task 3b: ppo/ → ml-ppo (1,004 LOC)

**Files:** `crates/ml/src/ppo/mod.rs`, `trainable_adapter.rs`, `stress_testing.rs`

**Steps:**
1. Move to `crates/ml-ppo/src/`
2. Fix imports
3. Re-export from ml
4. Check + test
5. Commit: `refactor(ml): merge ppo remnants into ml-ppo`

#### Task 3c: Supervised model dirs → ml-supervised (~4,900 LOC)

**Files:**
- `crates/ml/src/tft/` (1,796) — training.rs, trainable_adapter.rs
- `crates/ml/src/liquid/` (672)
- `crates/ml/src/tgnn/` (633)
- `crates/ml/src/kan/` (517)
- `crates/ml/src/xlstm/` (439)
- `crates/ml/src/diffusion/` (443)
- `crates/ml/src/mamba/` (399)
- `crates/ml/src/tlob/` (700)
- `crates/ml/src/transformers/` (821)

**Steps:**
1. For each model dir: compare with `ml-supervised/src/<model>/`
2. Move training logic and trainable adapters to `ml-supervised`
3. Move `transformers/` to `ml-supervised/src/transformers/`
4. Re-export from ml
5. Run: `SQLX_OFFLINE=true cargo check -p ml-supervised -p ml`
6. Commit: `refactor(ml): merge supervised model remnants into ml-supervised`

---

### Task 4: New Sub-crates (8,305 LOC)

**Parallel — independent of other tasks.**

#### Task 4a: benchmark/ → ml-benchmark (5,969 LOC)

**Steps:**
1. Create `crates/ml-benchmark/` with Cargo.toml
2. Add to workspace members in root Cargo.toml
3. Move `crates/ml/src/benchmark/` + `benchmarks.rs` to new crate
4. Add dependencies: ml-core, ml-dqn, ml-ppo, ml-supervised (model benchmarks reference models)
5. Re-export from ml: `pub use ml_benchmark as benchmark;`
6. Run: `SQLX_OFFLINE=true cargo check -p ml-benchmark -p ml`
7. Commit: `refactor(ml): extract benchmark into ml-benchmark`

#### Task 4b: deployment/ → ml-deployment (2,336 LOC)

**Steps:**
1. Create `crates/ml-deployment/` with Cargo.toml
2. Add to workspace members
3. Move `crates/ml/src/deployment/` to new crate
4. Add dependencies: ml-core
5. Re-export from ml
6. Check + test
7. Commit: `refactor(ml): extract deployment into ml-deployment`

---

### Task 5: Trainers → Model Sub-crates (18,034 LOC)

**Depends on:** Tasks 1 (foundations extracted), Task 3 (model impls merged)

This is the largest and most complex extraction. The trainers have heavy cross-module dependencies. After Tasks 1+3, those dependencies are now in sub-crates, so trainers can follow.

#### Task 5a: trainers/dqn/ → ml-dqn (9,554 LOC)

**Files:**
- `trainers/dqn/trainer.rs` (5,177)
- `trainers/dqn/config.rs` (1,223)
- `trainers/dqn/data_loading.rs` (920)
- `trainers/dqn/monitoring.rs` (284)
- `trainers/dqn/early_stopping.rs` (256)
- `trainers/dqn/features.rs` (163)
- `trainers/dqn/financials.rs` (178)
- `trainers/dqn/lr_scheduler.rs` (228)
- `trainers/dqn/risk.rs` (145)
- `trainers/dqn/statistics.rs` (134)
- `trainers/dqn/mod.rs` (45)

**Steps:**
1. Add dependencies to `crates/ml-dqn/Cargo.toml`:
   - `ml-features = { workspace = true }` (feature extraction)
   - `ml-labeling = { workspace = true }` (triple barrier)
   - `risk = { workspace = true }` (DrawdownMonitor, position limiter)
   - Any missing deps (evaluation metrics — may need to move evaluation too)
2. Create `crates/ml-dqn/src/trainer/` directory
3. Move all 11 files from `crates/ml/src/trainers/dqn/` → `crates/ml-dqn/src/trainer/`
4. Fix imports:
   - `use crate::cuda_pipeline::` → `use ml_core::cuda_pipeline::`
   - `use crate::dqn::` → `use crate::` (now in same crate)
   - `use crate::evaluation::` → determine home (may need to move evaluation first)
   - `use crate::features::` → `use ml_features::`
   - `use crate::labeling::` → `use ml_labeling::`
   - `use crate::memory_optimization::` → `use ml_core::memory_optimization::`
5. Add `pub mod trainer;` to ml-dqn lib.rs
6. In ml trainers/mod.rs: `pub use ml_dqn::trainer as dqn;` (or similar re-export)
7. Run: `SQLX_OFFLINE=true cargo check -p ml-dqn -p ml`
8. Run: `SQLX_OFFLINE=true cargo test -p ml-dqn --lib`
9. Commit: `refactor(ml): move DQN trainer to ml-dqn`

**Note:** The `evaluation/` module (metrics) is used by DQN trainer. It should move to ml-dqn or ml-core depending on whether other trainers also use it. Check with: `grep -r 'crate::evaluation' crates/ml/src/trainers/`

#### Task 5b: trainers/ppo.rs → ml-ppo (1,832 LOC)

**Steps:**
1. Add deps to ml-ppo Cargo.toml: ml-core (cuda_pipeline, batch_size_resolver)
2. Move `trainers/ppo.rs` → `crates/ml-ppo/src/trainer.rs`
3. Fix imports: `use crate::cuda_pipeline::PpoGpuData` → `use ml_core::cuda_pipeline::PpoGpuData`
4. Fix: `use crate::dqn::mixed_precision::training_dtype` → `use ml_core::mixed_precision::training_dtype`
5. Add `pub mod trainer;` to ml-ppo lib.rs
6. Re-export from ml
7. Check + test
8. Commit: `refactor(ml): move PPO trainer to ml-ppo`

#### Task 5c: trainers/tft/ → ml-supervised (2,957 LOC)

**Steps:**
1. Add deps to ml-supervised: ml-checkpoint, ml-core (memory_optimization)
2. Move `trainers/tft/` → `crates/ml-supervised/src/tft/trainer/` (or tft_trainer/)
3. Fix imports
4. Re-export from ml
5. Check + test
6. Commit: `refactor(ml): move TFT trainer to ml-supervised`

#### Task 5d: Remaining trainers (3,691 LOC)

**Files:**
- `trainers/mamba2.rs` (581) → ml-supervised
- `trainers/liquid.rs` (530) → ml-supervised
- `trainers/tlob.rs` (769) → ml-supervised
- `trainers/online_learning.rs` (982) → ml-core
- `trainers/curriculum.rs` (565) → ml-core (shared training infrastructure)
- `trainers/validation_metrics.rs` (454) → ml-core (shared)
- `trainers/tft_parquet.rs` (302) → ml-supervised

**Steps:**
1. Move model-specific trainers to ml-supervised
2. Move shared training infra to ml-core
3. Fix imports in each
4. Update ml trainers/mod.rs to re-export from sub-crates
5. Check + test
6. Commit: `refactor(ml): move remaining trainers to sub-crates`

---

### Task 6: Adapters → ml-hyperopt & ml-ensemble (~16K LOC)

**Depends on:** Task 5 (trainers in sub-crates)

#### Task 6a: hyperopt/adapters/ → ml-hyperopt (12,752 LOC + 1,295 supporting)

**Steps:**
1. Add dependencies to `crates/ml-hyperopt/Cargo.toml`:
   - `ml-dqn = { workspace = true }` (DQN adapter)
   - `ml-ppo = { workspace = true }` (PPO adapter)
   - `ml-supervised = { workspace = true }` (8 supervised adapters)
   - `ml-features = { workspace = true }` (feature extraction)
   - `ml-data = { workspace = true }` (data loading)
   - `dbn`, `zstd` (DBN file loading)
2. Create `crates/ml-hyperopt/src/adapters/` directory
3. Move all 14 adapter files + mod.rs
4. Move `campaign.rs`, `shared_data.rs`, `tests_argmin.rs`
5. Fix imports: `use crate::trainers::dqn::` → `use ml_dqn::trainer::`
6. Fix: `use crate::evaluation::` → determine new path
7. Update ml hyperopt/mod.rs to re-export from ml-hyperopt
8. Run: `SQLX_OFFLINE=true cargo check -p ml-hyperopt -p ml`
9. Run: `SQLX_OFFLINE=true cargo test -p ml-hyperopt --lib`
10. Commit: `refactor(ml): move hyperopt adapters to ml-hyperopt`

#### Task 6b: ensemble/adapters/ → ml-ensemble (3,427 LOC)

**Steps:**
1. Add deps to ml-ensemble: ml-dqn, ml-ppo, ml-supervised, ml-features
2. Move `ensemble/adapters/` to `crates/ml-ensemble/src/adapters/`
3. Move `ensemble/model_adapter.rs` to ml-ensemble
4. Fix imports: model-specific `use crate::tft::` → `use ml_supervised::tft::`
5. Fix: `use crate::dqn::mixed_precision::` → `use ml_core::mixed_precision::`
6. Re-export from ml
7. Check + test
8. Commit: `refactor(ml): move ensemble adapters to ml-ensemble`

---

### Task 7: Final Cleanup

**Depends on:** All previous tasks

#### Task 7a: Small module extraction

Move remaining small modules to existing sub-crates:
- `model_registry/` + `model_registry.rs` + `registry/` → ml-core or ml-checkpoint
- `evaluation/` → ml-core (shared metrics used by multiple trainers)
- `data_validation/` stub → delete (ml-data-validation crate exists)
- Other stubs (backtesting/, checkpoint/, labeling/, etc.) → delete re-export stubs

#### Task 7b: Update ml lib.rs

Rewrite `crates/ml/src/lib.rs` to be a clean orchestration facade:
- Remove all extracted `pub mod` declarations
- Replace with `pub use` re-exports from sub-crates
- Keep local modules: integration/, inference.rs, model_factory.rs, training_pipeline.rs, etc.
- Keep `From` impls that bridge local types to ml-core types

#### Task 7c: Update downstream consumers

Check and fix imports in:
- `services/trading_service/` (uses `ml::` paths)
- `services/trading_agent_service/`
- `services/backtesting_service/`
- `services/ml_training_service/`
- `bin/fxt/` (CLI binary)
- Training binaries

For each: `grep -r 'use ml::' <service>/src/` and fix any broken paths.

#### Task 7d: Workspace Cargo.toml

- Add new crates to `[workspace.members]`: ml-benchmark, ml-deployment
- Add workspace deps if needed
- Verify feature flag propagation (cuda feature must chain through all sub-crates)

#### Task 7e: Final verification

```bash
# Full workspace check
SQLX_OFFLINE=true cargo check --workspace

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

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

# Run ml orchestration crate tests
SQLX_OFFLINE=true cargo test -p ml --lib

# Verify line count reduction
find crates/ml/src -name '*.rs' -type f | xargs wc -l | tail -1
# Expected: ~12,000 (down from 91,000)
```

Commit: `refactor(ml): finalize phase 2 split — ml reduced to orchestration layer`

---

## Key Risks & Mitigations

1. **Circular dependencies**: Sub-crates must never depend on `ml`. All deps flow: ml → sub-crates → ml-core. Verified: no sub-crate currently depends on ml.

2. **evaluation module**: Used by DQN trainer AND hyperopt adapters. Must decide home (ml-core or ml-dqn) BEFORE moving trainers. Check: `grep -r 'crate::evaluation' crates/ml/src/`

3. **mixed_precision::training_dtype**: Referenced from DQN, PPO, ensemble. Already in ml-core. Verify re-export path works.

4. **Feature flags**: cuda feature must propagate. After split, `ml`'s `[features] cuda = [...]` must include all new sub-crate cuda features.

5. **Re-export backward compat**: Services use `ml::trainers::dqn::DQNTrainer`. After move, must re-export: `pub use ml_dqn::trainer as dqn;` in trainers/mod.rs.

6. **Test count regression**: Track test counts before/after. Expected: same total, redistributed across sub-crates.

## Pre-Split Baseline

```bash
# Record before starting
find crates/ml/src -name '*.rs' -type f | xargs wc -l | tail -1  # ~91,000
SQLX_OFFLINE=true cargo test -p ml --lib 2>&1 | grep 'test result'
```
