Commit Graph

1130 Commits

Author SHA1 Message Date
jgrusewski
b07cc35f83 fix(ml): align TFT feature counts with data pipeline + fix S3 path-style upload
TFT create_model used TFTConfig::default() values for num_known_features(10)
and num_unknown_features(210) totaling 220, but input_dim was 51 from the
feature extractor. Set both explicitly: known=0, unknown=feature_dim.

S3 uploader now uses path-style requests (required for Scaleway S3) and
explicitly passes AWS credentials from env vars instead of relying on the
instance metadata credential provider (unavailable on Kapsule).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 14:29:34 +01:00
jgrusewski
33a91b07c0 fix(infra): use emptyDir for training output, fix S3 credential mapping
- Switch output volume from PVC to emptyDir (sidecar uploads to S3)
- Map s3-credentials secret keys to AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY
  (object_store crate expects standard AWS env var names)
- Fix data-dir path to include /futures-baseline subdirectory

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 14:09:00 +01:00
jgrusewski
d936ac36be feat(infra): auto-rotate GitLab PAT monthly via K8s CronJob
Adds a CronJob that runs on the 1st of every month at 03:00 UTC:
1. Reads current PAT from K8s Secret (gitlab-pat)
2. Calls GitLab rotate API (atomic: creates new, revokes old)
3. Updates K8s Secret with new token (3 retries)
4. Prints token to logs as recovery fallback if update fails

RBAC scoped to only get/patch the gitlab-pat secret.
Runs on gitlab node pool (zero GPU cost).

Manual trigger: kubectl -n foxhunt create job <name> --from=cronjob/gitlab-pat-rotation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 14:01:11 +01:00
jgrusewski
de8d7b86f8 chore: commit generated proto stubs for on-demand training RPCs
The e2e test crate uses pre-generated proto code (no build.rs).
Missing stubs for: ReportJobCompletion, ListPendingPromotions,
ApprovePromotion, RejectPromotion + message types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 13:55:40 +01:00
jgrusewski
73b368a0de fix(ci): detect GPU at runtime, partition sccache by compute cap
The CI runner pool has both L4 (sm_89) and H100 (sm_90) nodes.
Hardcoding CUDA_COMPUTE_CAP causes PTX mismatches when sccache
serves objects compiled for the wrong architecture.

- Detect compute capability via nvidia-smi at job start
- Partition sccache into /mnt/sccache/sm_89 and sm_90
- Each GPU type maintains its own warm cache

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 13:04:52 +01:00
jgrusewski
64a4f4fc0d fix(ci): purge sccache before test — stale L4 PTX fails on H100
sccache PVC holds CUDA kernel objects compiled for sm_89 (L4).
After moving the runner pool to H100 (sm_90), these produce
CUDA_ERROR_INVALID_PTX at runtime. Nuke the cache so candle
recompiles kernels for the correct compute capability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:59:03 +01:00
jgrusewski
0f9d756caa feat: on-demand training dispatch via K8s Jobs with sidecar uploader
Extend ml_training_service to dispatch GPU training jobs as K8s batch/v1
Jobs, collect results via a Rust sidecar uploader, and support model
promotion with operator approval via fxt CLI.

- K8s dispatcher creates Jobs on gpu-training pool with native sidecar
- training_uploader crate: watches DONE/FAILED marker, uploads to S3,
  reports completion via ReportJobCompletion gRPC
- PromotionManager compares metrics, queues better models for approval
- 4 new proto RPCs: ReportJobCompletion, ListPendingPromotions,
  ApprovePromotion, RejectPromotion
