# fxt 2.0 — Foxhunt Operations Platform Implementation Plan

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

**Goal:** Clean rewrite of fxt into a full-scale monitoring and operations CLI with JSON output, MCP server, and cockpit TUI — using a single consolidated proto directory.

**Architecture:** fxt binary with three interfaces (CLI, MCP, TUI) sharing a unified gRPC client layer compiled from `proto/` at workspace root. API Gateway is the single endpoint. monitoring_service is removed.

**Tech Stack:** Rust, tonic 0.14, clap 4.5, ratatui 0.29, serde_json, crossterm 0.28

**Working directory:** `/home/jgrusewski/Work/foxhunt/.claude/worktrees/fxt-overhaul`

**Build command:** `SQLX_OFFLINE=true cargo check -p fxt 2>&1`

**Test command:** `SQLX_OFFLINE=true cargo test -p fxt --lib 2>&1`

**Clippy command:** `SQLX_OFFLINE=true cargo clippy -p fxt -- -D warnings 2>&1`

---

## Phase 1: Proto Consolidation (Tasks 1–3)

### Task 1: Create workspace-root proto/ directory

Copy the authoritative service protos to `proto/` at workspace root. Merge the two monitoring protos into one.

**Files:**
- Create: `proto/trading.proto` (copy from `services/trading_service/proto/trading.proto`)
- Create: `proto/config.proto` (copy from `services/trading_service/proto/config.proto`)
- Create: `proto/ml.proto` (copy from `services/trading_service/proto/ml.proto`)
- Create: `proto/risk.proto` (copy from `services/trading_service/proto/risk.proto`)
- Create: `proto/ml_training.proto` (copy from `services/ml_training_service/proto/ml_training.proto`)
- Create: `proto/broker_gateway.proto` (copy from `services/broker_gateway_service/proto/broker_gateway.proto`)
- Create: `proto/trading_agent.proto` (copy from `services/trading_agent_service/proto/trading_agent.proto`)
- Create: `proto/data_acquisition.proto` (copy from `services/data_acquisition_service/proto/data_acquisition.proto`)
- Create: `proto/config_service.proto` (copy from `services/api_gateway/proto/config_service.proto`)
- Create: `proto/health.proto` (copy from `bin/fxt/proto/health.proto`)
- Create: `proto/monitoring.proto` (MERGED from monitoring_service + trading_service versions)

**Step 1: Copy all non-conflicting protos**

```bash
mkdir -p proto
cp services/trading_service/proto/trading.proto proto/
cp services/trading_service/proto/config.proto proto/
cp services/trading_service/proto/ml.proto proto/
cp services/trading_service/proto/risk.proto proto/
cp services/ml_training_service/proto/ml_training.proto proto/
cp services/broker_gateway_service/proto/broker_gateway.proto proto/
cp services/trading_agent_service/proto/trading_agent.proto proto/
cp services/data_acquisition_service/proto/data_acquisition.proto proto/
cp services/api_gateway/proto/config_service.proto proto/
cp bin/fxt/proto/health.proto proto/
```

**Step 2: Create merged monitoring.proto**

