Commit Graph

1765 Commits

Author SHA1 Message Date
jgrusewski
ddfcd3fd39 fix(fxt): add tokio io-std feature for MCP stdin/stdout
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 15:08:40 +01:00
jgrusewski
ca06f71c88 Merge branch 'worktree-pods-versioning'
# Conflicts:
#	bin/fxt/src/error.rs
2026-03-04 14:59:55 +01:00
jgrusewski
e3c8428db4 feat(fxt): wire MCP server to real gRPC calls
All 32 MCP tool handlers now make real gRPC calls to the API Gateway
instead of returning stub responses. Covers: service monitoring,
training management, trading operations, ML inference, risk metrics,
broker status, data acquisition, cluster resources, agent control,
config management, and hyperopt tuning.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 14:54:38 +01:00
jgrusewski
def8bdc78a chore(fxt): delete dead integration test files
end_to_end_tests.rs and error_handling_tests.rs imported non-existent
modules (fxt::client, fxt::prelude, etc.) from a prior architecture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 14:44:32 +01:00
jgrusewski
61f88d3746 fix(monitoring): real streaming metrics, staleness detection, richer TUI
- StreamSystemStatus now queries real Prometheus data (was all zeros)
- StreamAlerts returns honest unimplemented (was sending empty events)
- Training epoch shows "N" not "N/0" (max_epochs not in proto)
- GPU power shows "300W" not "300W / 0W" when limit unavailable
- Services cockpit shows error reason when service is DOWN
- System status polling detects staleness after 3 consecutive failures
- Training cockpit header shows active K8s job count

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 14:44:05 +01:00
jgrusewski
667e480c09 fix(fxt): replace MCP server stubs with honest not_implemented errors
All 30 MCP tool handlers now return structured JSON error with
isError=true instead of fake "stub: would ..." placeholder responses.
Tool registrations preserved so list_tools still works.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 14:43:34 +01:00
jgrusewski
463b2cd130 refactor(fxt): complete TLI→FXT rename + fix token storage panic
- Rename TliConfig→FxtConfig, TliError→FxtError, TliResult→FxtResult
- Migrate token storage path foxhunt-tli→foxhunt-fxt with auto-migration
- Fix FileTokenStorage::Default panic (fallback to /tmp on missing HOME)
- Update all doc comments, login prompt, config.toml.example, env vars
- Update keyring service names foxhunt-tli-access→foxhunt-fxt-access
- Add TOKEN_REFRESH_BUFFER_SECS named constant
- Add clarifying comments for JWT dummy key and IBKR default client ID

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 14:43:07 +01:00
jgrusewski
edfe4da2e8 refactor(proto): remove duplicate config_service.proto
config_service.proto (package foxhunt.config, 4 RPCs) was a subset of
config.proto (package config, 12 RPCs). Migrated api_gateway to implement
ConfigService from config.proto directly. Removed dead config_service()
method from fxt client.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 14:17:44 +01:00
jgrusewski
091dee8dc5 refactor(fxt): rename all stale TLI references to FXT
57 references updated across source, tests, benches, and config.
Proto package name foxhunt.tli kept for wire compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 14:01:19 +01:00
jgrusewski
48e2213adc refactor(fxt): extract FILTER_ALL constant for gRPC "fetch all" empty string filters
Replace three occurrences of String::new() used as "fetch all" filter
values in gRPC request construction with a named FILTER_ALL constant,
making the intent explicit.

The api_gateway monitoring handler was reviewed but its Prometheus URL
appears only once in tests, so no constant was extracted (single-use).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:59:16 +01:00
jgrusewski
90520f76e4 fix(fxt): standardize dash rendering and display consistency in cockpits
- Use em-dash (—) consistently instead of mix of - and — for "no data"
  placeholders across all cockpits (data, trading, risk, services, pods)
- Collapse pipeline status section to single "not connected" message
  instead of showing 4 misleading rows of dashes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:59:14 +01:00