- fxt commands: train start, model list/approve/reject
- Training binaries write DONE/FAILED markers + metrics.json
- Dockerfile, K8s job template, and CI pipeline updated
- StartTraining gracefully falls back to in-process when outside K8s
- 27 new tests (16 service + 11 promotion), 141 total service tests pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:43:17 +01:00
jgrusewski
5853a704ce fix(ci): update CUDA_COMPUTE_CAP to 90 for H100 gpu-training pool
The CI runner now runs on H100 (compute cap 9.0), not L4 (8.9).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:55:55 +01:00
jgrusewski
365470fc3b ci: trigger pipeline after transient #79 failure
Pipeline #79 failed instantly (0 jobs created) - likely transient
GitLab issue. This empty commit triggers a clean pipeline #81.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:53:15 +01:00
jgrusewski
37d6a15fc3 fix(ml): correct sign error in continuous policy log_probs
The tensor-based log_probs() had an inverted sign on the squared
difference term: .sub(&(x * -0.5)) = +0.5*x² instead of -0.5*x².
This caused actions far from the mean to get higher log probabilities.

Also fix test assertions: continuous log probability densities CAN
be positive (when σ is small and action is near mean), unlike
discrete log probabilities which are always <= 0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:46:17 +01:00
jgrusewski
1a3e89f0c7 docs: add on-demand training dispatch implementation plan
10 tasks: completion markers, training-uploader crate, proto RPCs,
K8s dispatcher, promotion manager, fxt CLI commands, infra updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:42:54 +01:00
jgrusewski
6c39413680 docs: add on-demand training dispatch design
K8s Job dispatch from ml_training_service with Rust sidecar uploader,
S3 artifact storage, and model promotion with fxt CLI approval gate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:39:05 +01:00
jgrusewski
6a24c3d9c6 fix(ci): map deploy image names to actual K8s deployment names
Deploy was using underscore names (trading_service) but K8s deployments
use hyphens (trading-service), causing 7/8 services to silently fail.
Uses explicit image_repo:deployment:container mapping table.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:25:56 +01:00
jgrusewski
d1657871d3 fix(ci): rewrite kubeconfig to in-cluster API endpoint for deploy
The deploy pod runs inside Kapsule but the kubeconfig points to the
external API URL (api.k8s.fr-par.scw.cloud:6443). Due to pod CIDR
overlap with the egress path, the connection is reset by peer. Sed
rewrites the server URL to kubernetes.default.svc (in-cluster endpoint)
while keeping the scw exec plugin for token auth.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:19:16 +01:00
jgrusewski
013740f8a9 fix(ci): export SCW credentials for deploy kubeconfig exec plugin
The scw CLI used by the kubeconfig exec plugin (get-token) needs
SCW_ACCESS_KEY, SCW_SECRET_KEY, SCW_DEFAULT_PROJECT_ID, and
SCW_DEFAULT_ORGANIZATION_ID environment variables. Falls back to
project ID for org ID (common for single-org Scaleway accounts).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:38:44 +01:00
jgrusewski
51436e5cd0 fix(ml): update Mamba2 test fixtures for parquet→data_dir rename
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:30:15 +01:00
jgrusewski
017eafb9f7 Merge branch 'feature/training-pipeline' into main
feat(ml): add manually-triggered GitLab CI training pipeline

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:06:56 +01:00
jgrusewski
6e339316cf feat(ml): add manually-triggered GitLab CI training pipeline
Adds a parent/child GitLab CI pipeline for ML model training:

- Generator script produces per-model hyperopt/train/evaluate jobs
- Parent pipeline (.gitlab-ci-training.yml) with manual trigger
- NFS-backed ReadWriteMany PVC for shared training outputs
- Hyperopt params wired into training binaries (DQN, PPO, TFT, Mamba2)
- Shared DBN loader eliminates duplicate code across hyperopt adapters
- Supervised hyperopt unified to DBN data (was parquet-only)

