Commit Graph

1771 Commits

Author SHA1 Message Date
jgrusewski
f08ea46395 fix(ci): auto-restart tailscale proxy on configmap changes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 16:59:58 +01:00
jgrusewski
2f75071551 fix(ml): keep DQN and PPO training pipelines in BF16 on Ampere+ GPUs
On Ampere+ GPUs the training dtype is BF16.  Network forward passes
with BF16 inputs return BF16 tensors, but all loss-path arithmetic
(rewards, dones, gamma, PER weights, Huber constants, atoms, clip
epsilon) was created as F32 from Tensor::from_vec.  Candle forbids
mixed-dtype binary ops, causing "dtype mismatch in {mul,sub,add}"
on every training step.

DQN fixes (7 mismatch sites):
- Cast current_q_values to F32 immediately after forward pass
- Cast all target network outputs (standard, dueling, C51, IQN) to F32
- Keep rewards/dones/gamma/weights/atoms/half/delta as F32 (remove
  .to_dtype(training_dtype) casts — now use DType::F32 sentinel)
- Entropy regularization and CQL paths now match (F32 + F32)

PPO fixes (3 mismatch sites):
- Use DType::F32 for clip epsilon one_tensor (was training_dtype BF16)
- Cast critic.forward() output to F32 in both MLP and LSTM value loss
- Cast target_returns to F32 for symlog/normalization path

Infra: revert runner-h100 from SXM2 (zero Scaleway quota) back to
ci-training-h100 PCIe pool.

2704 ml lib tests pass, 260 PPO tests pass, 0 clippy warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 16:52:56 +01:00
jgrusewski
528ca9eac1 fix(ci): update stale PVC comment in deploy job
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 16:47:49 +01:00
jgrusewski
cc209aec7e feat(infra): replace PVC binary distribution with MinIO S3 fetch
Eliminate foxhunt-binaries and training-binaries PVCs — services and
training jobs now fetch binaries from MinIO via rclone initContainers.
This removes L40S GPU autoscale-for-PVC-writes, removes RWO node
affinity constraints, and requires zero image rebuilds.

Changes:
- .gitlab-ci.yml: replace ~115 lines of binary-writer pod logic with
  aws s3 cp to MinIO (~25 lines)
- 8 service YAMLs + 2 GPU overlays: add rclone initContainer + emptyDir
- Training job template: MinIO rclone fetch replaces PVC copy
- Delete foxhunt-binaries-pvc.yaml and training-binaries-pvc.yaml
- Add s3.fxhnt.ai DNS record (Terraform) and nginx proxy block
- Replace pod-writer-deploy skill with MinIO-based deploy skill

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 16:46:57 +01:00
jgrusewski
3242abf54a fix(api_gateway): install ring CryptoProvider for kube-rs TLS
kube-rs Client::try_default() uses rustls for in-cluster TLS but no
CryptoProvider was installed, causing a panic on first pod list call.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 15:31:12 +01:00
jgrusewski
dd10497cfd fix(ml): cast Bellman target to F32 before TD error sub on BF16 GPUs
BUG #41 kept forward pass in F32 for autograd, but the target-side
tensors (reward, gamma, done, next_q) were cast to BF16 via `dtype`.
The `state_action_values.sub(&target_q_values)` then hit F32-vs-BF16
mismatch on Ampere+ GPUs, causing every training step to fail silently.

Fix: `.to_dtype(state_action_values.dtype())` on the detached target.
Safe because target is detached (no autograd graph to break).

Also: H100 runner → SXM2 pool, GPU availability checker script.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 15:29:15 +01:00
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