jgrusewski
2e40a19d6f fix(ml): keep PPO and ensemble pipelines in BF16 on Ampere+ GPUs
Same class of bug as the DQN fix (4c88498b): network outputs were
being cast to F32 mid-pipeline, defeating tensor-core acceleration
on H100/L40S. Now the full training loop stays in training_dtype()
(BF16 on CUDA Ampere+, F32 on CPU), with F32 casts only at scalar
extraction boundaries (to_scalar, to_vec1).

Files fixed:
- ppo.rs: Actor/Critic forward, act_with_log_prob, compute_losses,
  update_mlp, LSTM recurrent loop, predict method
- lstm_networks.rs: removed F32 output casts from both networks
- continuous_ppo.rs: one_tensor + scalar extractions
- hidden_state_manager.rs: zeros/ones use training_dtype()
- flow_policy/mod.rs: log_det accumulators + dummy log_std
- ensemble/adapters/ppo.rs + dqn.rs: F32 cast at extraction

2704 tests pass, 0 clippy warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:51:23 +01:00
jgrusewski
4c88498b8b fix(ml): keep DQN training pipeline in BF16 on Ampere+ GPUs
Remove premature F32 output casts from network forward passes
(Sequential, NetworkLayers, DistributionalDueling) that were negating
tensor-core acceleration. Instead, cast F32 input tensors (rewards,
dones, gamma, weights, atoms) to training_dtype() at the boundary
and only escape to F32 at scalar extraction points (loss value,
TD errors, logging vectors).

465 DQN tests pass, 0 clippy warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:28:06 +01:00
jgrusewski
7c8fa991cc feat(infra): standalone Prometheus deployment for foxhunt namespace
- ServiceAccount + ClusterRole (nodes, pods, endpoints, metrics)
- ConfigMap with 7 scrape jobs: self, foxhunt-services, annotated-pods,
  gitlab-annotated-pods, node-exporter, kube-state-metrics, dcgm-exporter,
  pushgateway
- Deployment (Prometheus v3.8.1, 14d retention, 1500MB size limit)
- PVC (2Gi scw-bssd), Service (port 80 → 9090)

