Files
foxhunt/docs/plans/2026-03-07-ml-crate-split-plan.md
jgrusewski 985e5b3343 docs(ml): add design doc and implementation plan for crate split
Split the monolithic ml crate (260K lines, 55s compile) into 5 crates:
ml-core, ml-rl, ml-supervised, ml-infra, ml (facade).
15-task plan with full module inventory and import migration guide.

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

29 KiB

ML Crate Split Implementation Plan

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

Goal: Split the monolithic ml crate (260K lines, 55s compile) into 5 crates that compile in parallel, reducing incremental rebuilds from 55s to ~10-15s.

Architecture: 4 sub-crates (ml-core, ml-rl, ml-supervised, ml-infra) + thin ml facade that re-exports everything. External consumers see no API changes.

Tech Stack: Rust workspace, Cargo features, pub use re-exports.

Reference: See docs/plans/2026-03-07-ml-crate-split-design.md for full module inventory and dependency analysis.


Phase 1: Preparation (in worktree .worktrees/ml-crate-split, branch feature/ml-crate-split)

Task 1: Verify Clean Baseline

Files: None modified

Step 1: Run full workspace check

Run: cd /home/jgrusewski/Work/foxhunt/.worktrees/ml-crate-split && SQLX_OFFLINE=true cargo check --workspace 2>&1 | tail -5 Expected: Finished with 0 errors

Step 2: Run ml tests

Run: SQLX_OFFLINE=true cargo test -p ml --lib 2>&1 | tail -5 Expected: test result: ok with 2506+ tests

Step 3: Record baseline

Note the exact test count and build time for comparison after split.


Task 2: Create Sub-Crate Directory Structure

Files:

  • Create: crates/ml-core/Cargo.toml
  • Create: crates/ml-core/src/lib.rs
  • Create: crates/ml-rl/Cargo.toml
  • Create: crates/ml-rl/src/lib.rs
  • Create: crates/ml-supervised/Cargo.toml
  • Create: crates/ml-supervised/src/lib.rs
  • Create: crates/ml-infra/Cargo.toml
  • Create: crates/ml-infra/src/lib.rs
  • Modify: Cargo.toml (workspace root — add members + workspace deps)

Step 1: Create crate directories

mkdir -p crates/ml-core/src crates/ml-rl/src crates/ml-supervised/src crates/ml-infra/src

Step 2: Add workspace members

In root Cargo.toml, add to [workspace] members:

"crates/ml-core",
"crates/ml-rl",
"crates/ml-supervised",
"crates/ml-infra",

And add workspace dependency entries:

ml-core = { path = "crates/ml-core" }
ml-rl = { path = "crates/ml-rl" }
ml-supervised = { path = "crates/ml-supervised" }
ml-infra = { path = "crates/ml-infra" }

Step 3: Create minimal Cargo.toml for each sub-crate

Each sub-crate gets a Cargo.toml with:

  • Same edition/rust-version as ml
  • Dependencies subset (only what its modules need)
  • Feature flags relevant to its modules
  • ml-core as dependency for ml-rl, ml-supervised, ml-infra

See the detailed Cargo.toml specs in Tasks 5-8.

Step 4: Create stub lib.rs for each

Each gets an empty pub mod list initially:

// crates/ml-core/src/lib.rs
// Placeholder — modules moved in subsequent tasks

Step 5: Verify workspace compiles

Run: SQLX_OFFLINE=true cargo check --workspace 2>&1 | tail -5 Expected: Compiles (empty crates are valid)

Step 6: Commit

git add crates/ml-core crates/ml-rl crates/ml-supervised crates/ml-infra Cargo.toml
git commit -m "feat(ml): scaffold sub-crate directory structure for ml split"

Phase 2: Build ml-core (Foundation)

Task 3: Extract Shared Items from dqn/ to Top-Level Modules

These items live in dqn/ but are used by PPO, trainers, ensemble, hyperopt — they belong in ml-core.