The merged proto combines:
- System health/alerting RPCs from `services/trading_service/proto/monitoring.proto` (10 RPCs)
- Training metrics RPCs from `services/monitoring_service/proto/monitoring.proto` (3 RPCs)
- All message types from both (no field number conflicts since they're separate messages)

Add these 3 RPCs to the existing service block in the trading_service version:
```protobuf
  // Training Metrics (absorbed from monitoring_service)
  rpc GetLiveTrainingMetrics(GetLiveTrainingMetricsRequest)
      returns (GetLiveTrainingMetricsResponse);
  rpc StreamTrainingMetrics(StreamTrainingMetricsRequest)
      returns (stream GetLiveTrainingMetricsResponse);
  rpc GetEpochHistory(GetEpochHistoryRequest)
      returns (GetEpochHistoryResponse);
```

And append all training-related message types (TrainingSession, GpuSnapshot, EpochFinancialSnapshot, etc.) after the existing system health messages.

Add `cpu_percent`, `memory_used_mb`, `memory_total_mb` fields to `GetLiveTrainingMetricsResponse` (these are used by the watch TUI).

**Step 3: Verify proto syntax**

```bash
protoc --proto_path=proto --descriptor_set_out=/dev/null proto/*.proto 2>&1
```

Expected: No errors.

**Step 4: Commit**

```bash
git add proto/
git commit -m "feat: create consolidated proto/ directory at workspace root

Merge monitoring_service training metrics into trading_service system health
monitoring.proto. All 11 proto files now in one canonical location."
```

---

### Task 2: Rewire all service build.rs files to reference proto/

Each service build.rs currently references `proto/*.proto` relative to its own directory. We need to update them to reference `../../proto/` (services are at `services/<name>/`).

**Files to modify:**
- `services/trading_service/build.rs`
- `services/ml_training_service/build.rs`
- `services/broker_gateway_service/build.rs`
- `services/trading_agent_service/build.rs`
- `services/data_acquisition_service/build.rs`
- `services/monitoring_service/build.rs` (will be deleted in Phase 2, but keep working for now)
- `services/api_gateway/build.rs`

**Step 1: Update each build.rs**

For each service, change proto paths from local (`proto/<name>.proto`) to root (`../../proto/<name>.proto`) and include path from `proto` to `../../proto`.

Example for `services/trading_service/build.rs`:
```rust
// Before:
.compile_protos(&["proto/trading.proto"], &["proto"])?;
// After:
.compile_protos(&["../../proto/trading.proto"], &["../../proto"])?;
```

For `services/api_gateway/build.rs`, also remove the reference to `../../bin/fxt/proto/trading.proto` (the fat-client proto). The API gateway should compile the same service protos. Keep the `config_service.proto` reference since that's also in `proto/` now (`../../proto/config_service.proto`).

For cross-service references in build.rs files (e.g., trading_service compiling ml_training.proto, trading_agent_service compiling ml.proto), change to `../../proto/<name>.proto` — they're all in the same root proto/ dir now.

**Step 2: Update cargo:rerun-if-changed paths in each build.rs**

Every `println!("cargo:rerun-if-changed=...")` must point to the new proto/ path.

**Step 3: Verify all services compile**

```bash
SQLX_OFFLINE=true cargo check --workspace 2>&1 | head -30
```

Expected: All services compile successfully.

**Step 4: Delete old per-service proto directories**

```bash
rm -rf services/trading_service/proto/
rm -rf services/ml_training_service/proto/
rm -rf services/broker_gateway_service/proto/
rm -rf services/trading_agent_service/proto/
rm -rf services/data_acquisition_service/proto/
rm -rf services/api_gateway/proto/
# Do NOT delete services/monitoring_service/proto/ yet (Phase 2)
# Do NOT delete bin/fxt/proto/ yet (Phase 1 Task 3)
```

**Step 5: Verify workspace still compiles after deletion**

```bash
SQLX_OFFLINE=true cargo check --workspace 2>&1 | head -30
```

**Step 6: Commit**

```bash
git add -A
git commit -m "refactor: rewire all service build.rs to proto/ root directory

Delete per-service proto/ directories. All services now compile from
the single canonical proto/ at workspace root."
```

---

### Task 3: Rewire fxt build.rs to use proto/ root

Replace all fxt proto references with the workspace root protos.

**Files:**
- Modify: `bin/fxt/build.rs`
- Delete: `bin/fxt/proto/` (entire directory — 8 fat-client proto files)

**Step 1: Rewrite bin/fxt/build.rs**

The new build.rs compiles all 11 protos from `../../proto/`. Client-only (no server generation needed for fxt — except for E2E test mock servers).

```rust
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let proto_root = "../../proto";

    tonic_prost_build::configure()
        .build_server(true)  // Required for E2E test mock servers
        .build_client(true)
        .type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]")
        .server_mod_attribute(".", "#[allow(unused_qualifications)]")
        .client_mod_attribute(".", "#[allow(unused_qualifications)]")
        .protoc_arg("--experimental_allow_proto3_optional")
        .compile_protos(
            &[
                &format!("{proto_root}/trading.proto"),
                &format!("{proto_root}/health.proto"),
                &format!("{proto_root}/ml.proto"),
                &format!("{proto_root}/config.proto"),
                &format!("{proto_root}/ml_training.proto"),
                &format!("{proto_root}/trading_agent.proto"),
                &format!("{proto_root}/broker_gateway.proto"),
                &format!("{proto_root}/monitoring.proto"),
                &format!("{proto_root}/risk.proto"),
                &format!("{proto_root}/data_acquisition.proto"),
                &format!("{proto_root}/config_service.proto"),
            ],
            &[proto_root],
        )?;

    for proto in &[
        "trading", "health", "ml", "config", "ml_training",
        "trading_agent", "broker_gateway", "monitoring", "risk",
        "data_acquisition", "config_service",
    ] {
        println!("cargo:rerun-if-changed={proto_root}/{proto}.proto");
    }

    Ok(())
}
```

**Step 2: Delete old fxt proto directory**

```bash
rm -rf bin/fxt/proto/
```

**Step 3: Update fxt source imports**

The generated Rust module paths will change because proto packages changed. For example:
- Old: `foxhunt::tli::TradingServiceClient` (from package `foxhunt.tli`)
- New: `trading::TradingServiceClient` (from package `trading`)
- Old: `foxhunt::ml::MlServiceClient` (from package `foxhunt.ml`)
- New: `ml::MlServiceClient` (from package `ml`)

Update all `tonic::include_proto!()` calls and `use` statements across fxt source files.

**Important**: The new proto types have different field names/structures than the fat-client protos. For example, the service `trading.proto` has `StreamOrders`, `StreamPositions`, `StreamMarketData` RPCs instead of the fat-client's unified `SubscribeMarketData`.

This step will likely break many existing fxt commands. That's expected — we're doing a clean rewrite. Just make the build.rs + proto compilation work; the commands will be rewritten in Phase 3.

**Step 4: Verify fxt compiles (lib.rs at minimum)**

```bash
SQLX_OFFLINE=true cargo check -p fxt 2>&1 | head -50
```

At this point, expect compilation errors in command files (they reference old proto types). That's fine — we're rewriting those.

**Step 5: Commit**

```bash
git add -A
git commit -m "refactor: point fxt build.rs at proto/ root, delete fat-client protos

fxt now compiles from the same service protos as all backend services.
Existing command implementations will be rewritten to use new proto types."
```

---

## Phase 2: Remove monitoring_service (Tasks 4–5)

### Task 4: Absorb monitoring_service into API Gateway

Move the Prometheus scraping logic from monitoring_service into the API Gateway. The API Gateway will serve the monitoring RPCs directly (no proxy needed).

**Files:**
- Read: `services/monitoring_service/src/prometheus_client.rs` (understand the Prometheus scraping)
- Read: `services/monitoring_service/src/service.rs` (understand the gRPC service impl)
- Create: `services/api_gateway/src/monitoring_handler.rs` (new: Prometheus client + monitoring service impl)
- Modify: `services/api_gateway/src/main.rs` (register the monitoring service)
- Modify: `services/api_gateway/build.rs` (compile monitoring.proto with server=true)

**Step 1: Read the monitoring_service source**

Understand `prometheus_client.rs` and `service.rs` to know what to port.

**Step 2: Create monitoring_handler.rs in api_gateway**

Port the Prometheus scraping and monitoring service implementation. Key pieces:
- `PrometheusClient` struct: fetches from `PROMETHEUS_URL` env var
- `MonitoringServiceImpl`: implements the 3 training metrics RPCs
- The merged proto now has both system health RPCs (10) and training RPCs (3)
- For system health RPCs: implement with real service health checks (call each backend service's health endpoint)
- For training RPCs: port the Prometheus scraping logic

**Step 3: Register monitoring service in API Gateway main.rs**

Add the monitoring service to the tonic server builder alongside existing services.

**Step 4: Verify API Gateway compiles**

```bash
SQLX_OFFLINE=true cargo check -p api_gateway 2>&1 | head -30
```

**Step 5: Commit**

```bash
git add services/api_gateway/
git commit -m "feat: absorb monitoring service into API Gateway

Port Prometheus scraping and training metrics RPCs into api_gateway.
API Gateway now serves monitoring RPCs directly without proxy."
```

---

### Task 5: Delete monitoring_service and update infrastructure

**Files:**
- Delete: `services/monitoring_service/` (entire directory)
- Modify: `Cargo.toml` (remove from workspace members)
- Modify: `infra/k8s/gitlab/tailscale-proxy.yaml` (remove monitoring path routing)
- Modify: `.gitlab-ci.yml` (remove monitoring_service compile/deploy jobs)
- Delete: `proto/monitoring_service_standalone.proto` if any leftovers
- Modify: K8s deployment manifest for monitoring-service (if exists)

**Step 1: Remove from workspace**

In `Cargo.toml`, remove `"services/monitoring_service"` from `workspace.members`.

**Step 2: Delete the service directory**

```bash
rm -rf services/monitoring_service/
```

**Step 3: Remove nginx monitoring path routing**

In `infra/k8s/gitlab/tailscale-proxy.yaml`, remove the `location /monitoring.MonitoringService/` block from the `api.fxhnt.ai` server. All monitoring RPCs now go to API Gateway's default location.

**Step 4: Update CI pipeline**

In `.gitlab-ci.yml`, remove any `monitoring_service` compile and deploy job references.

**Step 5: Remove K8s deployment if it exists**

Search for monitoring-service K8s manifests and delete them.

**Step 6: Verify workspace compiles**

```bash
SQLX_OFFLINE=true cargo check --workspace 2>&1 | head -30
```

**Step 7: Commit**

```bash
git add -A
git commit -m "chore: delete monitoring_service — absorbed into API Gateway

Remove from workspace members, delete K8s deployment, remove nginx routing.
Training metrics and system health now served directly by API Gateway."
```

---

## Phase 3: New fxt Binary Skeleton (Tasks 6–9)

### Task 6: Clean slate — strip fxt to minimal skeleton

Remove all existing command implementations and start fresh with the new command tree. Keep auth module and config module as reference (they're reusable).

**Files:**
- Modify: `bin/fxt/src/main.rs` (new clap CLI definition)
- Modify: `bin/fxt/src/lib.rs` (new module declarations)
- Modify: `bin/fxt/src/config.rs` (keep, already has api_url)
- Create: `bin/fxt/src/output.rs` (JSON/human output abstraction)
- Create: `bin/fxt/src/grpc.rs` (unified gRPC client)
- Delete: `bin/fxt/src/client/` (old gRPC clients — will be replaced)
- Delete: `bin/fxt/src/commands/` (all old commands — will be rewritten)
- Keep: `bin/fxt/src/auth/` (reusable)
- Keep: `bin/fxt/src/error.rs` (reusable)

**Step 1: Create output.rs — JSON/human output abstraction**

Every command returns a `CommandResult` that can be rendered as JSON or human-readable:

```rust
use serde::Serialize;

/// Output format selected by --json flag
#[derive(Debug, Clone, Copy)]
pub enum OutputFormat {
    Human,
    Json,
}

/// Render any serializable result in the chosen format
pub fn render<T: Serialize + HumanReadable>(result: &T, format: OutputFormat) -> anyhow::Result<()> {
    match format {
        OutputFormat::Json => {
            let json = serde_json::to_string_pretty(result)?;
            println!("{json}");
        }
        OutputFormat::Human => {
            result.print_human();
        }
    }
    Ok(())
}

/// Trait for human-readable rendering
pub trait HumanReadable {
    fn print_human(&self);
}
```

**Step 2: Create grpc.rs — unified gRPC client factory**

```rust
use tonic::transport::Channel;
use anyhow::Result;

/// Create a gRPC channel to the API endpoint
pub async fn connect(api_url: &str) -> Result<Channel> {
    let channel = Channel::from_shared(api_url.to_owned())?
        .connect()
        .await?;
    Ok(channel)
}
```

All service clients are created from this single channel.

**Step 3: Rewrite main.rs with new command tree**

Define the full clap CLI structure with all commands and subcommands from the design doc. Each command is a stub that prints "not implemented yet". Add the global `--json` flag.

```rust
#[derive(Parser)]
#[command(name = "fxt", about = "Foxhunt Operations Platform")]
struct Cli {
    /// API endpoint URL
    #[arg(long = "api-url", env = "API_URL", default_value = "https://api.fxhnt.ai")]
    api_url: String,

    /// Output format (JSON for CI/LLM integration)
    #[arg(long, short)]
    json: bool,

    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    Auth(auth::AuthCommand),
    Trade(trade::TradeCommand),
    Train(train::TrainCommand),
    Tune(tune::TuneCommand),
    Model(model::ModelCommand),
    Agent(agent::AgentCommand),
    Backtest(backtest::BacktestCommand),
    Broker(broker::BrokerCommand),
    Data(data::DataCommand),
    Service(service::ServiceCommand),
    Cluster(cluster::ClusterCommand),
    Risk(risk::RiskCommand),
    Config(config_cmd::ConfigCommand),
    Watch(watch::WatchCommand),
    Mcp(mcp::McpCommand),
}
```

**Step 4: Create stub command modules**

Create `bin/fxt/src/commands/` with one file per command group. Each file defines the subcommand enum and a stub `execute()` function:

- `bin/fxt/src/commands/mod.rs`
- `bin/fxt/src/commands/auth.rs`
- `bin/fxt/src/commands/trade.rs`
- `bin/fxt/src/commands/train.rs`
- `bin/fxt/src/commands/tune.rs`
- `bin/fxt/src/commands/model.rs`
- `bin/fxt/src/commands/agent.rs`
- `bin/fxt/src/commands/backtest.rs`
- `bin/fxt/src/commands/broker.rs`
- `bin/fxt/src/commands/data.rs`
- `bin/fxt/src/commands/service.rs`
- `bin/fxt/src/commands/cluster.rs`
- `bin/fxt/src/commands/risk.rs`
- `bin/fxt/src/commands/config_cmd.rs`
- `bin/fxt/src/commands/watch.rs` (stub for now, Phase 5)
- `bin/fxt/src/commands/mcp.rs` (stub for now, Phase 6)

**Step 5: Verify fxt compiles and runs help**

```bash
SQLX_OFFLINE=true cargo check -p fxt 2>&1
SQLX_OFFLINE=true cargo run -p fxt -- --help 2>&1
```

**Step 6: Write basic CLI tests**

Test that `--help` works, `--json` flag is parsed, each subcommand is recognized.

**Step 7: Run tests**

```bash
SQLX_OFFLINE=true cargo test -p fxt --lib 2>&1
```

**Step 8: Commit**

```bash
git add -A
git commit -m "feat: fxt 2.0 skeleton — new command tree with 15 command groups

Clean rewrite with unified gRPC client, JSON/human output abstraction,
and stub implementations for all commands."
```

---

### Task 7: Implement grpc.rs — full client layer

Build the unified gRPC client that creates typed service clients from a single channel.

**Files:**
- Modify: `bin/fxt/src/grpc.rs`

**Step 1: Write the client factory**

```rust
pub struct FoxhuntClient {
    channel: Channel,
}

impl FoxhuntClient {
    pub async fn connect(api_url: &str) -> Result<Self> { ... }

    pub fn trading(&self) -> trading::trading_service_client::TradingServiceClient<Channel> { ... }
    pub fn ml(&self) -> ml::ml_service_client::MlServiceClient<Channel> { ... }
    pub fn ml_training(&self) -> ml_training::ml_training_service_client::MlTrainingServiceClient<Channel> { ... }
    pub fn broker(&self) -> broker_gateway::broker_gateway_service_client::BrokerGatewayServiceClient<Channel> { ... }
    pub fn trading_agent(&self) -> trading_agent::trading_agent_service_client::TradingAgentServiceClient<Channel> { ... }
    pub fn monitoring(&self) -> monitoring::monitoring_service_client::MonitoringServiceClient<Channel> { ... }
    pub fn config(&self) -> config::configuration_service_client::ConfigurationServiceClient<Channel> { ... }
    pub fn data_acquisition(&self) -> data_acquisition::data_acquisition_service_client::DataAcquisitionServiceClient<Channel> { ... }
    pub fn risk(&self) -> risk::risk_service_client::RiskServiceClient<Channel> { ... }
}
```

The exact client type names depend on the generated code from the service protos. Read the generated code (`target/debug/build/fxt-*/out/`) to find the correct module paths.

**Step 2: Test client construction**

Write a unit test that creates a client (can't connect without a server, but can verify construction doesn't panic).

**Step 3: Commit**

```bash
git add bin/fxt/src/grpc.rs
git commit -m "feat: unified FoxhuntClient with typed service accessors"
```

---

### Task 8: Implement service command (first real command)

The `service` command group is the most useful for ops. Implement it first as the template for all other commands.

**Files:**
- Modify: `bin/fxt/src/commands/service.rs`

**Step 1: Define the ServiceCommand enum**

```rust
#[derive(Parser)]
pub struct ServiceCommand {
    #[command(subcommand)]
    action: ServiceAction,
}

#[derive(Subcommand)]
enum ServiceAction {
    /// List all services with health status
    List,
    /// Get detailed status of a specific service
    Status { service: String },
    /// Health check all services
    Health,
}
```

**Step 2: Implement list subcommand**

Calls `monitoring_client.get_system_status()` → renders as table (human) or JSON.

**Step 3: Implement health subcommand**

Calls `monitoring_client.get_health_check()` → renders per-service health.

**Step 4: Implement status subcommand**

Calls `monitoring_client.get_system_status()` with filter → renders detailed view.

**Step 5: Test with `--json` output**

Both human-readable and JSON output paths should work.

**Step 6: Commit**

```bash
git add bin/fxt/src/commands/service.rs
git commit -m "feat: fxt service list/status/health with --json support"
```

---

### Task 9: Implement remaining CLI commands

Follow the same pattern as Task 8 for each command group. Each command:
1. Defines clap subcommands
2. Creates the appropriate gRPC client from `FoxhuntClient`
3. Makes the RPC call
4. Renders result via `output::render()` (JSON or human)

**Command implementation order** (most useful first):

1. `train` — start/stop/status/list/logs (uses ml_training client)
2. `tune` — start/stop/status/approve (uses ml_training client)
3. `model` — list/status/predict/ensemble/promote (uses ml client)
4. `trade` — submit/cancel/positions/orders/account (uses trading + broker clients)
5. `broker` — status/connect/executions (uses broker_gateway client)
6. `agent` — start/stop/status/config (uses trading_agent client)
7. `backtest` — run/status/results (uses trading client's backtesting RPCs)
8. `data` — download/status/cache/feeds (uses data_acquisition client)
9. `risk` — status/limits/drawdown/stress/emergency (uses risk client)
10. `config` — get/set/export/env (uses config client)
11. `cluster` — status/resources/events (uses monitoring client + kubectl)
12. `auth` — login/logout/status (port from existing auth module)

Each command should be its own commit. Each commit should include:
- The command implementation
- At least one test (CLI arg parsing)
- Verify `cargo check` and `cargo clippy` pass

---

## Phase 4: TUI Cockpits (Tasks 10–14)

### Task 10: TUI foundation — theme, state, event loop

Build the cockpit TUI framework before adding individual cockpit views.

**Files:**
- Create: `bin/fxt/src/tui/mod.rs`
- Create: `bin/fxt/src/tui/theme.rs` (color constants from logo)
- Create: `bin/fxt/src/tui/state.rs` (global TUI state)
- Create: `bin/fxt/src/tui/event_loop.rs` (event handling + gRPC streams)
- Create: `bin/fxt/src/tui/cockpit.rs` (cockpit trait + registry)
- Modify: `bin/fxt/src/commands/watch.rs` (wire up TUI launch)

**Step 1: Create theme.rs**

Define the color palette from the foxhunt logo:

```rust
use ratatui::style::Color;

pub const BG: Color = Color::Rgb(15, 15, 35);          // #0F0F23
pub const SURFACE: Color = Color::Rgb(26, 26, 46);     // #1A1A2E
pub const TEXT: Color = Color::Rgb(224, 224, 224);      // #E0E0E0
pub const PRIMARY: Color = Color::Rgb(139, 92, 246);    // #8B5CF6 purple
pub const ACCENT: Color = Color::Rgb(6, 182, 212);      // #06B6D4 cyan
pub const SUCCESS: Color = Color::Rgb(16, 185, 129);    // #10B981 green
pub const WARNING: Color = Color::Rgb(245, 158, 11);    // #F59E0B amber
pub const ERROR: Color = Color::Rgb(239, 68, 68);       // #EF4444 red
pub const MUTED: Color = Color::Rgb(107, 114, 128);     // #6B7280 gray
pub const BORDER: Color = Color::Rgb(76, 29, 149);      // #4C1D95 purple dim
pub const HIGHLIGHT: Color = Color::Rgb(34, 211, 238);  // #22D3EE bright cyan
```

**Step 2: Create cockpit trait**

```rust
pub trait Cockpit {
    fn name(&self) -> &str;
    fn key(&self) -> char;  // '1' through '6'
    fn render(&self, frame: &mut Frame, area: Rect, state: &AppState);
}
```

**Step 3: Create AppState**

Hold all data for all cockpits. Updated by background gRPC streams.

**Step 4: Create event loop**

- Terminal setup/teardown (crossterm)
- Key handling: 1-6 switch cockpits, q quit, ? help
- Background tasks: gRPC streaming subscriptions (monitoring, executions, training)
- 1-second tick for gauge refresh

**Step 5: Wire up watch command**

`fxt watch` launches the TUI event loop.

**Step 6: Verify compiles and basic TUI starts**

```bash
SQLX_OFFLINE=true cargo check -p fxt 2>&1
```

**Step 7: Commit**

```bash
git add bin/fxt/src/tui/ bin/fxt/src/commands/watch.rs
git commit -m "feat: TUI foundation — theme, state, event loop, cockpit trait"
```

---

### Task 11: Cockpit 1 — Overview

Four quadrants: Services | Resources | Training | Portfolio.

**Files:**
- Create: `bin/fxt/src/tui/cockpits/mod.rs`
- Create: `bin/fxt/src/tui/cockpits/overview.rs`

**Step 1: Implement overview cockpit**

Layout: 2x2 grid using `Layout::default().direction(Direction::Vertical)` split into top/bottom, each split horizontally.

- Top-left: Service list (name + status indicator)
- Top-right: System resources (CPU/RAM/GPU gauges)
- Bottom-left: Active training summary (model, epoch, loss, Sharpe)
- Bottom-right: Portfolio summary (equity, P&L, positions, margin)

Use the theme colors for all widgets.

**Step 2: Commit**

---

### Task 12: Cockpit 2 — Training

**Files:**
- Create: `bin/fxt/src/tui/cockpits/training.rs`

Active sessions table + GPU panel + CPU/RAM panel + Loss sparkline + Epoch financial metrics.

---

### Task 13: Cockpit 3–6 — Trading, Services, Risk, Data

**Files:**
- Create: `bin/fxt/src/tui/cockpits/trading.rs`
- Create: `bin/fxt/src/tui/cockpits/services.rs`
- Create: `bin/fxt/src/tui/cockpits/risk.rs`
- Create: `bin/fxt/src/tui/cockpits/data.rs`

Implement each cockpit according to the design doc layouts.

---

### Task 14: TUI integration test

Write an E2E test that launches the TUI with a mock gRPC server, verifies all 6 cockpits render without panicking, and responds to key events.

---

## Phase 5: MCP Server Mode (Tasks 15–16)

### Task 15: MCP protocol implementation

**Files:**
- Create: `bin/fxt/src/mcp/mod.rs`
- Create: `bin/fxt/src/mcp/server.rs` (JSON-RPC 2.0 stdin/stdout)
- Create: `bin/fxt/src/mcp/tools.rs` (tool definitions)
- Modify: `bin/fxt/src/commands/mcp.rs` (wire up serve command)

**Step 1: Implement MCP JSON-RPC protocol**

Read JSON-RPC requests from stdin, dispatch to tool handlers, write responses to stdout.

Protocol messages:
- `initialize` → return server info + tool list
- `tools/list` → return all available tools
- `tools/call` → execute a tool and return result

**Step 2: Define tool registry**

Map each CLI command to an MCP tool:
```rust
Tool { name: "fxt_service_list", description: "List all services with health status", input_schema: {} }
Tool { name: "fxt_train_start", description: "Start ML training job", input_schema: { model: string, symbol: string } }
// ... one per CLI subcommand
```

**Step 3: Wire up serve command**

`fxt mcp serve` starts the stdin/stdout loop.

**Step 4: Test with mock input**

Write a test that pipes JSON-RPC requests and verifies responses.

**Step 5: Commit**

---

### Task 16: MCP tool implementations

Each MCP tool reuses the same logic as the CLI command, but returns structured JSON instead of printing.

Refactor each command's `execute()` to return a serde-serializable result, then:
- CLI path: calls `output::render(result, format)`
- MCP path: serializes result as JSON-RPC response

This is where the `--json` architecture pays off — both CLI and MCP use the same serialization.

---

## Phase 6: Final Verification (Task 17)

### Task 17: Full verification and cleanup

**Step 1: Run full workspace check**

```bash
SQLX_OFFLINE=true cargo check --workspace 2>&1
```

**Step 2: Run fxt tests**

```bash
SQLX_OFFLINE=true cargo test -p fxt --lib 2>&1
```

**Step 3: Run clippy**

```bash
SQLX_OFFLINE=true cargo clippy -p fxt -- -D warnings 2>&1
```

**Step 4: Run fxt --help to verify command tree**

```bash
SQLX_OFFLINE=true cargo run -p fxt -- --help 2>&1
SQLX_OFFLINE=true cargo run -p fxt -- service --help 2>&1
SQLX_OFFLINE=true cargo run -p fxt -- watch --help 2>&1
SQLX_OFFLINE=true cargo run -p fxt -- mcp --help 2>&1
```

**Step 5: Verify --json flag works**

```bash
SQLX_OFFLINE=true cargo run -p fxt -- service list --json 2>&1
```

**Step 6: Final commit**

```bash
git add -A
git commit -m "feat: fxt 2.0 complete — operations platform with CLI, TUI cockpits, MCP server"
```