Also includes pods-panel-versioning design doc.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:24:52 +01:00
jgrusewski
8aa8e6db6a Merge branch 'worktree-pods-versioning'
CalVer auto-versioning, Pods cockpit (key 7), cluster resources fix,
Prometheus NetworkPolicy fixes.
2026-03-04 13:20:57 +01:00
jgrusewski
19a3c66959 chore: update Cargo.lock for kube/k8s-openapi dependencies
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:19:51 +01:00
jgrusewski
6d37b95072 chore: add SubscribeClusterPods stub to trading_service + plan doc
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:17:41 +01:00
jgrusewski
ae0b2ed817 feat(fxt): wire Pods cockpit as key 7, update event loop
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:11:49 +01:00
jgrusewski
87b01cdbd5 feat(infra): RBAC for api_gateway pod listing
Add ServiceAccount, Role, and RoleBinding so the api-gateway pod can
list/get/watch pods and pod metrics in the foxhunt namespace. Also sets
serviceAccountName on the Deployment to bind the new SA.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:10:19 +01:00
jgrusewski
92ef7e21ec feat(infra): api-gateway K8s API egress for pod listing
Allow api-gateway to reach the Kubernetes API server for pod listing
(Pods cockpit). Two CIDR blocks are needed because Cilium applies
NetworkPolicy after DNAT:
- 10.32.0.0/12:443 — kubernetes.default.svc ClusterIP
- 172.16.0.0/16:6443 — real node IP after Cilium DNAT

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:10:18 +01:00
jgrusewski
22809ba373 feat(api_gateway): implement SubscribeClusterPods with kube crate
Add kube 3.0 + k8s-openapi 0.27 dependencies and create pods_handler.rs
that queries the Kubernetes API for pod status. Replace the UNIMPLEMENTED
stub in monitoring_handler.rs with a real streaming implementation that
polls pods every N seconds and maps them to PodInfo proto messages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:08:45 +01:00
jgrusewski
2571c2bbb3 feat(fxt): add Pods cockpit with service-grouped table
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:04:53 +01:00
jgrusewski
3c8fe238dd feat(fxt): add spawn_pods_stream with service grouping
Adds a new gRPC stream spawner that subscribes to SubscribeClusterPods
and groups pods by service name for the pods cockpit view. Follows the
same backoff/reconnect pattern used by all other stream spawners.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:02:10 +01:00
jgrusewski
8bb86a4630 feat(api_gateway): populate ServiceStatus.version from build_info
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:01:01 +01:00
jgrusewski
4024c92a03 feat(ci): compute CalVer FOXHUNT_BUILD_VERSION in compile jobs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:00:21 +01:00
jgrusewski
a714912cb6 fix(monitoring): wire cluster resources, fix Prometheus networking
- Add query_cluster_resources() for real CPU/RAM from Prometheus
- Wire into build_response() replacing hardcoded zeros
- Fix NetworkPolicy: add ports 8080/9100/9400 for KSM/node-exporter/dcgm
- Add CiliumNetworkPolicy for hostNetwork pod scraping
- Add SubscribeClusterPods stub to unblock proto addition from Task 5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 12:58:12 +01:00
jgrusewski
4951daea36 feat(fxt): add PodData/PodGroupData structs and StateUpdate::Pods
Add K8s pod data structures to TUI state layer for the upcoming pods
cockpit view. PodData represents individual pods, PodGroupData groups
them by service with ready/total counts. The StateUpdate::Pods variant
and apply_update handler wire the data path from gRPC streams to state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 12:56:40 +01:00
jgrusewski
c49608815a feat(proto): add SubscribeClusterPods streaming RPC
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 12:55:21 +01:00
jgrusewski
9d88898722 feat(common): add build_info module with CalVer version function
CI sets FOXHUNT_BUILD_VERSION (e.g. 2026.03.408); local dev falls
back to CARGO_PKG_VERSION.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 12:54:57 +01:00
jgrusewski
39848a926b fix(eval): read use_noisy_nets from hyperopt params in evaluate_baseline
NoisyLinear layers have different parameter names (sigma_weight/sigma_bias)
than standard Linear layers. Hardcoding use_noisy_nets=false caused
checkpoint loading to silently fail when the training run used noisy nets,
resulting in empty folds in the eval report.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 12:07:51 +01:00
jgrusewski
1931d93d66 feat(ci): add 2×H100 GPU pool for multi-GPU training
Pool ci-training-h100x2 (H100-2-80G, 160GB VRAM total).
MultiGpuConfig::detect() in ml crate auto-enables data parallelism.
Tags: kapsule,h100x2. Autoscale 0→1.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:32:26 +01:00
jgrusewski
f0c494ab11 Merge branch 'feature/fxt-production-fixes' 2026-03-04 10:40:38 +01:00
jgrusewski
5fe3608d92 fix(fxt,infra): production hardening — OTLP telemetry, TUI fixes, K8s infra
- Remove opentelemetry-otlp internal-logs feature (OTLP feedback loop)
- Switch trace sampling from AlwaysOn to 10% ratio-based
- Add RUST_LOG filtering (opentelemetry/h2/tonic/hyper=warn) to all 8 services
- Wire per-service latency measurement via health check → proto metadata → TUI
- Replace Vec::remove(0) with VecDeque ring buffers (O(1) vs O(n))
- Add Arc<AtomicBool> connected_sent for first-connected detection across 12 streams
- Add MAX_RECONNECT_ATTEMPTS (10) uniformly to all stream spawners
- Change kill switch/circuit breaker fields to Option types with N/A display
- Wire data_cache to real download status stream, remove dead cluster_events
- Remove ServiceData::new() hardcoded stubs, add honest placeholders
- Fix nanos_to_hms zero/negative guard, total_records semantic fix
- Fix RwLock held across yield in broker_gateway stream_account_state
- Add break after yield Err in broker/trading stream generators
- Fix connected_at advancing per tick in stream_session_status
- Tempo: replace emptyDir with 10Gi PVC, bump memory to 512Mi/2Gi
- Remove Prometheus gitlab-annotated-pods duplicate scrape job
- Wire 6 new gRPC streaming adapters (risk, trading, ml, data-acquisition)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 10:39:56 +01:00
jgrusewski
81fc16fb9b chore: demote verbose multi-objective logs to debug level
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 10:16:14 +01:00
jgrusewski
2381c61bb6 refactor(ci): rename GPU runner tags from 'gpu' to 'l40s' for clarity
Clear separation: l40s tag → L40S pool, h100 tag → H100 pool.
Updated runner-rl-values.yaml and CI base templates to match.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 09:48:25 +01:00
jgrusewski
a563c64dd3 feat(ci): add H100 GPU pool for hyperopt jobs
- Create ci-training-h100 runner (runner-h100-values.yaml)
- Route hyperopt-dqn and hyperopt-ppo to H100 via kapsule,h100 tags
- H100 is ~2x faster and cheaper per run vs L40S for hyperopt workloads

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 09:41:37 +01:00
jgrusewski
1ebbfea8f6 fix(eval): match all architecture params from hyperopt in evaluate_baseline
num_atoms, dueling_hidden_dim, v_min/v_max, gamma were using defaults
instead of hyperopt values — causing tensor shape mismatch on checkpoint
load (e.g. output layer 45×200=9000 vs 45×51=2295). Also fixed use_iqn
to read from use_qr_dqn key (matching trainer's field mapping).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 09:19:49 +01:00
jgrusewski
aabe1cf223 fix(ci): pass hyperopt params and tx costs to evaluate_baseline
evaluate_baseline was using default hidden_dim_base=128 instead of the
hyperopt-optimized 4096, causing checkpoint loading to fail silently
(tensor shape mismatch). This produced empty folds: [] in eval reports.

Also adds consistent --tx-cost-bps/--tick-size/--spread-ticks to
train_baseline_rl and evaluate_baseline in the hyperopt-dqn job so all
three pipeline stages (hyperopt, train, eval) use the same cost model.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 08:46:02 +01:00
jgrusewski
da598c9442 Merge branch 'feature/tui-live-data' 2026-03-04 08:12:57 +01:00
jgrusewski
d3c834eeb7 fix: pass reward-shaping params from hyperopt to walk-forward DQN
Hyperopt found hold_penalty_weight=1.818, curiosity_weight=0.402, etc.
but walk-forward training used tiny defaults (0.01, 0.1), causing the
agent to learn "holding is optimal" → 0 trades across all 50 epochs.

Now passes: hold_penalty_weight, max_position_absolute, huber_delta,
entropy_coefficient, curiosity_weight, weight_decay, kelly_fractional,
kelly_max_fraction from hyperopt JSON to DQNHyperparameters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 06:24:26 +01:00
jgrusewski
1d25d96f7e fix: walk-forward training passes hyperopt params, evaluator falls back to epoch checkpoints
Walk-forward DQN training hardcoded epsilon_start=1.0 with noisy_nets=true,
forcing pure random exploration for all 50 epochs (0 trades, 0 Sharpe).
Now reads epsilon, PER, dueling, distributional, noisy nets, and QR-DQN
params from the hyperopt JSON. When noisy_nets=true, defaults epsilon to
0.05 instead of 1.0.

Evaluator now falls back to highest dqn_fold{N}_epoch{E}.safetensors when
_best.safetensors doesn't exist (early stopping saves epoch checkpoints).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 04:03:21 +01:00
jgrusewski
6f68e59723 feat(tui): wire live gRPC data into watch dashboard
- Add data_fetcher.rs: StateUpdate enum, DataFetcher with 7 background
  stream/poller tasks (training metrics, system status, account state,
  executions, portfolio summary, download status, risk metrics)
- Wire event_loop.rs: FoxhuntClient connect, DataFetcher start,
  mpsc channel drain each tick, apply_update() for all 11 variants
- Add connection status indicator to status bar (LIVE/CONNECTING/OFFLINE)
- Replace mock data defaults with empty vecs/zeros in state.rs
- Add ConnectionStatus enum (Connecting/Connected/Reconnecting/Error)
- Exponential backoff (1s → 30s) on stream errors with auto-reconnect

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 02:43:21 +01:00
jgrusewski
63eff69bb6 feat(api_gateway): add all stream adapters + monitoring streams
Gateway poll-to-stream adapters:
- broker_gateway: StreamAccountState (3s), StreamSessionStatus (5s)
- data_acquisition: StreamDownloadStatus (5s)
- risk: StreamCircuitBreakerStatus (2s), StreamRiskMetrics (3s)
- ml: StreamModelStatus (5s)
- trading: StreamPortfolioSummary (3s), StreamOrderBook (1s)
- trading_agent: StreamAgentStatus (3s)

Monitoring handler real implementations:
- stream_system_status: fan-out health checks
- stream_metrics: Prometheus query loop
- stream_alerts: empty placeholder (no alertmanager yet)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 02:17:19 +01:00
jgrusewski
2c5f99aedb feat(proto): add 9 streaming RPCs for TUI live data
New poll-to-stream RPCs (gateway adapters):
- broker_gateway: StreamAccountState, StreamSessionStatus
- risk: StreamCircuitBreakerStatus, StreamRiskMetrics
- data_acquisition: StreamDownloadStatus
- ml: StreamModelStatus
- trading_agent: StreamAgentStatus
- trading: StreamPortfolioSummary, StreamOrderBook

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 02:10:00 +01:00
jgrusewski
d14b6a38f1 docs: TUI live data wiring implementation plan
17-task plan for replacing hardcoded mock data in fxt watch
with real gRPC streaming data from the API Gateway.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 02:04:08 +01:00
jgrusewski
1d0227b79e docs: TUI live data wiring design
Streaming-first architecture: 11 new poll-to-stream gateway adapters,
channel-based DataFetcher in the TUI, auto-reconnect with backoff.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 01:54:33 +01:00
jgrusewski
671ae541c6 Merge branch 'worktree-dqn-production-hardening' — DQN/PPO production hardening 2026-03-04 01:46:00 +01:00
jgrusewski
8ac49a2db3 fix(fxt): make AES-GCM tamper detection test more robust
Flip 4 positions in the base64 payload instead of 1 to eliminate
any chance of environmental flakiness from OsRng contention.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 01:44:27 +01:00
jgrusewski
74eba7920e fix(dqn): real confidence from softmax Q-values, read lock for concurrent inference
Added select_action_inference(&self) — pure greedy forward pass with
no exploration mutations (epsilon, noisy nets, count bonus, step counter
are all skipped). Returns (FactoredAction, f32) where confidence is
softmax probability of the selected action, clamped [0.5, 0.95].

Also added select_action_with_confidence(&mut self) for training path
with the same confidence computation.

Production wrapper now uses read lock (was write lock), enabling
concurrent predictions. Confidence is real softmax probability
instead of hardcoded 0.85.

323 trading_service tests, 15 DQN core tests, 0 clippy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 01:43:48 +01:00
jgrusewski
078771bf95 feat(fxt): auto-refresh tokens and FXT_PASSWORD env var for non-interactive login
- Auto-refresh: before non-auth commands, if the access token is expired
  but a refresh token exists, silently regenerate tokens. Prints
  "Token refreshed." to stderr so users know what happened.
- FXT_PASSWORD: login reads password from this env var when set,
  skipping the interactive prompt. Safer than a --password flag
  (env vars don't appear in ps output or shell history).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 01:34:34 +01:00