Files:

  • Move: crates/ml/src/dqn/mixed_precision.rscrates/ml/src/mixed_precision.rs
  • Move: crates/ml/src/dqn/xavier_init.rscrates/ml/src/xavier_init.rs
  • Move: crates/ml/src/dqn/portfolio_tracker.rscrates/ml/src/portfolio_tracker.rs
  • Move: crates/ml/src/dqn/action_space.rscrates/ml/src/action_space.rs
  • Move: crates/ml/src/dqn/order_router.rscrates/ml/src/order_router.rs
  • Modify: crates/ml/src/dqn/mod.rs — replace module declarations with re-exports
  • Modify: crates/ml/src/lib.rs — add pub mod for new top-level modules
  • Modify: All files importing these via crate::dqn::* path

Step 1: Move files

cd crates/ml/src
mv dqn/mixed_precision.rs mixed_precision.rs
mv dqn/xavier_init.rs xavier_init.rs
mv dqn/portfolio_tracker.rs portfolio_tracker.rs
mv dqn/action_space.rs action_space.rs
mv dqn/order_router.rs order_router.rs

Step 2: Update dqn/mod.rs

Replace the old pub mod declarations with re-exports so existing crate::dqn::X paths still work:

// In dqn/mod.rs, REPLACE:
//   pub mod mixed_precision;
//   pub mod xavier_init;
//   pub mod portfolio_tracker;
//   pub mod action_space;
//   pub mod order_router;
// WITH:
pub use crate::mixed_precision;
pub use crate::xavier_init;
pub use crate::portfolio_tracker;
pub use crate::action_space;
pub use crate::order_router;

Step 3: Add modules to ml/lib.rs

Add these lines to lib.rs:

pub mod mixed_precision;
pub mod xavier_init;
pub mod portfolio_tracker;
pub mod action_space;
pub mod order_router;

Step 4: Fix internal imports in moved files

In each moved file, change use crate::dqn:: imports to use crate:: where referencing peer modules (e.g., mixed_precision.rs may import from dqn internals — fix those paths).

Specific fixes needed:

  • action_space.rs: has no crate::dqn:: imports (only std/serde) — no changes needed
  • xavier_init.rs: uses candle_core/candle_nn only — no changes needed
  • mixed_precision.rs: uses candle_core only — no changes needed
  • portfolio_tracker.rs: check for crate::dqn:: imports and fix
  • order_router.rs: imports crate::dqn::action_space::* → change to crate::action_space::*; imports crate::common::action::* → keep as-is

Step 5: Verify compilation

Run: SQLX_OFFLINE=true cargo check -p ml 2>&1 | tail -5 Expected: Compiles with 0 errors (re-exports preserve all paths)

Step 6: Run tests

Run: SQLX_OFFLINE=true cargo test -p ml --lib 2>&1 | tail -5 Expected: Same test count as baseline, 0 failures

Step 7: Run clippy

Run: SQLX_OFFLINE=true cargo clippy -p ml -- -D warnings 2>&1 | tail -10 Expected: 0 errors, 0 warnings

Step 8: Commit

git add -A crates/ml/
git commit -m "refactor(ml): extract shared items from dqn/ to top-level modules

Move mixed_precision, xavier_init, portfolio_tracker, action_space,
order_router from dqn/ to ml/src/ root. These are shared infrastructure
used by PPO, trainers, hyperopt, and ensemble — not DQN-specific.
Re-export from dqn/mod.rs for backward compatibility."

Task 4: Write ml-core Cargo.toml

Files:

  • Create: crates/ml-core/Cargo.toml

Step 1: Write Cargo.toml

Build by examining crates/ml/Cargo.toml and including ONLY dependencies used by ml-core modules. Key deps:

  • candle-core, candle-nn, candle-optimisers (for mixed_precision, xavier_init, Adam, gpu)
  • common, config, data (workspace crates)
  • tokio, serde, thiserror, anyhow, chrono, tracing (core utilities)
  • arrow, parquet (for features/)
  • dbn, databento (for data_loaders/)
  • rust_decimal (financial types)
  • nalgebra, ndarray, num-traits (for features math)
  • dashmap, lru (for caching in features/)
  • sha2, hmac, hex (for security/)
  • flate2, memmap2 (for checkpoint/)
  • NOT: argmin (hyperopt only), NOT model-specific deps

Feature flags:

