Split ml-rl into ml-dqn and ml-ppo for 3-way parallel compilation and better sccache hit rate. Extract TradingAction to ml-core to enable full DQN/PPO independence. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
996 lines
32 KiB
Markdown
996 lines
32 KiB
Markdown
# 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 6 crates that compile in parallel, reducing incremental rebuilds from 55s to ~6-12s.
|
|
|
|
**Architecture:** 5 sub-crates (`ml-core`, `ml-dqn`, `ml-ppo`, `ml-supervised`, `ml-infra`) + thin `ml` facade. DQN, PPO, and supervised models compile in 3-way parallel. 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-dqn/Cargo.toml`
|
|
- Create: `crates/ml-dqn/src/lib.rs`
|
|
- Create: `crates/ml-ppo/Cargo.toml`
|
|
- Create: `crates/ml-ppo/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**
|
|
|
|
```bash
|
|
mkdir -p crates/ml-core/src crates/ml-dqn/src crates/ml-ppo/src crates/ml-supervised/src crates/ml-infra/src
|
|
```
|
|
|
|
**Step 2: Add workspace members**
|
|
|
|
In root `Cargo.toml`, add to `[workspace] members`:
|
|
```toml
|
|
"crates/ml-core",
|
|
"crates/ml-dqn",
|
|
"crates/ml-ppo",
|
|
"crates/ml-supervised",
|
|
"crates/ml-infra",
|
|
```
|
|
|
|
And add workspace dependency entries:
|
|
```toml
|
|
ml-core = { path = "crates/ml-core" }
|
|
ml-dqn = { path = "crates/ml-dqn" }
|
|
ml-ppo = { path = "crates/ml-ppo" }
|
|
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-dqn`, `ml-ppo`, `ml-supervised`, `ml-infra`
|
|
|
|
See the detailed Cargo.toml specs in Tasks 4-10.
|
|
|
|
**Step 4: Create stub lib.rs for each**
|
|
|
|
Each gets an empty `pub mod` list initially:
|
|
```rust
|
|
// 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**
|
|
|
|
```bash
|
|
git add crates/ml-core crates/ml-dqn crates/ml-ppo 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`. Also extract `TradingAction` from `dqn/agent.rs` to enable DQN/PPO independence.
|
|
|
|
**Files:**
|
|
- Move: `crates/ml/src/dqn/mixed_precision.rs` → `crates/ml/src/mixed_precision.rs`
|
|
- Move: `crates/ml/src/dqn/xavier_init.rs` → `crates/ml/src/xavier_init.rs`
|
|
- Move: `crates/ml/src/dqn/portfolio_tracker.rs` → `crates/ml/src/portfolio_tracker.rs`
|
|
- Move: `crates/ml/src/dqn/action_space.rs` → `crates/ml/src/action_space.rs`
|
|
- Move: `crates/ml/src/dqn/order_router.rs` → `crates/ml/src/order_router.rs`
|
|
- Modify: `crates/ml/src/dqn/agent.rs` — move `TradingAction` enum to new file
|
|
- Create: `crates/ml/src/trading_action.rs` — `TradingAction` enum (Buy/Sell/Hold)
|
|
- 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**
|
|
|
|
```bash
|
|
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: Extract TradingAction from dqn/agent.rs**
|
|
|
|
Create `crates/ml/src/trading_action.rs` with the `TradingAction` enum (copied from `dqn/agent.rs`). In `dqn/agent.rs`, replace the enum with `pub use crate::trading_action::TradingAction;`. This is the only PPO→DQN dependency remaining after the other extractions — making it shared enables full DQN/PPO independence.
|
|
|
|
**Step 3: Update dqn/mod.rs**
|
|
|
|
Replace the old `pub mod` declarations with re-exports so existing `crate::dqn::X` paths still work:
|
|
```rust
|
|
// 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;
|
|
pub use crate::trading_action::TradingAction;
|
|
```
|
|
|
|
**Step 4: Add modules to ml/lib.rs**
|
|
|
|
Add these lines to `lib.rs`:
|
|
```rust
|
|
pub mod mixed_precision;
|
|
pub mod xavier_init;
|
|
pub mod portfolio_tracker;
|
|
pub mod action_space;
|
|
pub mod order_router;
|
|
pub mod trading_action;
|
|
```
|
|
|
|
**Step 5: Fix internal imports in moved files**
|
|
|
|
In each moved file, change `use crate::dqn::` imports to `use crate::` where referencing peer modules.
|
|
|
|
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
|
|
- PPO test files using `crate::dqn::TradingAction` → change to `crate::trading_action::TradingAction`
|
|
|
|
**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**
|
|
|
|
```bash
|
|
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:
|
|
```toml
|
|
[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**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
# 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:
|
|
```rust
|
|
// 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**
|
|
|
|
```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**
|
|
|
|
```bash
|
|
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-dqn and ml-ppo (RL Models — Separate Crates)
|
|
|
|
### Task 6: Write ml-dqn Cargo.toml
|
|
|
|
**Files:**
|
|
- Create: `crates/ml-dqn/Cargo.toml`
|
|
|
|
**Step 1: Write Cargo.toml**
|
|
|
|
Dependencies: `ml-core` + `candle-core`, `candle-nn` + DQN-specific deps (`crossbeam`, etc.).
|
|
|
|
Feature flags:
|
|
```toml
|
|
[features]
|
|
default = ["cuda"]
|
|
cuda = ["ml-core/cuda"]
|
|
nccl = ["cuda"]
|
|
```
|
|
|
|
**Step 2: Commit**
|
|
|
|
---
|
|
|
|
### Task 7a: Move DQN + cuda_pipeline to ml-dqn
|
|
|
|
**Files:**
|
|
- Move: `crates/ml/src/dqn/` → `crates/ml-dqn/src/dqn/`
|
|
- Move: `crates/ml/src/cuda_pipeline/` → `crates/ml-dqn/src/cuda_pipeline/`
|
|
- Modify: `crates/ml-dqn/src/lib.rs`
|
|
- Modify: `crates/ml/src/lib.rs`
|
|
|
|
**Step 1: Move modules**
|
|
|
|
```bash
|
|
mv crates/ml/src/dqn crates/ml-dqn/src/
|
|
mv crates/ml/src/cuda_pipeline crates/ml-dqn/src/
|
|
```
|
|
|
|
**Step 2: Write ml-dqn/src/lib.rs**
|
|
|
|
```rust
|
|
// Same clippy attributes as ml-core
|
|
pub mod dqn;
|
|
pub mod cuda_pipeline;
|
|
```
|
|
|
|
**Step 3: Fix all imports in ml-dqn**
|
|
|
|
In every file under `crates/ml-dqn/src/`:
|
|
- `use crate::MLError` → `use ml_core::MLError`
|
|
- `use crate::mixed_precision` → `use ml_core::mixed_precision`
|
|
- `use crate::xavier_init` → `use ml_core::xavier_init`
|
|
- `use crate::portfolio_tracker` → `use ml_core::portfolio_tracker`
|
|
- `use crate::action_space` → `use ml_core::action_space`
|
|
- `use crate::order_router` → `use ml_core::order_router`
|
|
- `use crate::trading_action` → `use ml_core::trading_action`
|
|
- `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::Adam` → `use ml_core::Adam`
|
|
- `use crate::PRECISION_FACTOR` → `use ml_core::PRECISION_FACTOR`
|
|
- `use crate::{MLError, MLResult}` → `use ml_core::{MLError, MLResult}`
|
|
|
|
Keep `use crate::dqn::` as-is (references ml-dqn's own dqn module).
|
|
Keep `use crate::cuda_pipeline::` as-is (references ml-dqn's own cuda_pipeline module).
|
|
|
|
Update the re-exports from `dqn/mod.rs` added in Task 3:
|
|
```rust
|
|
// REPLACE crate:: references WITH ml_core::
|
|
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;
|
|
pub use ml_core::trading_action::TradingAction;
|
|
```
|
|
|
|
cuda_pipeline imports:
|
|
- `crate::dqn::mixed_precision` → `ml_core::mixed_precision`
|
|
- `crate::dqn::replay_buffer_type::GpuBatch` → `crate::dqn::replay_buffer_type::GpuBatch` (stays in ml-dqn ✓)
|
|
|
|
Watch for: `use crate::trainers::DQNTrainer` in dqn/ — reverse dep, likely test-only. Remove or cfg(test) gate with a comment.
|
|
|
|
**Step 4: Update ml/src/lib.rs**
|
|
|
|
Add re-exports:
|
|
```rust
|
|
pub use ml_dqn::dqn;
|
|
pub use ml_dqn::cuda_pipeline;
|
|
```
|
|
|
|
Add dependency to `ml/Cargo.toml`:
|
|
```toml
|
|
ml-dqn = { workspace = true }
|
|
```
|
|
|
|
**Step 5: Verify compilation**
|
|
|
|
Run: `SQLX_OFFLINE=true cargo check -p ml-dqn 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**
|
|
|
|
```bash
|
|
git add -A
|
|
git commit -m "refactor(ml): move DQN + cuda_pipeline to ml-dqn crate"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 7b: Write ml-ppo Cargo.toml and Move PPO
|
|
|
|
**Files:**
|
|
- Create: `crates/ml-ppo/Cargo.toml`
|
|
- Move: `crates/ml/src/ppo/` → `crates/ml-ppo/src/ppo/`
|
|
- Modify: `crates/ml-ppo/src/lib.rs`
|
|
- Modify: `crates/ml/src/lib.rs`
|
|
|
|
**Step 1: Write ml-ppo Cargo.toml**
|
|
|
|
Dependencies: `ml-core` + `candle-core`, `candle-nn`. NO dependency on `ml-dqn`.
|
|
|
|
Feature flags:
|
|
```toml
|
|
[features]
|
|
default = ["cuda"]
|
|
cuda = ["ml-core/cuda"]
|
|
```
|
|
|
|
**Step 2: Move PPO**
|
|
|
|
```bash
|
|
mv crates/ml/src/ppo crates/ml-ppo/src/
|
|
```
|
|
|
|
**Step 3: Write ml-ppo/src/lib.rs**
|
|
|
|
```rust
|
|
// Same clippy attributes as ml-core
|
|
pub mod ppo;
|
|
```
|
|
|
|
**Step 4: Fix all imports in ml-ppo**
|
|
|
|
Same pattern as ml-dqn: `use crate::X` → `use ml_core::X` for all shared items.
|
|
|
|
Key changes:
|
|
- `use crate::dqn::circuit_breaker` → `use ml_core::common::circuit_breaker` (direct path, no dqn dependency)
|
|
- `use crate::dqn::mixed_precision::training_dtype` → `use ml_core::mixed_precision::training_dtype`
|
|
- `use crate::dqn::portfolio_tracker` → `use ml_core::portfolio_tracker`
|
|
- `use crate::dqn::xavier_init::linear_xavier` → `use ml_core::xavier_init::linear_xavier`
|
|
- `use crate::dqn::TradingAction` → `use ml_core::trading_action::TradingAction` (test-only)
|
|
|
|
Fix `ppo/circuit_breaker.rs`:
|
|
```rust
|
|
// Change: pub use crate::common::circuit_breaker::*
|
|
// To: pub use ml_core::common::circuit_breaker::*
|
|
```
|
|
|
|
Watch for: `use crate::hyperopt::adapters::PPOTrainer` in ppo/ — reverse dep, likely test-only.
|
|
|
|
**Step 5: Update ml/src/lib.rs**
|
|
|
|
```rust
|
|
pub use ml_ppo::ppo;
|
|
```
|
|
|
|
Add dependency: `ml-ppo = { workspace = true }`
|
|
|
|
**Step 6: Verify compilation**
|
|
|
|
Run: `SQLX_OFFLINE=true cargo check -p ml-ppo 2>&1 | tail -10`
|
|
Run: `SQLX_OFFLINE=true cargo check -p ml 2>&1 | tail -10`
|
|
|
|
**Step 7: Verify DQN and PPO are independent**
|
|
|
|
Run: `SQLX_OFFLINE=true cargo tree -p ml-ppo --depth 1 | grep ml-dqn`
|
|
Expected: NO output (ml-ppo must NOT depend on ml-dqn)
|
|
|
|
**Step 8: Run tests**
|
|
|
|
Run: `SQLX_OFFLINE=true cargo test -p ml --lib 2>&1 | tail -5`
|
|
|
|
**Step 9: Commit**
|
|
|
|
```bash
|
|
git add -A
|
|
git commit -m "refactor(ml): move PPO to ml-ppo crate (independent of ml-dqn)"
|
|
```
|
|
|
|
---
|
|
|
|
## 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:
|
|
```toml
|
|
[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**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```rust
|
|
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::X` → `use ml_core::X` for anything from ml-core.
|
|
|
|
Specific cross-model deps to handle:
|
|
- `tft` imports `crate::liquid::FixedPoint` → `crate::liquid::FixedPoint` (both in ml-supervised ✓)
|
|
- `mamba` imports `crate::liquid::FixedPoint` → `crate::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_dtype` → `ml_core::mixed_precision::training_dtype`
|
|
- All models import `crate::training::unified_trainer::*` → `ml_core::training::unified_trainer::*`
|
|
|
|
**Step 4: Update ml/src/lib.rs**
|
|
|
|
```rust
|
|
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**
|
|
|
|
```bash
|
|
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-dqn`, `ml-ppo`, `ml-supervised` + `argmin` (for hyperopt), model-specific deps.
|
|
|
|
Feature flags:
|
|
```toml
|
|
[features]
|
|
default = ["cuda"]
|
|
cuda = ["ml-core/cuda", "ml-dqn/cuda", "ml-ppo/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.rs` → `crates/ml-infra/src/training_pipeline.rs`
|
|
- Move: `crates/ml/src/walk_forward.rs` → `crates/ml-infra/src/walk_forward.rs`
|
|
- Modify: `crates/ml-infra/src/lib.rs`
|
|
- Modify: `crates/ml/src/lib.rs`
|
|
|
|
**Step 1: Move modules**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```rust
|
|
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 four model sub-crates:
|
|
- `use crate::dqn::DQN` → `use ml_dqn::dqn::DQN`
|
|
- `use crate::dqn::DQNConfig` → `use ml_dqn::dqn::DQNConfig`
|
|
- `use crate::ppo::PPO` → `use ml_ppo::ppo::PPO`
|
|
- `use crate::cuda_pipeline::*` → `use ml_dqn::cuda_pipeline::*`
|
|
- `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::*`
|
|
- All shared items: `use crate::X` → `use ml_core::X`
|
|
|
|
**Step 4: Update ml/src/lib.rs**
|
|
|
|
```rust
|
|
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**
|
|
|
|
```bash
|
|
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-dqn`, `ml-ppo`, `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_dqn` via re-export, so `use crate::dqn::` still works ✓
|
|
- `use crate::ppo::` — same via re-export from `ml_ppo` ✓
|
|
- `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**
|
|
|
|
```bash
|
|
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:
|
|
```bash
|
|
SQLX_OFFLINE=true cargo test -p ml-core --lib 2>&1 | tail -5
|
|
SQLX_OFFLINE=true cargo test -p ml-dqn --lib 2>&1 | tail -5
|
|
SQLX_OFFLINE=true cargo test -p ml-ppo --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:
|
|
```bash
|
|
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:
|
|
```toml
|
|
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 6-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**
|
|
|
|
```bash
|
|
git add -A
|
|
git commit -m "refactor(ml): complete crate split — 6 crates, 3-way parallel compilation
|
|
|
|
Split the monolithic ml crate (260K lines, 55s compile) into:
|
|
- ml-core: shared types, traits, features, data, checkpoint, safety
|
|
- ml-dqn: DQN + cuda_pipeline
|
|
- ml-ppo: PPO (independent of ml-dqn)
|
|
- ml-supervised: TFT, Mamba, Liquid, TGNN, TLOB, KAN, xLSTM, Diffusion
|
|
- ml-infra: hyperopt, ensemble, trainers, benchmark, deployment
|
|
- ml: thin facade re-exporting everything
|
|
|
|
3-way parallel compilation (ml-dqn + ml-ppo + ml-supervised).
|
|
Zero API changes for downstream consumers.
|
|
Incremental rebuild after model edit: ~55s → ~6-12s."
|
|
```
|
|
|
|
---
|
|
|
|
## 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/ + TradingAction | refactor | 2 |
|
|
| 4 | Write ml-core Cargo.toml | config | 2 |
|
|
| 5 | Move core modules to ml-core | **large** | 3, 4 |
|
|
| 6 | Write ml-dqn Cargo.toml | config | 4 |
|
|
| 7a | Move DQN + cuda_pipeline to ml-dqn | **large** | 5, 6 |
|
|
| 7b | Write ml-ppo Cargo.toml + move PPO | **large** | 5 |
|
|
| 8 | Write ml-supervised Cargo.toml | config | 4 |
|
|
| 9 | Move supervised models | **large** | 5, 8 |
|
|
| 10 | Write ml-infra Cargo.toml | config | 6, 7b, 8 |
|
|
| 11 | Move infra modules | **large** | 7a, 7b, 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 |
|
|
|
|
Note: Tasks 7a, 7b, and 9 can execute in parallel (they move independent module sets).
|
|
|
|
## 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
|