Pipeline: hyperopt (4 models) → train (10 models) → evaluate ensemble

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:04:58 +01:00
jgrusewski
e3b8fe9382 fix(ci): deploy job uses infra-runner with scw + kubectl
Deploy was silently failing — bitnami/kubectl image lacks scw CLI
needed by Kapsule kubeconfig credential plugin. Now uses infra-runner
(has scw), installs kubectl inline until image is rebuilt. Adds
cluster-info check before deploy and proper error reporting per service.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 08:50:53 +01:00
jgrusewski
f0bbca23a5 perf(ci): compile training binaries with CUDA in compile-services job
Training image was recompiling from scratch inside Kaniko (~16+ min)
without sccache. Now training binaries are built in compile-services
with --features ml/cuda and PVC sccache (warm cache from service
binaries), then packaged into a CUDA runtime image (~30s).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 01:37:52 +01:00
jgrusewski
e72e4db235 refactor: delete 22 dead examples, 4 CSVs, consolidate data to test_data/
- Delete 22 dead/placeholder/broken example files (-3,489 lines code)
- Delete 4 tracked CSV files (-1.1M lines, were accidentally committed)
- Move baseline training data default from data/cache/ to test_data/
- Update 5 unified binary defaults, gitignore, k8s upload comment, docs
- Consolidate all training data under test_data/futures-baseline/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 01:24:02 +01:00
jgrusewski
c5db5aa39e perf(ci): compile once with PVC sccache, package with Kaniko
Split the build pipeline: one compile-services job builds all 8 service
binaries with PVC-backed sccache, saves as artifacts. Then 9 Kaniko jobs
just package pre-built binaries into slim runtime images (~30s each).

Before: 9 parallel Kaniko jobs each doing full cargo build --release
  (~20min each, no sccache, 9x duplicated dep compilation)
After:  1 compile job with sccache (~5min cached) + 9 package jobs (~30s)

- Add compile stage between test and build
- Add Dockerfile.runtime (minimal debian + pre-built binary)
- Add Dockerfile.web-gateway-runtime (Node dashboard + pre-built binary)
- Keep Dockerfile.training via Kaniko (needs CUDA dev image for H100)
- Remove all SCCACHE_BUCKET build-args from service builds
- Use dir:// context for Kaniko (only sends build-out/ dir, not full repo)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 00:50:25 +01:00
jgrusewski
754baeb434 perf(ci): skip check+clippy stage — test already compiles workspace
The check stage ran cargo check + clippy (~6min) before tests, but
the test stage compiles the same workspace anyway. Skip check with
an echo pass-through to save ~6min per pipeline. Easy to uncomment
when clippy enforcement is needed (e.g., before releases).

Also adds sccache stats to the test job for cache monitoring.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 00:24:57 +01:00
jgrusewski
c67ae083a9 fix(ci): remove stale SCCACHE_BUCKET override — S3 CI vars deleted
The SCCACHE_BUCKET, SCCACHE_REGION, and SCCACHE_ENDPOINT CI variables
were deleted from GitLab settings. The .rust-base override is no longer
needed — sccache now uses SCCACHE_DIR (PVC) by default.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 00:14:09 +01:00
jgrusewski
f6ef23c966 refactor(ml): delete 121 dead examples, 6 QAT tests, orphan binary
Mass cleanup of crates/ml/:
- Delete 121 dead/broken/superseded example files (-44,098 lines)
- Delete 6 QAT test files (-4,201 lines) — QAT is disabled at runtime
  (trainer.rs falls back to FP32 with warning)
- Delete 2 stale markdown files in examples/
- Delete orphaned src/bin/train_tft.rs (unimplemented stub)
- Clean up Cargo.toml: remove stale [[example]] entries, add missing ones
- Fix stale binary references in log_size_test.rs
- Add infra consistency test (35 checks across Dockerfile/train.sh/Cargo.toml)

Remaining examples (7): train_baseline_rl, train_baseline_supervised,
evaluate_baseline, hyperopt_baseline_rl, hyperopt_baseline_supervised,
download_baseline, cuda_test

All 2390 lib tests pass. All examples compile. 35/35 infra checks pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 00:05:38 +01:00
jgrusewski
ff8a275d47 fix(ml): widen flaky QAT checkpoint test range + use PVC sccache
- Widen observer checkpoint test bounds from 4σ to 5σ — random
  calibration data can exceed 4.0 with 100 batches of normal(0,1)
- Override SCCACHE_BUCKET="" in .rust-base so check/test jobs use
  PVC-backed SCCACHE_DIR instead of S3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:55:35 +01:00