[features]
default = ["cuda"]
cuda = ["candle-core/cuda", "candle-core/cudnn", "candle-nn/cuda", "candle-nn/cudnn"]
s3-storage = ["aws-config", "aws-sdk-s3", "aws-types", "aws-credential-types", "urlencoding"]
high-precision = ["rust_decimal/serde-float"]
mimalloc-allocator = ["mimalloc"]
simd = []
gc = []
financial = []
minimal-inference = []

Step 2: Verify it parses

Run: cargo check -p ml-core 2>&1 | head -5 (will fail on missing modules, that's OK — just verifying Cargo.toml syntax)

Step 3: Commit

git add crates/ml-core/Cargo.toml
git commit -m "feat(ml-core): write Cargo.toml with dependency subset"

Task 5: Move Core Modules to ml-core

This is the largest single task. Move all modules assigned to ml-core.

Files:

  • Move: ~30 modules from crates/ml/src/ to crates/ml-core/src/
  • Modify: crates/ml-core/src/lib.rs — declare all modules
  • Modify: crates/ml/src/lib.rs — remove moved modules, add pub use ml_core::*

Step 1: Move type definitions from lib.rs

Extract the type definitions (Trade, MarketRegime, CommonError, CommonTypeError, MLError, HealthStatus, MarketDataSnapshot, FeatureVector, IntegerTensor, UpdateSummary, ModelPrediction, InferenceResult, ModelMetadata, Features, MLModel, HFTPerformanceProfile, ParallelExecutor, LatencyOptimizer, ModelRegistry helpers, and all From impls for MLError) from crates/ml/src/lib.rs into crates/ml-core/src/lib.rs.

Keep the crate-level attributes (#![deny(...)], #![allow(...)]) in both — ml-core gets the canonical set, ml facade gets a minimal subset.

Step 2: Move modules

# Core infrastructure
mv crates/ml/src/error.rs crates/ml-core/src/
mv crates/ml/src/types crates/ml-core/src/
mv crates/ml/src/common crates/ml-core/src/
mv crates/ml/src/config crates/ml-core/src/
mv crates/ml/src/cuda_compat.rs crates/ml-core/src/
mv crates/ml/src/tensor_ops.rs crates/ml-core/src/
mv crates/ml/src/traits.rs crates/ml-core/src/
mv crates/ml/src/model.rs crates/ml-core/src/
mv crates/ml/src/optimizers crates/ml-core/src/
mv crates/ml/src/gradient_accumulation.rs crates/ml-core/src/
mv crates/ml/src/gradient_utils.rs crates/ml-core/src/
mv crates/ml/src/gpu crates/ml-core/src/
mv crates/ml/src/safety crates/ml-core/src/
mv crates/ml/src/security crates/ml-core/src/
mv crates/ml/src/memory_optimization crates/ml-core/src/
mv crates/ml/src/checkpoint crates/ml-core/src/
mv crates/ml/src/training crates/ml-core/src/
mv crates/ml/src/training.rs crates/ml-core/src/

# Extracted shared items
mv crates/ml/src/mixed_precision.rs crates/ml-core/src/
mv crates/ml/src/xavier_init.rs crates/ml-core/src/
mv crates/ml/src/portfolio_tracker.rs crates/ml-core/src/
mv crates/ml/src/action_space.rs crates/ml-core/src/
mv crates/ml/src/order_router.rs crates/ml-core/src/

# Features and data
mv crates/ml/src/features crates/ml-core/src/
mv crates/ml/src/feature_cache.rs crates/ml-core/src/
mv crates/ml/src/preprocessing.rs crates/ml-core/src/
mv crates/ml/src/data_loader.rs crates/ml-core/src/
mv crates/ml/src/data_loaders crates/ml-core/src/
mv crates/ml/src/data_pipeline crates/ml-core/src/
mv crates/ml/src/data_validation crates/ml-core/src/
mv crates/ml/src/evaluation crates/ml-core/src/

# Metrics and monitoring
mv crates/ml/src/metrics crates/ml-core/src/
mv crates/ml/src/performance.rs crates/ml-core/src/
mv crates/ml/src/observability crates/ml-core/src/

Step 3: Update ml-core/src/lib.rs

Declare all moved modules with pub mod. Include all type definitions and re-exports. Copy the relevant use imports (serde, chrono, rust_decimal, etc.).

Step 4: Update all use crate:: imports in moved files

Every file in crates/ml-core/src/ that uses use crate:: — these references are now correct (they refer to ml-core's own modules). Verify no moved file imports something that stayed in ml.

Potential issues:

  • safety/ imports from crate::dqn:: (but mixed_precision is now in ml-core root)
  • features/ may import from model-specific modules → fix or feature-gate
  • data_loaders/ imports crate::types::OHLCVBar → still in ml-core ✓
  • evaluation/ imports crate::features:: → still in ml-core ✓
  • training/unified_trainer.rs — verify it only imports items now in ml-core
  • checkpoint/ — verify no model-specific imports

Run grep -rn 'use crate::dqn\|use crate::ppo\|use crate::tft\|use crate::mamba\|use crate::ensemble\|use crate::hyperopt\|use crate::trainers' crates/ml-core/src/ to find any imports of modules that are NOT in ml-core. These must be removed/refactored.

Step 5: Update ml/src/lib.rs

Replace removed pub mod declarations with re-exports:

// Add dependency
pub use ml_core;

// Re-export everything from ml-core
pub use ml_core::*;
// Explicitly re-export modules for path compatibility
pub use ml_core::common;
pub use ml_core::config;
pub use ml_core::checkpoint;
// ... etc for all modules that external consumers access by path (ml::checkpoint::X)

Step 6: Add ml-core dependency to ml/Cargo.toml

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

Step 7: Verify compilation

Run: SQLX_OFFLINE=true cargo check -p ml-core 2>&1 | tail -5 Expected: ml-core compiles

Run: SQLX_OFFLINE=true cargo check -p ml 2>&1 | tail -5 Expected: ml compiles (re-exports resolve)

Step 8: Fix any compilation errors

Iterate on import fixes until both ml-core and ml compile cleanly.

Step 9: Run tests

Run: SQLX_OFFLINE=true cargo test -p ml --lib 2>&1 | tail -5 Expected: All tests still pass

Step 10: Run clippy

Run: SQLX_OFFLINE=true cargo clippy -p ml-core -p ml -- -D warnings 2>&1 | tail -10 Expected: 0 errors, 0 warnings

Step 11: Commit

git add -A
git commit -m "refactor(ml): move core modules to ml-core crate

Move types, errors, traits, features, data loaders, checkpoint,
safety, GPU utils, and shared infrastructure to ml-core.
Re-export from ml facade for backward compatibility."

Phase 3: Build ml-rl (RL Models)

Task 6: Write ml-rl Cargo.toml

Files:

  • Create: crates/ml-rl/Cargo.toml

Step 1: Write Cargo.toml

Dependencies: ml-core + candle-core, candle-nn + RL-specific deps (crossbeam, etc.).

Feature flags:

[features]
default = ["cuda"]
cuda = ["ml-core/cuda"]
nccl = ["cuda"]

Step 2: Commit


Task 7: Move RL Modules to ml-rl

Files:

  • Move: crates/ml/src/dqn/crates/ml-rl/src/dqn/
  • Move: crates/ml/src/ppo/crates/ml-rl/src/ppo/
  • Move: crates/ml/src/cuda_pipeline/crates/ml-rl/src/cuda_pipeline/
  • Modify: crates/ml-rl/src/lib.rs
  • Modify: crates/ml/src/lib.rs

Step 1: Move modules

mv crates/ml/src/dqn crates/ml-rl/src/
mv crates/ml/src/ppo crates/ml-rl/src/
mv crates/ml/src/cuda_pipeline crates/ml-rl/src/

Step 2: Write ml-rl/src/lib.rs

// Same clippy attributes as ml-core
pub mod dqn;
pub mod ppo;
pub mod cuda_pipeline;

Step 3: Fix all imports in ml-rl

In every file under crates/ml-rl/src/:

  • use crate::MLErroruse ml_core::MLError
  • use crate::mixed_precisionuse ml_core::mixed_precision
  • use crate::xavier_inituse ml_core::xavier_init
  • use crate::portfolio_trackeruse ml_core::portfolio_tracker
  • use crate::action_spaceuse ml_core::action_space
  • use crate::order_routeruse ml_core::order_router
  • use crate::common::use ml_core::common::
  • use crate::cuda_compat::use ml_core::cuda_compat::
  • use crate::gradient_accumulation::use ml_core::gradient_accumulation::
  • use crate::training::unified_trainer::use ml_core::training::unified_trainer::
  • use crate::safety::use ml_core::safety::
  • use crate::tensor_ops::use ml_core::tensor_ops::
  • use crate::gpu::use ml_core::gpu::
  • use crate::checkpoint::use ml_core::checkpoint::
  • use crate::features::use ml_core::features::
  • use crate::evaluation::use ml_core::evaluation::
  • use crate::Adamuse ml_core::Adam
  • use crate::PRECISION_FACTORuse ml_core::PRECISION_FACTOR
  • use crate::{MLError, MLResult}use ml_core::{MLError, MLResult}

Keep use crate::dqn:: and use crate::ppo:: as-is (these reference ml-rl's own modules).

Remove the re-exports from dqn/mod.rs added in Task 3 (they re-exported from crate:: which was ml, now those modules are in ml-core):

// REMOVE these from dqn/mod.rs:
// pub use crate::mixed_precision;
// pub use crate::xavier_init;
// etc.
// REPLACE WITH:
pub use ml_core::mixed_precision;
pub use ml_core::xavier_init;
pub use ml_core::portfolio_tracker;
pub use ml_core::action_space;
pub use ml_core::order_router;

Also fix PPO re-exports in ppo/mod.rs:

// ppo/circuit_breaker.rs re-exports from crate::common::circuit_breaker
// Change to: ml_core::common::circuit_breaker

Handle cross-RL deps:

  • PPO imports crate::dqn::circuit_breaker → becomes crate::dqn::circuit_breaker (still works via dqn re-export) OR ml_core::common::circuit_breaker (direct)
  • PPO imports crate::dqn::mixed_precision::training_dtypeml_core::mixed_precision::training_dtype
  • PPO imports crate::dqn::portfolio_trackerml_core::portfolio_tracker
  • PPO imports crate::dqn::xavier_init::linear_xavierml_core::xavier_init::linear_xavier
  • cuda_pipeline imports crate::dqn::mixed_precisionml_core::mixed_precision
  • cuda_pipeline imports crate::dqn::replay_buffer_type::GpuBatchcrate::dqn::replay_buffer_type::GpuBatch (stays in ml-rl)

Watch for: use crate::trainers::DQNTrainer in dqn/ — this is a reverse dep. If it exists, it's likely in test code. If in non-test code, it needs to be removed/refactored (trainers will be in ml-infra).

Watch for: use crate::hyperopt::adapters::PPOTrainer in ppo/ — same issue. Check if test-only.

Step 4: Update ml/src/lib.rs

Add re-exports:

pub use ml_rl::dqn;
pub use ml_rl::ppo;
pub use ml_rl::cuda_pipeline;

Add dependency to ml/Cargo.toml:

ml-rl = { workspace = true }

Step 5: Verify compilation

Run: SQLX_OFFLINE=true cargo check -p ml-rl 2>&1 | tail -10 Run: SQLX_OFFLINE=true cargo check -p ml 2>&1 | tail -10

Step 6: Fix errors iteratively

Keep fixing imports until both compile.

Step 7: Run tests

Run: SQLX_OFFLINE=true cargo test -p ml --lib 2>&1 | tail -5

Step 8: Commit

git add -A
git commit -m "refactor(ml): move DQN, PPO, cuda_pipeline to ml-rl crate"

Phase 4: Build ml-supervised (Supervised Models)

Task 8: Write ml-supervised Cargo.toml

Files:

  • Create: crates/ml-supervised/Cargo.toml

Dependencies: ml-core + candle-core, candle-nn.

Feature flags:

[features]
default = ["cuda"]
cuda = ["ml-core/cuda"]

Step 1: Commit


Task 9: Move Supervised Models to ml-supervised

Files:

  • Move: crates/ml/src/tft/crates/ml-supervised/src/tft/
  • Move: crates/ml/src/mamba/crates/ml-supervised/src/mamba/
  • Move: crates/ml/src/liquid/crates/ml-supervised/src/liquid/
  • Move: crates/ml/src/tgnn/crates/ml-supervised/src/tgnn/
  • Move: crates/ml/src/tlob/crates/ml-supervised/src/tlob/
  • Move: crates/ml/src/kan/crates/ml-supervised/src/kan/
  • Move: crates/ml/src/xlstm/crates/ml-supervised/src/xlstm/
  • Move: crates/ml/src/diffusion/crates/ml-supervised/src/diffusion/
  • Move: crates/ml/src/transformers/crates/ml-supervised/src/transformers/
  • Move: crates/ml/src/flash_attention/crates/ml-supervised/src/flash_attention/
  • Modify: crates/ml-supervised/src/lib.rs
  • Modify: crates/ml/src/lib.rs

Step 1: Move modules

for mod in tft mamba liquid tgnn tlob kan xlstm diffusion transformers flash_attention; do
  mv crates/ml/src/$mod crates/ml-supervised/src/
done

Step 2: Write ml-supervised/src/lib.rs

pub mod tft;
pub mod mamba;
pub mod liquid;
pub mod tgnn;
pub mod tlob;
pub mod kan;
pub mod xlstm;
pub mod diffusion;
pub mod transformers;
pub mod flash_attention;

Step 3: Fix all imports

Same pattern as Task 7: use crate::Xuse ml_core::X for anything from ml-core.

Specific cross-model deps to handle:

  • tft imports crate::liquid::FixedPointcrate::liquid::FixedPoint (both in ml-supervised ✓)
  • mamba imports crate::liquid::FixedPointcrate::liquid::FixedPoint (both in ml-supervised ✓)
  • tft imports crate::inference::RealInferenceError → This module stays in ml facade! Need to either:
    • Move RealInferenceError type to ml-core, OR
    • Remove this import if it's only used in a few places and can be refactored
  • All models import crate::dqn::mixed_precision::training_dtypeml_core::mixed_precision::training_dtype
  • All models import crate::training::unified_trainer::*ml_core::training::unified_trainer::*

Step 4: Update ml/src/lib.rs

pub use ml_supervised::tft;
pub use ml_supervised::mamba;
pub use ml_supervised::liquid;
pub use ml_supervised::tgnn;
pub use ml_supervised::tlob;
pub use ml_supervised::kan;
pub use ml_supervised::xlstm;
pub use ml_supervised::diffusion;
pub use ml_supervised::transformers;
pub use ml_supervised::flash_attention;

Add dependency: ml-supervised = { workspace = true }

Step 5: Verify and fix

Run: SQLX_OFFLINE=true cargo check -p ml-supervised 2>&1 | tail -10 Run: SQLX_OFFLINE=true cargo check -p ml 2>&1 | tail -10

Step 6: Run tests

Run: SQLX_OFFLINE=true cargo test -p ml --lib 2>&1 | tail -5

Step 7: Commit

git add -A
git commit -m "refactor(ml): move 8 supervised models to ml-supervised crate

TFT, Mamba, Liquid, TGNN, TLOB, KAN, xLSTM, Diffusion plus shared
transformers and flash_attention infrastructure."

Phase 5: Build ml-infra (Training Infrastructure)

Task 10: Write ml-infra Cargo.toml

Files:

  • Create: crates/ml-infra/Cargo.toml

Dependencies: ml-core, ml-rl, ml-supervised + argmin (for hyperopt), model-specific deps.

Feature flags:

[features]
default = ["cuda"]
cuda = ["ml-core/cuda", "ml-rl/cuda", "ml-supervised/cuda"]

Step 1: Commit


Task 11: Move Infrastructure Modules to ml-infra

Files:

  • Move: crates/ml/src/hyperopt/crates/ml-infra/src/hyperopt/
  • Move: crates/ml/src/ensemble/crates/ml-infra/src/ensemble/
  • Move: crates/ml/src/trainers/crates/ml-infra/src/trainers/
  • Move: crates/ml/src/benchmark/crates/ml-infra/src/benchmark/
  • Move: crates/ml/src/deployment/crates/ml-infra/src/deployment/
  • Move: crates/ml/src/training_pipeline.rscrates/ml-infra/src/training_pipeline.rs
  • Move: crates/ml/src/walk_forward.rscrates/ml-infra/src/walk_forward.rs
  • Modify: crates/ml-infra/src/lib.rs
  • Modify: crates/ml/src/lib.rs

Step 1: Move modules

for mod in hyperopt ensemble trainers benchmark deployment; do
  mv crates/ml/src/$mod crates/ml-infra/src/
done
mv crates/ml/src/training_pipeline.rs crates/ml-infra/src/
mv crates/ml/src/walk_forward.rs crates/ml-infra/src/

Step 2: Write ml-infra/src/lib.rs

pub mod hyperopt;
pub mod ensemble;
pub mod trainers;
pub mod benchmark;
pub mod deployment;
pub mod training_pipeline;
pub mod walk_forward;

Step 3: Fix all imports

This is the most complex crate — it imports from all three sub-crates:

  • use crate::dqn::DQNuse ml_rl::dqn::DQN
  • use crate::dqn::DQNConfiguse ml_rl::dqn::DQNConfig
  • use crate::ppo::PPOuse ml_rl::ppo::PPO
  • use crate::tft::*use ml_supervised::tft::*
  • use crate::mamba::*use ml_supervised::mamba::*
  • use crate::liquid::*use ml_supervised::liquid::*
  • use crate::diffusion::*use ml_supervised::diffusion::*
  • use crate::kan::*use ml_supervised::kan::*
  • use crate::xlstm::*use ml_supervised::xlstm::*
  • use crate::tgnn::*use ml_supervised::tgnn::*
  • use crate::tlob::*use ml_supervised::tlob::*
  • use crate::cuda_pipeline::*use ml_rl::cuda_pipeline::*
  • All shared items: use crate::Xuse ml_core::X

Step 4: Update ml/src/lib.rs

pub use ml_infra::hyperopt;
pub use ml_infra::ensemble;
pub use ml_infra::trainers;
pub use ml_infra::benchmark;
pub use ml_infra::deployment;
pub use ml_infra::training_pipeline;
pub use ml_infra::walk_forward;

Add dependency: ml-infra = { workspace = true }

Step 5: Verify and fix

Run: SQLX_OFFLINE=true cargo check -p ml-infra 2>&1 | tail -10 Run: SQLX_OFFLINE=true cargo check -p ml 2>&1 | tail -10

Step 6: Run tests

Run: SQLX_OFFLINE=true cargo test -p ml --lib 2>&1 | tail -5

Step 7: Commit

git add -A
git commit -m "refactor(ml): move hyperopt, ensemble, trainers to ml-infra crate"

Phase 6: Finalize Facade and Verify

Task 12: Clean Up ml Facade

Files:

  • Modify: crates/ml/src/lib.rs — final cleanup
  • Modify: crates/ml/Cargo.toml — update dependencies

Step 1: Update ml/Cargo.toml

  • Add: ml-core, ml-rl, ml-supervised, ml-infra as dependencies
  • Remove: all dependencies that are now only used by sub-crates
  • Keep: dependencies used by remaining facade modules (inference.rs, validation/, risk/, etc.)

Step 2: Verify ml/src/lib.rs has complete re-exports

Every module path that external consumers use (e.g., ml::dqn::DQNConfig, ml::ensemble::EnsembleCoordinator) must resolve. Create a checklist from the external consumer import scan (Task 1 research).

Step 3: Fix remaining facade module imports

Modules still in ml facade (inference.rs, validation/, risk/, regime/, etc.) need import updates:

  • use crate::dqn:: — these now come from ml_rl via re-export, so use crate::dqn:: still works ✓
  • use crate::tft:: — same via re-export from ml_supervised
  • use crate::ensemble:: — same via re-export from ml_infra
  • use crate::MLError — from ml_core via pub use ml_core::*

This should work because the facade re-exports everything. But verify with compilation.

Step 4: Verify compilation

Run: SQLX_OFFLINE=true cargo check -p ml 2>&1 | tail -5

Step 5: Commit

git add -A
git commit -m "refactor(ml): finalize facade with complete re-exports"

Task 13: Full Workspace Verification

Files: None modified

Step 1: Full workspace check

Run: SQLX_OFFLINE=true cargo check --workspace 2>&1 | tail -10 Expected: 0 errors across all workspace members

Step 2: Full workspace clippy

Run: SQLX_OFFLINE=true cargo clippy --workspace -- -D warnings 2>&1 | tail -20 Expected: 0 errors, 0 warnings

Step 3: Run ml tests

Run: SQLX_OFFLINE=true cargo test -p ml --lib 2>&1 | tail -5 Expected: Same test count as baseline (2506+), 0 failures

Step 4: Run sub-crate tests

Run each in parallel:

SQLX_OFFLINE=true cargo test -p ml-core --lib 2>&1 | tail -5
SQLX_OFFLINE=true cargo test -p ml-rl --lib 2>&1 | tail -5
SQLX_OFFLINE=true cargo test -p ml-supervised --lib 2>&1 | tail -5
SQLX_OFFLINE=true cargo test -p ml-infra --lib 2>&1 | tail -5

Expected: Tests distributed across sub-crates, total matches baseline

Step 5: Run downstream service checks

Run:

SQLX_OFFLINE=true cargo check -p trading_service 2>&1 | tail -3
SQLX_OFFLINE=true cargo check -p ml_training_service 2>&1 | tail -3
SQLX_OFFLINE=true cargo check -p backtesting_service 2>&1 | tail -3
SQLX_OFFLINE=true cargo check -p trading_agent_service 2>&1 | tail -3

Expected: All compile with 0 errors (they use ml facade, re-exports preserve paths)

Step 6: Verify compile time improvement

Run: SQLX_OFFLINE=true cargo build -p ml --timings 2>&1 | tail -5 Open the timings HTML report. Compare per-crate times against the baseline 55.1s.

Step 7: Commit if any fixes were needed


Task 14: Update common dev-dependency

Files:

  • Modify: crates/common/Cargo.toml

Step 1: Update dev-dependency

common has ml = { path = "../ml" } as a dev-dependency. Update the path since ml is still at the same location:

ml = { path = "../ml" }  # Should still work — verify

If paths changed, update accordingly.

Step 2: Verify

Run: SQLX_OFFLINE=true cargo test -p common --lib 2>&1 | tail -5

Step 3: Commit if changed


Task 15: Update Memory and Documentation

Files:

  • Modify: /home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/MEMORY.md

Step 1: Update MEMORY.md

Update the Architecture section to reflect the new 5-crate ML structure. Update the ML Model Training section with new crate paths. Remove any stale references to crates/ml/src/dqn/mixed_precision.rs etc.

Step 2: Final commit

git add -A
git commit -m "refactor(ml): complete crate split — 5 crates, parallel compilation

Split the monolithic ml crate (260K lines, 55s compile) into:
- ml-core: shared types, traits, features, data, checkpoint, safety
- ml-rl: DQN + PPO + cuda_pipeline
- ml-supervised: TFT, Mamba, Liquid, TGNN, TLOB, KAN, xLSTM, Diffusion
- ml-infra: hyperopt, ensemble, trainers, benchmark, deployment
- ml: thin facade re-exporting everything

Zero API changes for downstream consumers.
Incremental rebuild after model edit: ~55s → ~10-15s."

Execution Checklist

Task Description Est. Size Depends On
1 Verify clean baseline verify
2 Create sub-crate dirs + workspace scaffold 1
3 Extract shared items from dqn/ refactor 2
4 Write ml-core Cargo.toml config 2
5 Move core modules to ml-core large 3, 4
6 Write ml-rl Cargo.toml config 4
7 Move RL modules to ml-rl large 5, 6
8 Write ml-supervised Cargo.toml config 4
9 Move supervised models large 5, 8
10 Write ml-infra Cargo.toml config 6, 8
11 Move infra modules large 7, 9, 10
12 Clean up ml facade medium 11
13 Full verification verify 12
14 Update common dev-dep small 12
15 Update memory + final commit small 13

Critical Invariants (Verify After Every Task)

  1. No stubs: Every function must be wired to its real implementation
  2. No lost code: Every module in the inventory must be accounted for
  3. No API changes: use ml::X::Y must work for all external consumers
  4. Tests pass: Same test count, 0 failures
  5. Clippy clean: 0 errors, 0 warnings
  6. Feature flags work: --features cuda, --no-default-features, --features financial all compile