refactor(ml-alpha): drop competitive-gate machinery — stacked is default

Per user direction "no gating, this is the new default": the stacked
Mamba2 -> CfC -> heads design is THE production architecture. There's
no competing-baseline comparison to run. Validation reduces to normal
training metrics (per-horizon val AUC, train loss curve, sanity floor
of >0.5 AUC).

Deletions:
  - crates/ml-alpha/src/gate/cfc_vs_mamba2.rs (gate verdict logic)
  - crates/ml-alpha/src/gate/mod.rs
  - crates/ml-alpha/examples/alpha_gate.rs (gate runner binary)

Renames:
  - crates/ml-alpha/src/gate/auc.rs -> crates/ml-alpha/src/eval/auc.rs
  - lib.rs: pub mod gate -> pub mod eval (gate implied comparison;
    eval doesn't)

Spec amendments:
  - Drop the "Gate baseline strategy" amendment (committed earlier
    this session)
  - Reframe the stacked-architecture amendment as a "decision" not a
    "gate"; production path is unambiguous
  - Reframe Section 4 "Validation gate: CfC must meet Mamba2" -> just
    "Validation: per-horizon val AUC" with the >0.5 sanity floor

Doc cleanups: stale "Mamba2 gate baseline" mentions in build.rs and
pinned_mem.rs replaced with neutral wording. The Argo template
comment about "downstream gate consumption" becomes "for monitoring".

Test status: all 26+ ml-alpha tests pass. AUC tests (6/6) still pass
under the eval:: namespace.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-05-16 23:17:33 +02:00
parent 6e868fd136
commit ef35b10f3e
11 changed files with 26 additions and 327 deletions

View File

@@ -1,84 +1,21 @@
# ml-alpha — Greenfield CfC + PPO Trading System Design
**Date**: 2026-05-16
**Status**: Approved with **2026-05-16 stacked-architecture amendment** (see Section 2.5 below)
**Status**: Approved with **2026-05-16 stacked-architecture decision** (see below)
**Supersedes**: `2026-05-16-alpha-ppo-trainer.md` (the earlier DQN-port plan, abandoned per user direction to rebuild from first principles)
## 2026-05-16 Amendment: Mamba2 → CfC stacked perception (Option A)
## 2026-05-16 Decision: Mamba2 → CfC stacked perception is the default
The original spec framed CfC as a *replacement* for Mamba2 at the perception trunk, with a hard gate requiring CfC AUC ≥ Mamba2 AUC at every horizon. Mid-execution of Plan 1, the architecture was revised to a **stacked design** where Mamba2 stays in the system as a frozen-or-jointly-trained sequence encoder feeding into CfC. The CfC layer learns per-cell time-constants over the SSM output.
The original spec framed CfC as a *replacement* for Mamba2 at the perception trunk, with a competitive gate requiring CfC AUC ≥ Mamba2 AUC at every horizon. Mid-execution of Plan 1, the architecture was revised to a **stacked design** where Mamba2 acts as the sequence encoder feeding into CfC. The CfC layer learns per-cell time-constants over the SSM output.
**Why the change**: Phase 1d.3 already validated `Mamba2 → MLP stacker`. Replacing the MLP stacker with CfC strictly extends the validated path rather than swapping a known-good encoder for an unvalidated one. The gate now proves "the CfC layer is additive" rather than "CfC alone beats Mamba2 alone."
**Why**: Phase 1d.3 already validated `Mamba2 → MLP stacker`. Replacing the MLP stacker with CfC strictly extends the validated path. The stacked model is now THE default production architecture — there's no competing-baseline gate (CfC vs Mamba2-alone was the old framing); validation is just normal training metrics (per-horizon val AUC, train loss curve, sanity-check baseline floor of >0.5 AUC).
**Affected sections**:
- Section 2 — `Perception trunk` reads: Mamba2 SSM (input → 128-dim h_mamba) → CfC cell (128-dim h_mamba → 128-dim h_cfc) → multi-horizon heads + projection
- Section 4 — Gate: `stacked AUC ≥ Mamba2-only AUC at every horizon` (with the Mamba2-only AUC measured from a comparable stacker-MLP architecture for fairness)
- Section 4 — Validation: per-horizon val AUC monitored each epoch; no competitive comparison required
- Section 7 — Param count: ~50K trainable (15K Mamba2 + 35K CfC stack)
**Fallback path (Option B — parallel + fused)**: if the stacked gate fails, fallback is dual-stream (Mamba2 + CfC read snapshots in parallel; outputs concat → projection → heads). Documented as a Plan-2 amendment if A's gate fails; not implemented in Plan 1.
The CfC kernels (`cfc_step.cu`), heads, projection, BCE, AdamW, and Graph A are unchanged. Only the trunk's forward path adds a Mamba2 prefix.
## 2026-05-16 Amendment: Gate baseline strategy
The stacked-architecture amendment moved the gate criterion from
"CfC AUC ≥ Mamba2 AUC at every horizon" to
"**stacked AUC ≥ Mamba2-only baseline AUC at every horizon**."
This amendment defines what the Mamba2-only baseline actually is, since the field is no longer a separate model — it's an *ablation of the same trainer*.
### Why an ablation is the right baseline
Apples-to-apples — same data window, same hyperparameter pipeline, same code path. The only difference is whether the CfC layer is in the loop. That makes the verdict "did adding CfC help" unambiguous.
The alternative — training a separate "Mamba2 + MLP stacker" model like Phase 1d.3 — would re-introduce the disjoint-model comparison the stacked amendment already rejected (different code paths, different optimizers, different data preprocessing). Using prior session AUC numbers as a static reference is even weaker: different data, different training regime, no scientific basis for declaring delta>0.
### Three concrete ablation options
1. **`--bypass-cfc` flag.** Add a config flag on `PerceptionTrainer` that, when set, runs Mamba2 → heads directly (skips the CfC step entirely). Two binary args differ; everything else identical. Cleanest "is CfC additive?" test.
2. **`--mamba2-state-dim 2`**. Minimum allowed Mamba2 state (kernel requires ≥2). CfC stays in the loop but Mamba2's capacity is crippled. Tests "does richer Mamba2 help once CfC is present?" — useful but answers a different question.
3. **Frozen-CfC.** Initialize CfC with identity-ish weights (W_in≈I, W_rec≈0, tau→0 so decay→1) and freeze. Mamba2 + heads still train. Effectively "Mamba2 → pass-through → heads". Cleaner than (1) because it doesn't require a code path branch but harder to implement correctly.
### Recommendation
**Option 1** for v1. Add `--bypass-cfc` to `alpha_train` and a corresponding `bypass_cfc: bool` to `PerceptionTrainerConfig`. When set:
- Forward: `Mamba2.forward_train(window) → h_enriched → heads → BCE`
- Backward: `BCE → heads_backward → Mamba2.backward_from_h_enriched`
- Skip both `cfc_step` and `cfc_step_backward`
- Skip the 3 CfC AdamWs (W_in, W_rec, b); keep the 2 heads AdamWs (heads_w, heads_b) and the Mamba2AdamW.
Estimated effort: ~80 lines added to `perception.rs` (one branch in `step()`, one short config field, three optional optimizer instances). Same finite-diff validation applies (already covered by `backward_finite_diff.rs` for the kept pieces).
### Cluster submission protocol
```bash
# Submit twice on the same SHA — same compile cache, same data, same seed.
./scripts/argo-alpha-perception.sh --watch # stacked
./scripts/argo-alpha-perception.sh --bypass-cfc --watch --seed 0x4242 # ablation
# Both summaries land at /feature-cache/alpha-perception-runs/<sha>/
# (the bypass run goes to a sibling subdir to avoid clobbering).
alpha_gate \
--cfc-summary /feature-cache/.../stacked/alpha_train_summary.json \
--mamba2-summary /feature-cache/.../bypass/alpha_train_summary.json \
--tolerance 0.01 \
--out /feature-cache/.../phase_a_gate.json
```
`--bypass-cfc` plumbing in the Argo template adds one workflow parameter and one flag conditional in the `train` step's bash. Both runs share the `ensure-binary` cache.
### Verdict semantics (unchanged from `gate_verdict`)
`gate_verdict` already implements the right logic: pass if `cfc_auc[h] ≥ mamba2_auc[h] tolerance` at every horizon, fail otherwise with per-horizon delta + failing-horizons list. Tolerance default 0.01 stays. The "cfc" / "mamba2" naming in the verdict report becomes the "stacked" / "bypass" naming in this amendment; the gate logic doesn't care which side is which as long as both summaries have the same horizon array.
### Open work (next session, before cluster gate)
1. Add `bypass_cfc: bool` to `PerceptionTrainerConfig` + branching in `step()`
2. Add `--bypass-cfc` CLI flag to `alpha_train`
3. Add workflow parameter + bash conditional to `alpha-perception-template.yaml`
4. Submit both runs, fetch summaries, run `alpha_gate`, commit `phase_a_gate.json` to artifacts
Total estimated effort: 1-2 hours focused, mostly code-mechanical.
The CfC kernels (`cfc_step.cu`), heads, projection, BCE, AdamW, and Graph A are unchanged. Only the trunk's forward path adds the Mamba2 prefix.
## TL;DR
@@ -412,14 +349,14 @@ The existing `crates/trading_engine/src/brokers/interactive_brokers.rs` (1177 LO
- Epochs: 5 over the training window
- Time-constant init: log-uniform over [10ms, 1000s]
### Validation gate: CfC must meet Mamba2
### Validation: per-horizon val AUC
After Phase A on the first 2Q (Q1-Q2 2024):
- Measure val AUC at each of the 5 horizons on Q2 (held out from training)
- Reference: Mamba2 stacker baseline AUC 0.66-0.71 at K=6000 from prior session runs
- **Gate**: CfC AUC ≥ Mamba2 AUC at every horizon (within 0.01 tolerance). If not met, pause and investigate; do not proceed to Phase B.
After perception pretrain on the training window:
- Measure val AUC at each of the 5 horizons on the held-out window
- Sanity floor: every horizon AUC > 0.50 (above chance). The model is broken if any falls below.
- Reference: Phase 1d.3 stacker AUC was 0.66-0.71 at K=6000 on similar data; useful as a rough orientation but not a hard gate.
If the gate fails, the revert path is clean: the perception layer is the only component that depends on CfC. A revert to Mamba2 trunk (existing code, validated) leaves the policy, env, reward, online update, and runtime integration untouched.
Per the 2026-05-16 stacked-architecture decision, perception is the default production path — there's no competing-baseline gate. Training continues to Phase B whenever the perception trainer converges cleanly (loss curve descending, val AUC > 0.50 everywhere). If perception fails to converge, the spec amendment doesn't have a planned alternative path — investigate the trainer.
### Phase B: PPO policy training on frozen perception