jgrusewski
7b9da53603 infra(ci): switch CI builds from H100 to L4 — 4.7x cost reduction
Create dedicated ci-build node pool (L4-1-24G, €0.75/hr) replacing
H100 (€3.50+/hr) for CI builds. H100 reserved for training only.

- Add ci-build pool to Terraform (L4-1-24G, scale-to-zero, fr-par-2)
- Update GitLab Runner to target ci-build pool with L4-sized limits
- Change CUDA_COMPUTE_CAP from 90 (H100) to 89 (L4) in CI
- Dockerfile.training keeps CUDA_COMPUTE_CAP=90 for H100 training

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:30:04 +01:00
jgrusewski
71eefa6d53 refactor(ml): delete straggler train_mamba2/train_ppo examples
These two files survived the 20-file consolidation in 022036cb.
Both are now fully superseded:
- train_ppo.rs → train_baseline_rl --model ppo
- train_mamba2.rs → train_baseline_supervised --model mamba2

Also updates entrypoint-generic.sh usage examples to reference
the unified binaries (train_baseline_rl, train_baseline_supervised).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:24:53 +01:00
jgrusewski
267240530d perf(ci): enable Kaniko layer caching + Docker Hub auth on all builds
- Add --cache=true --cache-repo to all 12 Kaniko builds
- Cache Docker layers in Scaleway CR (rg.fr-par.scw.cloud/foxhunt-ci/cache)
- Add Docker Hub auth to devcontainer + infra-runner prepare jobs
- First build populates cache; subsequent builds skip base image pulls

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:20:44 +01:00
jgrusewski
022036cb96 refactor(ml): consolidate 21 training binaries into 2 unified baselines
Replace 20 per-model training examples with:
- train_baseline_rl: DQN + PPO (renamed from train_baseline)
- train_baseline_supervised: TFT, Mamba2, Liquid, TGGN, TLOB, KAN, xLSTM, Diffusion
  via model factory + UnifiedTrainable generic training loop

Update Dockerfile.training (16→7 binaries), train.sh MODEL_BINARY map,
and job-template.yaml default. -12,759 lines.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:16:25 +01:00
jgrusewski
373a6f33a1 fix(ci): add Docker Hub auth to Kaniko to avoid pull rate limits
All Kaniko builds hit Docker Hub anonymous rate limit (100 pulls/6h).
Add DOCKERHUB_USERNAME + DOCKERHUB_TOKEN to config.json alongside
Scaleway CR credentials.

Requires CI variables: DOCKERHUB_USERNAME, DOCKERHUB_TOKEN

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:15:40 +01:00
jgrusewski
51460a3382 perf(ci): switch sccache from S3 to local block storage PVC
- Create 20Gi sccache-pvc (scw-bssd) for fast local cache hits
- Mount at /mnt/sccache in CI build pods via runner helm values
- Use SCCACHE_DIR instead of SCCACHE_BUCKET for check/test jobs
- Kaniko builds keep S3 sccache (can't mount PVCs in Kaniko)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:12:34 +01:00
jgrusewski
387bc0d400 docs: add unified training binaries implementation plan
8-task plan covering rename, new supervised binary, deletions, and infra updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:05:10 +01:00
jgrusewski
45483d203a docs: unified training binaries design
Consolidate 21 training examples into 2 binaries:
- train_baseline_rl (DQN, PPO with RL walk-forward)
- train_baseline_supervised (8 models via UnifiedTrainable)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:59:51 +01:00
jgrusewski
a3cb195671 fix(docker): pin rclone to v1.69.1 — current download URL returns 404
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:51:51 +01:00
jgrusewski
ac688cac24 fix(ci): skip Rust builds on infra-only changes, remove stale files
Add `rules:` with `changes:` filters to check, test, and .kaniko-base
jobs so the Rust build pipeline only triggers when source code changes
(crates/, bin/, services/, Cargo.*). Prevents H100 spin-up on
infra-only pushes.

Remove 31 stale/orphan files (-11,885 lines):
- monitoring/ directory (duplicated by config/grafana + config/prometheus)
- 10 broken/placeholder migration files (.broken, .skip)
- 10 unreferenced config files (haproxy, nginx-lb, mutants, etc.)

Add 3 historical design docs from previous restructure work.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:45:44 +01:00
jgrusewski
9729395073 fix(ml): wire spread_cost_bps into all training examples, remove dead code
- Add tx_cost_bps/tick_size/spread_ticks CLI args to tggn, xlstm, diffusion
- Subtract spread + commission from target returns during data prep
- Delete unused validate_model_parameters from train_mamba2_dbn
- Wire validate_training_batch into mamba2 pre-training validation

All 16 training examples now compile with zero warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:30:24 +01:00
jgrusewski
6c59e16cd6 fix(ml): fix train_mamba2_dbn and train_dqn_es_fut compile errors
- Add missing Mamba2Config fields (early_stopping_enabled, patience, min_delta, min_epochs)
- Replace unwrap_or on f64 (not Option) with direct field access
- Replace .unwrap() on path.to_str() with safe .ok_or_else()
- Fix DQN closure signature: 3 args (epoch, data, is_final), not 2

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:19:22 +01:00
jgrusewski
0eff692cf3 feat(infra): IaC pipeline, tfstate migration, and drift detection
- Migrate tfstate backend from nl-ams to fr-par
- Add missing resources to TF: sccache bucket, foxhunt-ci registry,
  grafana + prometheus DNS records
- Add infra-runner Dockerfile (terraform + terragrunt + scw + glab)
- Add CI jobs: infra-plan (MR), infra-apply (merge), drift-check (weekly)
- All IaC jobs run on gitlab pool (zero extra cost)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:52:42 +01:00
jgrusewski
980489edc5 fix(ci): use relative artifact paths and avoid PIPESTATUS in drift check
- Artifact paths changed from /tmp/ to ${CI_PROJECT_DIR}/ for GitLab
  k8s executor compatibility
- Drift check uses $? instead of PIPESTATUS (Alpine sh doesn't support
  bash arrays)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:51:46 +01:00
jgrusewski
1caeb0b0d6 ci: add IaC pipeline (plan on MR, apply on merge, weekly drift check)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:48:57 +01:00
jgrusewski
9d20c3c97c infra: migrate tfstate backend from nl-ams to fr-par
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:48:33 +01:00
jgrusewski
3f4ae18a4c infra: add Dockerfile for IaC CI runner (terraform + terragrunt + scw + glab)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:47:59 +01:00
jgrusewski
d271315e7e infra(dns): add grafana + prometheus A records to Terraform
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:46:56 +01:00
jgrusewski
b321fa47a7 infra(object-storage): add sccache bucket to Terraform
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:46:45 +01:00
jgrusewski
c80899066c infra(registry): add foxhunt-ci namespace to Terraform
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:46:39 +01:00
jgrusewski
254b72d111 docs: IaC pipeline implementation plan (10 tasks)
Tasks 1-7: code changes (cleanup, module updates, Dockerfile, CI jobs,
root.hcl migration). Tasks 8-10: manual ops (state migration, resource
imports, image build, schedule setup).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:44:20 +01:00
jgrusewski
7d80a0db48 docs: IaC pipeline & tfstate migration design
Covers tfstate migration nl-ams→fr-par, resource import for
drift gaps (sccache bucket, foxhunt-ci registry, DNS records),
and GitLab CI/CD pipeline (plan on MR, apply on merge, weekly
drift detection).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:41:45 +01:00
jgrusewski
f3619ebcec fix(ci): add AWS_REGION for sccache S3 in Dockerfiles
sccache 0.8+ (opendal backend) requires AWS_REGION, not just
AWS_DEFAULT_REGION. Also set SCCACHE_REGION as belt-and-suspenders.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:39:39 +01:00
jgrusewski
6b9e58feee fix(infra): add wait_for_pool_ready=false for gpu-dev pool
Scale-to-zero pools start with 0 nodes, so Terraform's default
wait_for_pool_ready=true always fails with "state warning, wants ready".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:28:37 +01:00