Commit Graph

1368 Commits

Author SHA1 Message Date
jgrusewski
b93971726d feat(rl): B-11-β Q-distill informativeness gate
First behavior change since B-7 (preceding B-8/B-9/B-10 were
observability-only). Attenuates the distill gradient when softmax(Q/τ)
is near-uniform — when target_entropy → ln(N_ACTIONS), the distill term
is pure max-entropy regularization with no informational signal, so it
fights PPO instead of carrying Q's preferences into π.

Verified cluster cause @ alpha-rl-88f5c (B-10 smoke, c1dc84a34):
target_entropy = 2.3976 / ln(11) = 2.398 max → 99.98% of max with Q_range
13.79 and τ=20.18. Distill at λ=0.224 was applying ~uniform-target KL
regularization across 20k steps → eval pnl -$218M at full run
(alpha-rl-8gtk2).

Gate (per-block, smooth):
  λ_eff = λ_base × max(0, 1 − h_target / ln(N_ACTIONS))^p
where λ_base is the existing KL-target Schulman controller's output
(slot 486, untouched) and p defaults to 2.0 (slot 744).

Bitwise disabled-mode: when RL_Q_DISTILL_INFO_GATE_ENABLED_INDEX = 0.0
the ternary forces gate_factor = 1.0 verbatim and s_lambda_eff = lambda
exactly. Same ISV-toggle pattern as B-7's reward-clamp control (slot 724).

Source delta:
- 3 new ISV slots: RL_Q_DISTILL_INFO_GATE_ENABLED_INDEX = 743,
  RL_Q_DISTILL_INFO_GATE_SENSITIVITY_INDEX = 744,
  RL_Q_DISTILL_LAMBDA_EFFECTIVE_INDEX = 745; RL_SLOTS_END 743 → 746.
- Bootstrap array 230 → 233 (defaults: ENABLED=1.0, SENSITIVITY=2.0,
  LAMBDA_EFFECTIVE=0.0 sentinel; kernel block-0 overwrites every step).
- rl_q_pi_distill_grad.cu: shared s_lambda_eff, per-block target-entropy
  reduction in the existing thread-0 s_pi_target block, gate compute,
  __syncthreads broadcast; grad_distill uses s_lambda_eff. Block-0 emits
  slot 745 alongside existing B-10 G2 emits.
- Diag: policy_diagnostic.q_distill_lambda_effective (672 leaves total).
- New invariant smoke: tests/q_distill_info_gate_invariants.rs (4 gates).

Local 200+50 b=16 fold-1 smoke validates all 4 invariants across 249 rows:
peak h_target/ln(N) = 1.000 (β cause confirmed at smoke scale too); gate
factor range [0, 5.99e-6]; 132 saturated rows (h_frac ≥ 0.999). l_q_b
healthy throughout.

NOT byte-identical to pre-B-11-β runs by design: gated grad_distill
changes π → action distribution → trade outcomes. V4 (spec §4) calls
this out explicitly. Cluster comparison is on aggregate signals (eval
pnl, l_pi trajectory), not exact reproducibility.

Falsification criteria (spec §2.G5) gate the next iteration:
- eval pnl > -$50M  → β was dominant, B-11-β is the fix
- -$50M to -$100M  → β contributed, write B-11-γ (advantage standardization)
- < -$100M          → β not dominant alone; B-11-α + B-11-γ combined

Risk D explicit threshold (spec §8): if λ_base (slot 486) exceeds 2× its
pre-B-11-β baseline EMA sustained 5k steps, the KL-target controller is
in compensatory-ramp territory and B-11-β.1 gates the controller's input
on the gated KL.

Spec: docs/superpowers/specs/2026-06-01-b11-beta-q-distill-informativeness-gate.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 13:00:06 +02:00
jgrusewski
8c7ce02da9 feat(rl): B-10 policy-quality cascade diagnostic
alpha-rl-8gtk2 (B-7+B-8+B-9 run at SHA 87a8259c6) exposed a degenerate
equilibrium at step 6478 (~32% through 20k train): l_pi diverged 15×
from best (stale 478 steps), l_q + l_v bit-flat (stale 524/615 steps),
π near-uniform (entropy 2.36 / ln(11)=2.40 max, Hold-dominant 32%),
Kelly fraction floored at 0.025 with consistently-negative EV.

Three lit-confirmed candidate root causes (perplexity 2026-06-01):
  α Q-collapse via adaptive C51 EWMA atom support — Bellemare projection
    collapses target distribution to one-hot at center atom under
    EWMA-narrowed [V_MIN_eff, V_MAX_eff], yielding near-zero CE without
    Q having any information content.
  β Q→π distill amplification — softmax(Q/τ) is uniform for ANY τ when
    Q is uniform across actions, so high-τ distill = pure max-entropy
    regularization regardless of τ value.
  γ Heavy-tailed advantage normalization — per-batch standardization
    (Schulman 2017 canonical, computed in `rl_advantage_normalize.cu`,
    publishes var to slot 612) produces 5-10σ outliers under B-7's
    unclamped reward distribution → PPO surrogate magnitude explodes.

B-10 ships PURE OBSERVABILITY across 4 groups (G1 Q-dist informativeness,
G2 Q-distill effectiveness, G3 PPO advantage normalization, G4 PPO
surrogate decomposition). No controller, no behavior change, no atom-span
modification. The falsification criteria predetermine the B-11 path the
next cluster run motivates.

Changes:
- 13 new ISV slots (730-742) in isv_slots.rs; `RL_SLOTS_END = 743`.
- 13 bootstrap entries; fixed-size array `[(usize, f32); 217]` → 230 at
  integrated.rs:3394.
- New kernel `rl_q_distribution_stats.cu` — two entry points
  (`rl_q_distribution_per_batch` + `rl_q_distribution_reduce`) computing
  online Q distribution entropy + E_Q range + |E_Q| max via single-block
  tree-reduce (B-9 pattern). Reads online `q_logits_d` + `atom_supports_d`.
- New kernel `rl_ppo_diagnostic_stats_reduce.cu` — cross-batch reducer
  over 4 per-batch scratches (`ppo_a_norm_pb`, `ppo_ratio_dev_pb`,
  `ppo_ratio_clipped_pb`, `ppo_surrogate_pb`) written by surrogate_fwd.
  Reads slot 612 (var_pre_norm) for σ_used = sqrt(var); reconstructs
  |A_unnorm| stats from |A_norm| × σ_used.
- Modified `ppo_clipped_surrogate.cu` — 4 new pointer params for the
  scratches; writes happen in the same `act == 0` branch that already
  owns per-batch reductions, alongside existing loss_pi_per_b. Loss
  reduce path (`ppo_loss_reduce_b.cu`) and ratio path
  (`ppo_log_ratio_abs_max_b.cu`) untouched.
- Modified `rl_q_pi_distill_grad.cu` — adds 2 inline emits (slot 733
  target entropy, 734 target-π entropy diff) in the existing batch-0
  diagnostic block; controller writes (slot 486 λ, slot 487 τ) untouched.
- 5 new `[B] f32` scratch buffers + 2 new function handles in
  `PolicyHead` + 2 new function handles in `DqnHead`. New launch methods
  `launch_q_distribution_stats` (DqnHead) and
  `launch_ppo_diagnostic_stats_reduce` (PolicyHead) following B-9 pattern.
- Trainer wires: G1 launch after `dqn_head.forward(h_t)` at line 6615;
  G3+G4 reducer call right after `surrogate_forward` at line 5211
  (single call site verified — `surrogate_forward` is invoked once per
  step_with_lobsim_gpu pass).
- 14 new diag leaves under `policy_diagnostic` block: 13 from new slots
  + 1 from existing slot 407 (`rl_q_pi_agree_b` was writing every step
  but never reached diag; per B-10 spec §6 Open Decision 4 audit).
- `EXPECTED_LEAVES` 657 → 671 in `eval_diag_emission.rs`.

Local validation (RTX 3050 Ti, 200+50 b=16 fold-1 smoke):
  * Build clean, release binary 1m 43s.
  * Schema parity test path: train = eval = 671 leaves ✓
  * 11/14 new diag fields populating correctly:
    - q_distill_target_entropy = 2.395 (near-uniform ⇒ G2 working)
    - q_pi_agree_ema evolving 0.41 → -0.80 → 0.36 across training
    - PPO scratches show non-zero ppo_a_norm_mean=0.65 at step 50
      (when advantages have signal) and 0 elsewhere (correct: A=0 at
      most steps with replay still warming).
  * 3/14 fields (G1 q_dist_*) return 0 locally — possible sm_86-specific
    runtime issue with the new `rl_q_distribution_stats` kernel; will
    debug on the next cluster run (H100 sm_90) which is the canonical
    diagnostic environment for B-10's intended cluster validation.

Spec: docs/superpowers/specs/2026-06-01-b10-policy-quality-cascade-diagnostic.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 12:20:42 +02:00
jgrusewski
29b5acad55 feat(rl): B-9 C51 Bellman-target saturation observability
Under B-7 (clamp default-disabled), per-transition rewards in
bellman_target_projection / bellman_fused_select_project can exceed the
adaptive atom support [V_MIN_eff, V_MAX_eff]; each t_z overshoot is
silently clamped before being mapped onto the discrete support. B-9
publishes the per-step saturation rate + pre-clamp t_z extremes so we
can decide if the atom span has become a bottleneck — without
re-attempting the reverted Fix F atom-widening (pearl_c51_v_max_freeze
_required_for_surfer).

Changes:
- 4 new ISV slots (726-729): top/bot saturation rate + max/min pre-proj.
- bellman_target_projection.cu: both entry points (bellman_target_projection
  AND bellman_fused_select_project per feedback_no_partial_refactor) gain
  4 new [B] f32 pointer params; thread-0 sequential reduction over
  Q_N_ATOMS=21 (odd count rules out symmetric tree-reduce — matches the
  kernel's existing softmax max/sum pattern at lines 157/171).
- New cross-batch reducer cuda/rl_bellman_target_saturation_reduce.cu:
  single block, grid-stride gather + power-of-2 tree reduce, no atomicAdd
  per feedback_no_atomicadd.
- dqn.rs: load reducer cubin, add saturation_reduce_fn handle,
  launch_saturation_reduce method, 4 scratch pointer params on both
  bellman methods.
- integrated.rs: allocate 4 [B] f32 scratch buffers; pass through both
  fused_select_and_project_bellman call sites + launch reducer after each.
  4 new bootstrap entries (213 → 217 fixed-size array).
- build.rs: register new kernel.
- 4 new diag leaves under risk_stack.atom_calibration.target_*. Comment
  distinguishes them from popart.max_abs_reward_ema (different signal:
  Bellman target = r + γ·atom_value, can exceed reward by γ·V_MAX_eff).
- EXPECTED_LEAVES 653 → 657.
- tests/c51_atom_saturation_diagnostic.rs: GPU-oracle test asserts
  4 invariants over 249 rows — rates in [0,1], max≥min, rate>0 ⇒
  overshoot exists, top+bot ≤ 1.

Validation:
- 200+50 b=16 fold-1 smoke clean. Locally V_MAX_eff adapts to ~19.3
  (atom support is ISV-driven via rl_atom_support_update), so saturation
  is 0% in the smoke; both diag leaves emit + invariants hold.
- popart_disaggregation_invariants: still passes (249 rows, identity).
- eval_diag_emission: train = eval = 657 leaves.
- Determinism preserved: kernel adds shared-mem reduction over per-thread
  t_z values that were already computed; target_dist output unchanged.

Spec: docs/superpowers/specs/2026-06-01-b9-c51-atom-saturation-diagnostic.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 10:31:18 +02:00
jgrusewski
1739d9c173 feat(rl): B-8 popart σ_welford disaggregation + identity invariant
Under B-7 (clamp default-disabled), popart's Welford state now updates
against unclamped magnitudes; σ_effective = max(σ_welford, env.max) can
spike from either source but diag only emitted the combined value. This
blocks attribution of any future σ shocks.

Changes:
- RL_POPART_SIGMA_WELFORD_INDEX (slot 725): Welford-only σ, BEFORE the
  F4 envelope floor at rl_popart_normalize.cu:156. Pure observability —
  no computation change.
- rl_popart_normalize.cu: insert one ISV write between σ_welford
  computation (line 141) and envelope-floor application (line 156).
  Mirrors the existing #define-local-then-write pattern (POPART_SIGMA_INDEX).
- build_diag_value: new leaf popart.sigma_welford in the canonical
  popart block. NOT duplicating max_abs_reward_ema (already emitted at
  risk_stack.regime.popart_envelope.max_abs_reward_ema per
  feedback_single_source_of_truth_no_duplicates).
- EXPECTED_LEAVES 652 → 653.
- Bootstrap array [(usize, f32); 212] → 213 with sentinel 0.0
  (overwritten every step by popart kernel).
- tests/popart_disaggregation_invariants.rs: GPU-oracle test asserts
  identity popart.sigma == max(σ_welford, env.max) across 249 rows
  (200 train + 50 eval), skipping step 0 bootstrap.
- tests/eval_diag_emission.rs: migrate fold-idx 0/n_folds 2 → 1/3
  (the n_folds=2 split picks the first 4 files which don't satisfy
  loader's 1033-snapshot minimum after test_data grew from 2 → 9 files).

Validation:
- popart_disaggregation_invariants passes locally (249 rows OK).
- eval_diag_emission passes locally: train=653 eval=653 leaves.
- B-9 spec at docs/superpowers/specs/2026-06-01-b9-c51-atom-saturation-diagnostic.md
  builds on slots 726-729 next (uncommitted, awaiting implementation).

Spec: docs/superpowers/specs/2026-06-01-b8-popart-calibration-observability-and-floor.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 10:15:36 +02:00
jgrusewski
bc9eaac89d fix(rl): B-6 — ISV-driven adaptive asymmetric Wiener-α (Bayesian shrinkage)
B-5 (asymmetric α with static α_slow=0.001) revealed the static parameter
problem: provably bounds cascades (avg_win peak $2k vs B-4's $40k) BUT
over-conservative in train (dckcc step 800: avg_l > avg_w → Kelly says
don't trade → model can't discover edges; wr_ema crashed to 0.145).

The fundamental tension: static α_slow can't satisfy BOTH
  - Train convergence: asymmetry must FADE so model learns from real data
  - Boundary safety: asymmetry must ENGAGE at every fold to prevent cascade

B-6 RESOLVES this via Bayesian shrinkage:

  trust(n)   = min(1, cum_dones / n_full_threshold)           [Phase 1]
  stability  = exp(-CV × cv_gain)                              [Phase 2]
  trust_eff  = trust(n) × stability
  α_slow_eff = α_slow_min + (α_fast − α_slow_min) × trust_eff

Phase 1 (data-quantity): trust grows with cum_dones; reset_session_state
zeroes cum_dones → asymmetry RESUMES at every boundary. Math: at n=0
α_slow_eff = α_slow_min = 0.001 (full skepticism). At n=n_full = 30k
trades: α_slow_eff = α_fast = 0.05 (full standard Wiener).

Phase 2 (data-quality): Welford CV of reward magnitude gates trust. Stable
signal (CV→0): stability=1, trust opens normally. Volatile signal (CV high):
stability→0, asymmetry persists. cv_gain=0 disables Phase 2.

Per-EMA asymmetry direction encodes Kelly safety semantics:
  avg_win:  slow-up (skeptical of wins),    fast-down
  avg_loss: fast-up (admit losses),         slow-down (slow forget)
  wr_ema:   slow-up (skeptical of high WR), fast-down

ISV slots (all signal-derived from cum_dones + Welford):
  721 RL_EMA_ALPHA_SLOW_MIN_INDEX           = 0.001
  722 RL_EMA_TRUST_FULL_THRESHOLD_INDEX     = 30000
  723 RL_EMA_CV_GAIN_INDEX                  = 1.0

Threshold calibration (n_full=30k):
- Train: ~1000 cluster steps for trust to fully open → asymmetry active
  during cold-start (first 30 steps, cascade prevention) then fades.
- Eval: 30 dones/step × 500 eval steps = 15k dones → trust climbs to 0.5
  by eval end → partial protection throughout eval.

Composes:
- B-3 Kelly fractional-trust (Kelly SIZING gated by cum_dones)
- B-6 EMA asymmetric-α (Kelly INPUTS biased conservative by cum_dones)
Both fade as data accumulates; both reset at boundary.

Spec: docs/superpowers/specs/2026-06-01-ema-asymmetric-trust-with-cv-gain.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 01:13:27 +02:00
jgrusewski
16cf9f260c fix(rl): B-2 — pos_max_ema cold-start cascade eliminated, ISV-driven cap
The addendum's rate-cap (B-1) only protected the Wiener-α path; the first-
observation bootstrap branch let cold-start fat-tail events seed pos_max_ema
unbounded. At alpha-rl-4xmxm step 5, a single $947 scaled reward bootstrapped
pos_max_ema=879 directly, cascading through clamp_win → unclamped subsequent
rewards → env.max=11375 by step 37 (1500× the eventual steady-state σ).

B-2 fix per docs/superpowers/specs/2026-05-31-pos-max-ema-cold-start-redesign.md:

1. Bootstrap RL_POS/NEG_SCALED_REWARD_MAX_EMA_INDEX to MIN_WIN=1.0 (was 0)
   in `with_controllers_bootstrapped`. Conservative neutral value → clamp_win
   starts at MARGIN × 1.0 = 1.5 → rewards heavily clipped until adaptation.

2. Remove the `if (ema_prev == 0.0f) ema_new = pos_max;` branch from
   `rl_reward_clamp_controller.cu`. Single uniform update rule (Wiener-α +
   rate-cap) applies from cold-start onward. Mirror change for neg_max_ema.

3. Replace `#define POS_MAX_EMA_MAX_GROWTH_PER_STEP 1.5f` with ISV-driven
   reads (per feedback_isv_for_adaptive_bounds). Three new slots:
     717 RL_POS_MAX_EMA_COLD_START_INDEX             = 1.0
     718 RL_POS_MAX_EMA_GROWTH_CAP_BASE_INDEX        = 1.225 (√1.5 for
                                                       twice-per-step inv)
     719 RL_POS_MAX_EMA_GROWTH_CAP_CV_GAIN_INDEX     = 0.0   (adaptive layer
                                                       disabled by default)

4. Adaptive growth_cap from Welford CV of reward magnitude (slot 615-617)
   when cv_gain > 0: stable regime → tight cap, volatile regime → loose.
   Disabled by default; opt-in via ISV tuning.

Local smoke validation (800+200 fold-1 b=16):
- step 1:  pos_ema=1.0 (initialized, NOT bootstrapped from observation)
- step 5:  pos_ema=1.0 (no observation yet, sparse-skip working)
- step 25: pos_ema=63.8 (vs 1314 without B-2 — 21× reduction)
- step 37: pos_ema=32.5 (vs 7074 without B-2 — 218× reduction)
- env.max @ step 37: 126 (vs 11375 without B-2 — 90× reduction)

Generalizes the pattern: NORMALIZATION EMAs that gate signal magnitudes
should bootstrap to CONSERVATIVE neutral values, NEVER to first observation.
Cross-references pearl_first_observation_bootstrap which needs revision.

Phase 4 audit (other controllers with same anti-pattern) deferred to
follow-up — see spec §4 Phase 4 for the grep target list.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 23:09:59 +02:00
jgrusewski
1aa92f57f0 fix(rl): eval-boundary addendum — reward_scale warmed_flag + pos_max_ema rate-cap
The parent eval-boundary fix (72684ed3e) preserved env.max/clamp EMAs but
left the σ explosion intact. Diagnosis from alpha-rl-jnct8 (10k+2500 eval,
fold-1, -$106M eval pnl, σ → 4451 by eval step 5) revealed two pre-existing
mechanisms compounding the eval shock:

ISSUE A — reward_scale snaps to 0.10 at boundary
================================================
rl_fused_controllers' reward_scale block had a bootstrap-fraction-floor
gate: `(cumulative_dones < min_trades) → boot_floor = 0.10`. The intent
was cold-start protection (prevent scale crash before any closed-trade
ground truth). But cumulative_dones (slot 660) is reset by
reset_session_state for Kelly's PREDICTIVE-warmup purpose. At fold
boundary the gate fires again, clamping the preserved scale (0.0046 in
jnct8) UP to 0.10 — a 22× upward jump. Eval rewards are then 22× larger,
overwhelming env.max preservation; σ explodes regardless.

FIX A — decouple via monotonic warmed_flag (slot 716, never reset).
Set ONCE when cumulative_dones first crosses min_trades; persists across
all subsequent fold boundaries. boot_floor reads the flag instead of
re-evaluating trade_count. Math: at cold-start flag=0 → boot_floor=0.10
(original protection preserved). Post-warmup flag=1 → boot_floor=scale_min
(~1e-4) → preserved scale survives boundaries.

ISSUE B — pos_max_ema growth unbounded (train-phase fat-tail spike)
====================================================================
Pre-existing fat-tail behavior: at alpha-rl-jnct8 step 3895 a single
account had pre_clamp scaled reward 724. The clamp's Wiener-α EMA
(α=0.4 floor) admitted 40% of the observation, jumping pos_max_ema
112 → 834 in 5 steps. clamp_win = MARGIN × pos_max_ema followed
magnitude up rather than bounding it; env.max captured the unclamped
reward (121 → 1306). Recovery via slow-decay over 600 steps, but
during the spike PPO gradients were mis-scaled.

FIX B — asymmetric per-step growth cap on pos_max_ema (1.5× max).
Same Schulman-bounded-step pattern as reward_scale's 2% per-step
movement clamp. Decreases unbounded (allows fast recovery from spike).
Bootstrap path unchanged (ema_prev=0 → ema_new=pos_max, no cap).
Math: 5-step max growth = 1.5^5 ≈ 7.6× vs prior uncapped 7.4× in
practice — similar steady-state, bounded transient.

Local validation (b=16, 800+200 fold-1):
- σ preserved across boundary (51.9 → 51.4 at eval[1])
- σ stays bounded in 23-57 range across full eval phase (no 60× explosion)
- scale=0.10 stable across boundary
- warmed_flag stays 0 at b=16 (cumulative_dones never crosses min_trades
  at this scale — flip behavior tested at cluster b=1024)

Spec: docs/superpowers/specs/2026-05-31-eval-boundary-addendum-reward-scale-and-train-spike.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 22:25:24 +02:00
jgrusewski
13d8ed76da docs: checkpoints + eval-diag spec + plans (v1 superseded by v2) 2026-05-31 16:57:43 +02:00
jgrusewski
6c4945fe16 docs(spec): v9 defensive eval-boundary calibration (completes adaptive principle)
Per the newly-saved memory pearl pearl_adaptive_carryover_discipline,
diagnoses Fix D's "intentionally preserve train Kelly + inventory EMAs
into eval" as the dominant cause of v8's eval-phase regression.

Design — 3 layers at the train→eval boundary:

  Layer 1: Reset every adaptive EMA (Kelly wr/avg-win/avg-loss/
  cumulative_dones, inventory β/variance, reward-clamp pos/neg/clip
  EMAs) to neutral sentinels. Lets the controllers re-bootstrap from
  eval-distribution observations rather than carrying poisoned train
  state.

  Layer 2: Defensive warmup window (500 steps) overrides risk-sizing
  controllers — Kelly safety_frac 0.5→0.25, IQN τ_min 0.10→0.30,
  entropy coef floor 0.01→0.05, PPO ε floor 0.05→0.10. Linear decay
  back to normal over additional 200 steps.

  Layer 3: 2× LR multiplier during warmup window. Network weights
  adapt fast to new-regime statistics; this is the slowest-adapting
  layer of the agent.

Adds 8 new ISV slots (686-693), bumps RL_SLOTS_END 686→694. New
small kernel rl_eval_warmup_decay applies overrides each step during
warmup. Pure additive design — no kernel logic changes outside the
new warmup-decay kernel.

Validation requires running ALL 3 folds (vs v8's single fold-1) per
pearl_single_window_oos_is_not_oos. Success criterion: mean eval pnl
across 3 folds > v8 mean.

No code change in this commit — design document only. Implementation
deferred until decision is made on whether to ship v9 or first
collect v8's fold-0 + fold-2 baseline for cleaner comparison.
2026-05-31 01:31:46 +02:00
jgrusewski
82572ff3bd docs(spec): C51 atom span math validation + empirical proof
Formal proof of why the current `atom_max = EWMA(WIN_bound)` design
trains successfully despite empirical violations of the "structural
minimum atom_max ≥ WIN/(1-γ)" claim made in 2026-05-30-c51-atom-
resolution-design-alternatives.md.

Key findings (proven by local smoke + cluster v8 trajectory):

1. The fixed-point bound `WIN/(1-γ)` is overstated as a structural
   constraint. Empirical: smoke step 999 atom_max = 4.5× the bound,
   system trains healthy (qpa=+0.969).

2. The actually-binding constraint is the dynamic bound:
   `atom_max ≥ max V_target_observed ≤ WIN + γ × V_max_observed`
   Self-consistent and satisfied with margin 23 at smoke step 999.

3. Both speculative alternatives (Fix F = mean_abs_pnl anchor,
   Fix F-v2 = V_target_observed anchor) have closed-form instability
   at γ(1+ε) ≥ 1 — for γ=0.99 only ε ≤ 0.01 stable, no resolution
   benefit over current design.

4. Current design is *conservative* (atom_max ~5× larger than V_target
   3σ requires) but *correct* — slow EWMA hysteresis absorbs WIN
   transients and keeps Q-V Bellman self-consistent.

Includes empirical trajectory data:
- Local smoke (b=128, HEAD d57bee054, 1000 steps): atom_max 1798→1468,
  qpa +0.65→+0.97, clip_rate 5-11%, dynamic bound satisfied with
  margin 23 at convergence.
- Cluster v8 (b=1024, alpha-rl-vxbpq @ 6d4a962e5): popart_σ collapses
  56→1.69 from step 100→16830, qpa stays >+0.93, pnl_cum $164M.

Saves pearl_atom_span_dynamic_vs_fixed_point for future sessions.

Diagnostic emit of V_target_max_observed proposed (§5) but deferred
— current signals (WIN + γ × atom_max) already provide the bound
indirectly via JSONL. Real-time V_target_max is a nice-to-have for
future calibration work, not required to validate the current design.
2026-05-31 01:13:47 +02:00
jgrusewski
d57bee0542 docs(spec): atom resolution design alternatives (post Fix F failure)
Documents why Fix F crashed at cluster scale (qpa: +0.898 → -0.976
between step 371 and step 500 in alpha-rl-qrgjr v7), the structural
Bellman self-consistency constraint atom_max >= WIN/(1-γ) that makes
the resolution trade-off fundamental, and the design alternatives
considered (non-uniform atoms, adaptive γ, two-headed Q). Recommends
shipping A+B+C+D+E only — the pre-Fix-F design implements the
structurally-correct atom span via WIN-tracking EWMA.

No code change in this commit — design document only. Empirical
result from v8 (alpha-rl-vxbpq, SHA 6d4a962e5) will determine whether
deeper atom-resolution work (Options 4.C or 4.E) is justified.
2026-05-31 00:10:05 +02:00
jgrusewski
6d4a962e5c Revert "fix(rl): decouple C51 atom span from reward-clamp ceiling"
This reverts commit 0fe825a8c5.
2026-05-31 00:01:19 +02:00
jgrusewski
0fe825a8c5 fix(rl): decouple C51 atom span from reward-clamp ceiling
Spec: docs/superpowers/specs/2026-05-30-c51-atom-span-decouple-from-clamp.md

Diagnosed in cluster v5 (alpha-rl-rjsjq SHA 7064c9269) at step 371:
c51_v_max ratcheted up 1 → 24 → 53 → 859 → 1938 while WIN clamp swung
4910 → 568 → 7540 → 44 (volatile from MARGIN saturation). The slow
EWMA tracking win_bound time-averaged the early spikes, locking
atom span at extreme-value magnitude. Δz reached ~184 per atom — Q
could no longer discriminate +$500 from +$5000 wins.

Root cause: atom-span target was anchored on `MARGIN × pos_max_ema`,
where pos_max is the EXTREME-VALUE statistic across b=1024 batch
elements. The clamp bound (rightly fat-tail) and the atom span (should
be typical-magnitude) were treating two distinct concerns as one.

Fix F: anchor atom span on `atom_margin × mean_abs_pnl_ema` — true
central-tendency signal — while leaving the reward clamp on
pos_max_ema. The clamp can still admit fat-tail wins ($42k → +245)
and V regression (scalar head) retains the magnitude for PPO advantage,
but Q's CATEGORICAL distributional support now sizes itself for the
trades where most learning happens.

New ISV slot 685 (RL_C51_ATOM_MARGIN_INDEX, bootstrap 10.0).
RL_SLOTS_END 685 → 686.

Validation (local b=128 1k smoke):
  metric          | pre-Fix-F      | with Fix F
  v_max final      | 1929           | 86.6        (22× tighter)
  Δz per atom      | 184            | 8.7         (resolution restored)
  qpa final        | +0.957         | +0.958      (Q-π alignment preserved)
  mean_active      | +$47k          | +$34k       (within smoke variance)

13/13 risk_stack_invariants + 20/20 trade_management_kernels +
integrated_trainer_smoke pass.

Tradeoff documented in spec: Q loses fat-tail categorical magnitude
(rewards beyond ±87 project to top/bottom atom), but V regression
preserves it. Acceptable for the lottery-ticket strategy where typical-
magnitude resolution matters more than fat-tail magnitude precision.
2026-05-30 23:52:43 +02:00
jgrusewski
285d42aa7b feat(rl): adaptive risk-management stack — 5 layers, all ISV-driven
Layered risk stack per spec docs/superpowers/specs/2026-05-30-adaptive-risk-management-design.md:

  Layer 1 (CMDP)        hard session-DD, cooldown, max-open, inventory limits
  Layer 2 (IQN τ)       risk-averse action selection adapts to session drawdown
  Layer 3 (Inventory)   Avellaneda-Stoikov penalty β scales with reward magnitude
                        and inventory variance
  Layer 4 (Kelly)       half-Kelly fraction sizing from observed win-rate +
                        R-multiple, warmup-gated until cumulative_dones >= 1000
  Layer D (Trail)       wire dead a7/a8 (TrailTighten/Loosen) via independent
                        ISV factors, replacing the symmetric reciprocal

Architecture: every threshold ISV-driven (22 new slots, RL_SLOTS_END 662→684).
Every adaptive bound follows the canonical Wiener-α blend with floor 0.4,
sentinel-zero bootstrap, and asymmetric Schulman where applicable.

Kernels:
  rl_cmdp_constraints_check        session pnl + cooldown + consec-loss tracking
  rl_iqn_action_tau_controller     τ = clamp(0.5 - 5·dd_frac, τ_min, 1.0)
  rl_inventory_beta_controller     β_target = 0.01·E|reward| / (2·σ_inventory)
  rl_kelly_fraction_controller     f = clamp(safety · (p·b - q)/b, 0, 1)
  rl_win_rate_ema_update           closed-trade win-rate EMA from rewards + dones
  rl_avg_win_loss_ema_update       separate avg-win and avg-loss EMAs
  rl_inventory_variance_update     Welford variance of net-position-per-batch

Integration:
- All 7 new cubins loaded in IntegratedTrainer + launched per step in spec order
  (CMDP after reward pipeline, before actions_to_market_targets reads override
  flags; Layer 2/3/4 controllers in rl_fused_controllers.cu).
- actions_to_market_targets.cu: Layer 1 hard overrides (DD-triggered → 0 lots;
  cooldown → 0 lots; max-open → block opening actions; inventory cap → block
  one-sided expansion) and Layer 4 Kelly fraction scaling on target lots.
- rl_fused_reward_pipeline.cu: Layer 3 inventory penalty term in reward shaping.
- rl_trail_mutate.cu: a7 multiplies trail by RL_TRAIL_TIGHTEN_FACTOR_INDEX,
  a8 by RL_TRAIL_LOOSEN_FACTOR_INDEX (was symmetric reciprocal — pearls
  pearl_dead_trail_stop_actions_a7_a8).

Validation:
- 12 GPU-oracle invariants pass (tests/risk_stack_invariants.rs):
  G1-G4 CMDP, G5-G7 IQN τ, G8-G9 inventory β, G10-G12 Kelly.
- 20/20 trade_management_kernels.rs tests pass (a7/a8 migrated).
- 5/5 controller_adaptive_floors.rs tests still pass.
- integrated_trainer_smoke passes (end-to-end pipeline launch).
- Local 1k smoke b=128: completes 1000/1000 steps, no NaN, controllers steady.
- compute-sanitizer memcheck (5 steps b=128): ERROR SUMMARY: 0 errors.

Plan: docs/superpowers/plans/2026-05-30-adaptive-risk-management-plan.md
2026-05-30 20:52:28 +02:00
jgrusewski
083a88f7c3 feat(rl): adaptive controller floors — 12 controllers, all signal-driven
Replaces hardcoded thresholds AND clamp bounds across 12 RL controllers
with observed-signal-driven ISV-slot bounds. Eliminates the architectural
failure mode that surfaced in walk-forward fold 0 (alpha-rl-m9cx5) and
fold 1 (alpha-rl-jgdh6): under Phase 4.5 advantage normalization, eight
controllers (PPO clip, target_tau, rollout_steps, entropy_coef, per_α,
gamma, reward_scale, q_distill_lambda) saturated at extrema within ~50
steps and stayed pinned for the rest of training — same pattern across
two different data slices, confirming the bug is structural rather than
data-size-dependent.

Mechanism: each controller's noise floor was hardcoded as a small
fraction of its target (`_NOISE_FLOOR_FRAC = 0.01f`), calibrated against
a pre-Phase-4.5 signal regime. Phase 4.5 normalization reduces operating
KL by ~50× — observed signal stays below the 1%-of-target floor, the
Schulman widen path fires continuously (asymmetric in the wrong
direction), and ε hits MAX 0.50 within ~50 training steps. ksll2's full-
data run (n_folds=1) happened to escape via a single above-band KL
observation that triggered tighten; both walk-forward folds (3 and 6
files) did not.

Per `feedback_adaptive_not_tuned`, `feedback_isv_for_adaptive_bounds`,
`pearl_controller_anchors_isv_driven`: every threshold now derives from
observed signal statistics (Welford online variance) rather than
constants calibrated against a prior signal regime. User explicitly
expanded scope mid-implementation: "if all clamps ISV bound should be
added to this spec and tasks" — applying the principle consistently
means hardcoded MIN/MAX clamp bounds count too, not just the saturating
noise floors. 12 controllers, single atomic commit per
`feedback_no_partial_refactor`.

Spec: docs/superpowers/specs/2026-05-30-adaptive-controller-floor-design.md
Plan: docs/superpowers/plans/2026-05-30-adaptive-controller-floor-plan.md

# New kernel
- rl_signal_variance_update.cu (80 LOC) — Welford online variance.
  Single-thread single-block. Sentinel-zero skip per pearl. Per-controller
  Welford triple (count, mean, M²) drives every adaptive noise floor.

# Per-controller refactors (12 .cu files)
- ppo_clip + target_tau + rollout_steps + entropy_coef + per_α —
  adaptive noise floor = max(target × 0.5, sqrt(observed_var) × 2);
  asymmetric Schulman (tighten on single observation, widen requires 3
  consecutive below-band).
- gamma (Special G) — hardcoded GAMMA_MIN = 0.995 → adaptive via
  Welford MEAN of trade duration. Per spec Q6 resolution: the Welford
  mean's natural N-smoothing lag breaks the gamma↔trade_duration
  feedback loop without explicit step-period gating. 100-observation
  warmup falls back to EMA before Welford has enough samples.
- reward_scale (Special R) — asymmetric DECREASE rate cap (5% per
  step) on both bootstrap-replace and Wiener-blend paths; bootstrap-
  fraction floor (10% of bootstrap) until 100 trades close.
- q_distill_lambda (Special Q) — hardcoded MIN_LAMBDA = 0.05 → adaptive
  via Welford on q_distill_kl_ema: max(0.001, std × 0.05).
- v_blend_alpha (Phase 4.4) — 5 hardcoded constants → 5 ISV slots
  (DEAD_SIGNAL_FLOOR, TARGET_TRACK_RATIO, SCHULMAN_STEP, EMA_ALPHA,
  BOOTSTRAP_ALPHA) + adaptive dead-signal floor from V_scalar magnitude
  variance.
- ppo_ratio_clamp — adaptive MIN/MAX from observed log-ratio variance.
  Architectural 2.0 absolute floor preserved (don't degenerate to
  vanilla policy gradient).
- reward_clamp — V_BOUND_FLOOR, V_BOUND_EWMA_ALPHA, MIN_WIN, MIN_RATIO,
  MAX_RATIO, MIN/MAX_MARGIN, MARGIN_TOLERANCE/ADJUST_RATE,
  CLIP_RATE_EMA_ALPHA → 10 ISV slots.
- gate_threshold — hardcoded alpha = 0.01 → ISV slot 638.
- Clamp-bound expansion: EPS_MIN/MAX, TAU_MIN, ROLLOUT_MIN/MAX,
  COEF_MIN/MAX, PER_ALPHA_MIN/MAX, GAMMA_MAX, MAX_LAMBDA, KL_TOLERANCE,
  LAMBDA_RAMP_RATE, LAMBDA_DECAY_RATE → all ISV.
- WIENER_ALPHA_FLOOR shared across 9 controllers → single ISV slot 659.

# Trainer integration (integrated.rs)
- rl_signal_variance_update kernel loaded + helper method
  `launch_rl_signal_variance_update`.
- Per-step Welford launches for 9 controller inputs, placed between
  EMA producers and rl_fused_controllers in the per-step pipeline.
- Input-slot lookup array for rl_fused_controllers updated: rollout_steps
  now consumes RL_ADV_VAR_PRE_NORM_INDEX (emitted by
  rl_advantage_normalize before in-place normalize) instead of the
  post-norm advantage_var_ratio (definitionally ~0 under Phase 4.5).
- ~30 new ISV bootstrap entries in with_controllers_bootstrapped.

# ISV slot allocation (isv_slots.rs)
- 72 new slots, RL_SLOTS_END 588 → 660. +288 bytes mapped-pinned.
- 9 Welford variance triples + 5 asymmetric Schulman counters +
  3 new input signals (var_pre_norm, gamma_min_adaptive, reward_mag) +
  v_blend (5 + 3 Welford) + ppo_ratio_clamp (1 + 3 Welford) +
  reward_clamp (5) + gate_threshold (1) + q_distill (1) + 20 clamp bounds +
  shared wiener floor.

# Bootstrap-clamp consistency fix (caught by Task 16 testing)
The original draft bootstrapped RL_EPS_BOOTSTRAP at 0.01 (= KL target),
but EPS_MIN was 0.05 — bootstrap value below clamp range. First post-
bootstrap step always snapped ε to MIN regardless of signal direction
("snap to MIN" behavior the unit tests surfaced). Fixed by bootstrapping
ε at MIN (0.05) so asymmetric Schulman operates from a valid state.

# Validation
- cargo build --release: clean
- cargo build --tests: clean
- 3 GPU Welford kernel tests (G1 constant→0var, G2 sequence→known var,
  G3 sentinel skip): all pass
- 5 GPU adaptive-floor invariant tests (G3 PPO clip holds at bootstrap
  when signal below floor, G4 tightens on single above-band, G5 widens
  only after 3 consecutive below-band, G6 post-warmup uses Welford mean,
  G6 pre-warmup uses EMA): all pass
- integrated_trainer_smoke (full GPU pipeline): passes
- 1k local smoke at b=128: 1000/1000 steps, completed_clean, no NaN,
  controllers genuinely adapting (γ 0.90→0.974, per_α 0.40→0.52,
  q_distill_λ 0.05→0.21, ε held at 0.05 = MIN per Phase 4.5 small-KL
  regime — was previously stuck at MAX 0.50 throughout fold 0/1)
- compute-sanitizer memcheck b=128 5 steps: 0 errors

Cluster validation (G7-G9) submitted as follow-up runs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 15:41:51 +02:00
jgrusewski
dac9cfef5c spec: bypass cudarc for hot path — raw CUDA driver API + mega-kernel fusion
cudarc adds 18.8ms/step overhead at b=256 (GPU kernels take 0.16ms).
Phase 1: raw cuLaunchKernel wrapper. Phase 2: pre-extracted raw ptrs.
Phase 3: fused mega-kernels (20 launches → 3).
Target: 1ms/step → 500+ sps.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 09:39:17 +02:00
jgrusewski
e0ea71e90e spec+plan: DQN concepts adoption — PopArt, spectral norm, outcome head, enrichment E1-E8
7 tasks: PopArt normalization, spectral norm+decoupling, K=3 outcome
aux head, Q-bias correction, per-branch LR, curriculum weights,
adversarial regime injection.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 08:59:51 +02:00
jgrusewski
be3d5ceeae plan: multi-stream + per-push rewrite — 5 tasks
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 01:32:56 +02:00
jgrusewski
63416f7c12 spec: multi-stream pipeline + per-push rewrite — target 100 sps at b=256
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 01:30:29 +02:00
jgrusewski
33e14155fe plan: bidirectional HER — 5 tasks (struct, kernels, wiring, diag, tests)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:49:42 +02:00
jgrusewski
6f973b3a3f spec: bidirectional HER — backward peak + forward continuation
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:37:30 +02:00
jgrusewski
c2825a7928 spec: GPU-native Hindsight Experience Replay for wave-exit timing
Backward-looking HER: on trade close, compute peak unrealized PnL
during the trade's lifetime. If peak >> actual, inject synthetic
transition with peak_pnl and boosted priority. Teaches the agent
optimal wave-exit timing 10× faster than sparse reward alone.

Two kernels: rl_hindsight_track (per-step mid accumulation) and
rl_hindsight_inject (synthetic push on done with coordination).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:35:18 +02:00
jgrusewski
766adbc718 plan: GPU-resident PER + async diag — 7 tasks, greenfield
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:04:03 +02:00
jgrusewski
3f61d18735 spec: GPU-resident PER + async non-blocking diag streaming
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 00:00:00 +02:00
jgrusewski
4bed8f2dbf refactor(rl): pre-allocate 56 replay-step gradient buffers for Graph C
Move all step_synthetic/dqn_replay_step alloc_zeros to persistent
trainer fields (ss_* prefix). Enables CUDA Graph capture of the replay
training step — all device pointers are now stable across steps.

Introduces reduce_axis0_free() to resolve borrow-checker E0502 when
both source (per-batch scratch) and destination (reduced grad) are
self fields passed to the same function.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 23:00:40 +02:00
jgrusewski
3be9996689 spec: update CUDA graph spec with all session findings and implementation plan
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 21:41:10 +02:00
jgrusewski
6308be794e spec: CUDA Graph capture for RL step pipeline (2-3× throughput)
Captures 50+ kernel launches as 3 CUDA Graphs (pre-fill, post-fill,
replay-step) for single-launch replay. Eliminates ~300μs/step of
CPU launch overhead at b=16.

Key challenges: device-resident step counter (no scalar arg changes
in graph), lobsim fill breaks the graph (split into pre/post),
ISV write ordering. Estimated 1.5-3× throughput improvement.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 21:16:31 +02:00
jgrusewski
924448b55e spec: add mandatory constraints section — GPU-only, ISV-driven, TDD
Enumerates all project rules that apply to the Q-learning
improvements: CPU read-only, no atomicAdd, mapped-pinned only,
ISV-driven params, first-observation bootstrap, bilateral clamp,
raw reward in replay, no stubs/TODO/feature flags, GPU oracle
tests, local smoke before cluster, surfer philosophy constraints.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 20:09:14 +02:00
jgrusewski
6192bd4eb6 spec: Q-learning improvements — n-step + IQN ensemble + noisy nets
Three-phase plan to push Q convergence past profitability:
1. N-step returns (n=10): 10× more direct reward signal
2. IQN complementary head alongside C51: ensemble action selection
3. Noisy linear layers: state-dependent exploration

C51 stays for the confidence gate's distributional LCB. IQN adds
flexible quantile estimation. Ensemble combines both for action
selection. All ISV-driven.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 20:07:10 +02:00
jgrusewski
40855bfd62 docs(sp20): trader-grade trade management spec + audit infrastructure
Adds SP20 — full production trader-management system in one
greenfield commit (3-4 weeks of implementation work to follow):

  * Tier 0: multi-resolution time-scaled market features (3 horizons)
  * Tier 1: trade-arc awareness (4 features per batch)
  * Tier 2: per-unit trail-stop (entry + trail + stop per unit)
  * Tier 3: pyramiding + partial profit-taking (HalfFlat actions,
            N_ACTIONS=9→11)
  * Tier 4: Forward-Return-Distribution head + confidence gate +
            per-batch anti-martingale sizing + position heat cap +
            vol-adjusted defaults

Spec went through critical-review pass (v1→v2→v3):
  * v1: 3 tiers, side-channel features, single-gate acceptance
  * v2: 5 tiers added partial-flat + anti-mart + multi-res + checklist
  * v3: foundational fixes for 4 CRIT + 6 SIG + 6 MIN findings
        (per-unit pyramid state, encoder-input injection vs side-channel,
         FRD head replaces survivor-biased checklist, override stack
         ordering, per-batch anti-mart, real-time multi-res scales,
         P-1 ceiling falsification gate, multi-tier acceptance)

§0 Foundational Principles (NEW, non-negotiable):
  * §0.1 every numerical constant ISV-resident (no hardcoded #defines
         in new kernels; structural-dim exception only)
  * §0.2 every kernel/slot/head/action fully wired in same commit
  * §0.3 diagnostics baked in at birth (every observable in JSONL)
  * §0.4 per-phase ship-gate: all three audits must pass

Audit infrastructure shipped with the spec:
  * scripts/audit-isv.sh      — greps new .cu for hardcoded #defines
  * scripts/audit-wiring.sh   — verifies kernels/slots/heads/actions
                                have producer + consumer in code
  * scripts/audit-diag.sh     — runs local 100-step smoke, validates
                                manifest-listed jq paths present in JSONL
  * scripts/audit-manifest/   — per-phase append manifests (kernels,
                                slots, heads, actions, diag-fields)

Naming discipline: audit scripts and manifest are SP-agnostic (no
`sp20-` prefix) per new pearl `feedback_no_sp_or_version_prefixes_in_file_names`
— they'll serve future SPs too. SP numbers belong only in
docs/superpowers/{specs,plans}/ filenames.

Audit scripts dogfooded — already caught two real violations on
existing code that the formal review missed:
  * audit-isv: KL_EMA_ALPHA=0.05f hardcoded in rl_q_pi_distill_grad.cu
  * audit-wiring: TrailTighten action (a7) has no handler in any
                  kernel (per pearl_dead_trail_stop_actions_a7_a8)

These violations are SP20 P5/P10 fix targets.

User decision recorded in spec §3 P-1: ceiling-falsification phase
intentionally skipped — SP20 is the architectural launchpad for
the broader trader system regardless of whether current arch could
be pushed further at 1M steps. P-1 may be revisited as standalone
work after SP20 ships.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 16:28:39 +02:00
jgrusewski
0efdee4b0c docs(plans): rebuild plan v2 — strict memory + GPU-oracle gates
Audited the v1 plan against the full project memory catalog. Found
several rule violations and gaps:

A3 (was: 'host memcpy_htod canonical defaults to ISV[400..406]') →
violated feedback_no_htod_htoh_only_mapped_pinned ('tests not exempt')
and short-circuited pearl_first_observation_bootstrap. Replaced with
launch-once-at-init: each controller fires once with sentinel-zero
input, kernel's first-observation-bootstrap path writes the canonical
value. No host write to ISV. Canonical pattern across the codebase.

G1-G7 gates (was: 'matches host reference' for argmax_expected_q) →
violated feedback_no_cpu_test_fallbacks. Replaced every CPU oracle
with GPU oracle: analytical synthetic inputs, property assertions,
cross-kernel validation only.

Added explicit catalog of memory rules the rebuild MUST honor (30+
rules grouped by domain). Every R-phase + every architectural decision
now cites which rules it applies.

Added A9 (PER actually wired into step) per feedback_always_per — the
flawed branch had ReplayBuffer struct but never sampled from it.

Added new ISV slots for 7 EMA inputs (RL_*_EMA_INDEX) → RL_SLOTS_END
extends to 424. The controllers' inputs live on device, not host.

Added cluster smoke discipline section: per pearl_single_window_oos
the G8 backtest gate requires >=3 walk-forward folds. Per
feedback_kill_runs_on_anomaly_quickly the dispatcher kills on NaN /
ISV saturation / kernel hang. Per pearl_q_spread misaligned, the
dispatcher MUST NOT kill on Q_SPREAD. Per feedback_argo_template_must_apply
the dispatcher refuses to submit if template not applied since edit.
Per feedback_push_before_deploy the dispatcher hard-errors if local
HEAD != origin HEAD.

Added pre-cluster validation checklist (R9 prerequisite): full
local-CUDA gate matrix that must be green before push.

Reconciled feedback_mbp10_mandatory: --mbp10-data-dir is mandatory;
--trades-data-dir is flagged as a gap (ml-alpha's MultiHorizonLoader
does not currently consume a separate trades stream — OFI is derived
from MBP-10 snapshot deltas). Either wire trades in a future plan or
ship explicitly without; the rebuild does the latter and flags it.

Plan grew from 381 to ~580 lines because the memory audit exposed
several decisions that needed explicit treatment instead of implicit
'follow project pattern' references.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 09:34:07 +02:00
jgrusewski
e4c3cc60d2 docs(plans): integrated RL trainer GPU-pure rebuild
Replaces the flawed Phase F + G arc preserved on branch
`ml-alpha-phase-f-g-flawed` (commits 99a125cdb..b3808a5ac). The prior
attempt shipped a trainer with multiple production-blocking defects:

1. ISV[400..406] uninitialised → kernels read γ=0, ε=0, entropy=0
2. rl_reward_scale_controller drifted to 1e3 on no-trade steps
3. 6 controllers exist as .cu but never launched
4. Target net never soft-updated (τ has no consumer)
5. step_with_lobsim violated feedback_cpu_is_read_only with host
   Thompson sampling + EMA tracking + advantage/return loops
6. "toy" framing leaked into production (alpha_rl_train.rs shipped
   with next_snapshots=snapshots — the F.4 next-state code path was
   a no-op until that one issue got caught mid-review)
7. No NaN abort in production CLI

The convergence-gate fixtures (dqn_toy/ppo_toy → renamed
dqn_reward_signal/ppo_reward_signal on the flawed branch) hid every
defect because MockLobEnv is state-invariant with horizon=1.

The rebuild is GPU-pure: kernel-driven action sampling, kernel-driven
EMA tracking, kernel-driven advantage/return, ISV bootstrap at trainer
construction, all 7 controllers wired with device-resident EMA inputs,
target-net soft update consumer, NaN abort, no LobEnv trait (drives
LobSimCuda via the existing decision-policy kernel pattern).

Sequenced as R1..R9 with falsifiability gates G1..G7 that exercise
the specific failure modes the convergence-gate fixtures couldn't
catch. Calendar ~8.5 dev days.

Also adds memory pearl
feedback_extending_existing_code_audits_for_existing_violations
capturing the lesson: extending pre-existing CPU/orphan-controller
violations is how this happened.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 09:25:03 +02:00
jgrusewski
57de1a8b4e docs(plans): integrated RL trainer (DQN + PPO + BCE + aux) plan
Single integrated trainer where 5 loss heads (BCE direction, C51
distributional Q, categorical π, scalar V, aux prof+size) sit on a
shared Mamba2+CfC encoder. Joint training with adaptive loss-balance
λ weights via the existing pearl_loss_balance_controller infrastructure.
Discrete 9-action grid shared between Q and π heads; reward = per-trade
realized PnL from LobSimCuda.

Designed in response to lob-backtest-sweep-jpdhg result (PF=0.24,
sharpe=-9.72, mono-anti-cal in conviction→PnL): the encoder learns
directional AUC but never sees PnL-aware gradient because
stop_grad_aux_to_encoder blocks the aux head's gradient. RL training
fixes this by making the encoder optimize per-trade realized PnL via
Q-head Bellman + π-head clipped surrogate.

Every hyperparameter (γ, τ, PPO clip ε, entropy coef, rollout steps,
PER α, reward scale, loss-balance λs) is ISV-driven per
pearl_controller_anchors_isv_driven and feedback_isv_for_adaptive_bounds.
12 new ISV slots (400-411) + 12 reused existing slots + 8 new controller
kernels following the Wiener-α + first-observation-bootstrap pattern.

10 phases (A-H, ~3 weeks), falsifiable gate G8 = profit_factor > 1.0
on 2M-event backtest sweep.
2026-05-22 22:07:45 +02:00
jgrusewski
458d678e9f docs(plans): multi-resolution input architecture migration plan
Greenfield Phase 1 plan addressing temporal receptive field mismatch
(auc_h1000=0.576 plateau): replace seq_len=32 raw-tick input with
3-scale aggregation [(1,10), (30,10), (100,12)] covering 1510 ticks.

10 tasks total, executed via subagent-driven development. Falsifiable
gate: auc_h1000 >= 0.65 on clean front-month data.
2026-05-22 20:48:05 +02:00
jgrusewski
8d72c14a8b fix(ml): migrate test uploads from clone_htod to mapped-pinned
Per feedback_no_htod_htoh_only_mapped_pinned: mapped-pinned only for
CPU↔GPU; tests not exempt. Previous commit ab64c0412 was a shallow
deprecated-API swap (memcpy_stod -> clone_htod) — both still HTOD
copies. This commit replaces all 5 uploads and the 1 readback in
test_eval_action_select_thompson_picks_proportionally with the
production mapped-pinned + memcpy_dtod_async pattern (mirrors lines
433-448 of the same file).

Closure 'upload_pinned' DRYs the 5 upload sites; readback uses
MappedI32Buffer + DtoD into staging.host_slice_mut().to_vec().
Zero HTOD/HTOH copies remain in this file. Documented in
dqn-wire-up-audit.md.
2026-05-22 20:39:32 +02:00
jgrusewski
ab64c0412b chore(ml): migrate deprecated cudarc memcpy_* calls
cudarc 0.19 deprecated memcpy_stod (use clone_htod) and memcpy_dtov
(use clone_dtoh). Six call sites in cuda_pipeline/mod.rs migrated.
Pre-existing warnings surfaced now because the recent file-level
allow(unsafe_code) on this file no longer per-line-suppresses the
deprecated lint.

Per feedback_no_legacy_aliases: rename call sites directly, no
deprecated wrappers. Documented in dqn-wire-up-audit.md.
2026-05-22 20:34:59 +02:00
jgrusewski
cc40780b80 chore(ml): file-level allow(unsafe_code) on 12 CUDA-launch files
CUDA kernel launches via cudarc::launch_builder and MappedF32::new
(cuMemHostAlloc DEVICEMAP FFI) inherently require unsafe blocks. The
workspace-wide '-W unsafe-code' lint produced ~80 warnings across these
files, all structurally identical. Match the established pattern from
ml-backtesting/src/harness.rs and ml-backtesting/src/sim/mod.rs: single
file-level allow with a comment explaining the rationale.

Files: alpha_dqn_h600_smoke, alpha_baseline, cublaslt_debug,
gpu_walk_forward, cuda_pipeline/mod, hyperopt adapters (mamba2, ppo),
DQN smoke_tests (helpers, td_propagation), trainers/ppo, and two
ml/tests files. Documented in dqn-wire-up-audit.md.
2026-05-22 20:13:09 +02:00
jgrusewski
78a9e08358 feat(loader): InstrumentFilter::FrontMonth for cross-quarter ES.FUT data
Replaces Option<u32> instrument_id_filter with InstrumentFilter enum {All,
Id(u32), FrontMonth}. FrontMonth runs a two-pass detect over the DBN
stream: pass 1 counts instrument_ids and collects SymbolMapping records,
picks the dominant id, validates it resolves to an ES contract via regex
ES[FGHJKMNQUVXZ]\d{1,2}; pass 2 streams the filtered records.

Motivated by alpha-perception-k54wd: a single-id filter on parent-symbol
ES.FUT data caught Q1 2024 (kept=73M) but kept=0 for Q2-Q9 because ES
front-month rolls quarterly (ESH4 -> ESM4 -> ESU4 -> ESZ4 ...). FrontMonth
self-tunes across the rolls without needing a per-file id table.

Sidecar keys distinguish modes: mbp10 / mbp10_instr<id> / mbp10_front_month.
CLI flag renamed --instrument-id -> --instrument-mode {all,id=N,front-month}
with matching parameter rename in argo-alpha-perception.sh + template.
2026-05-22 17:38:41 +02:00
jgrusewski
783297e002 feat(loader): instrument_id filter + outdated test fix + sp18 fingerprint 2026-05-22 15:56:31 +02:00
jgrusewski
c535be046f baseline(per-horizon-cfc): 3-seed h6000 baseline JSON skeleton + retrieval gap
Per spec §4.1 + Q-3-seed-baseline. Both baseline Argo workflows
succeeded:
- alpha-perception-baseline-seed16963 (39min)
- alpha-perception-baseline-seed16964 (26min)

Precise AUC values deferred: train pod stdout is in MinIO archive
(xl.meta format, needs mc auth). The on-disk alpha_train_summary.json
on /feature-cache/alpha-perception-runs/f22f3f948/ corresponds to
seed 16963 (last to finish). Task 15 implementer retrieves via debug
pod mount of feature-cache PVC.

Known data point: seed 16962 = 0.7454 best_auc_h6000.

The Smoke 1 invariant gate threshold is signal-derived per
pearl_tests_must_prove_not_lock_observations — formulas captured in
the JSON for Task 15 to apply once values are retrieved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 16:10:48 +02:00
jgrusewski
ed34d356a8 docs(per-horizon-cfc): kickoff — spec + plan + historical record
Spec: docs/superpowers/specs/2026-05-21-per-horizon-cfc-inference-design.md
Plan: docs/superpowers/plans/2026-05-21-per-horizon-cfc-inference-plan.md

Spec went through 2 critical-review passes (32 total findings, all resolved).
Bucketing source: CfC.tau (per-channel, trained, log-uniform init at HIDDEN_DIM=128).
Atomic refactor reverts MTER scaffolding. 5 ISV-driven controllers. All-on-device
transition (no bulk DtoH). Single fused per-branch kernel. Compact ragged heads_w_skip.
Validated via 3 Argo smokes (training stability, CRT.diag inference differentiation,
fxt-backtest end-to-end).

Also committing historical record: superseded MTER spec/plan, intervention A
plan, ISV λ controller spec/plan, GPU log ring spec/plan. Per spec §5.3 these
stay as audit trail; not in build path.

Plan has 18 tasks, full TDD with bite-sized steps, ready for
subagent-driven-development execution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 15:05:28 +02:00
jgrusewski
faa9a73c10 spec(crt-train): output smoothness retraining intervention (intervention A)
Per project_crt_diag_findings.md — CRT.diag + diag.2 empirically
falsified all three hypotheses about why the model's per-event output
doesn't track horizon-specific dynamics. Labels ARE differentiated per
horizon, AUC=0.66 IS per-event measured, but per-event predictions
behave identically across all 5 horizons (2.5-event mean run length
raw, 3.3-event after aggressive Wiener-α EMA smoothing). h6000
predictions should change every thousands of events, not every 3.3.

Diagnosis: training dynamics issue. The BCE loss provides no signal
pushing toward horizon-coherent predictions. Adjacent-event labels at
horizon K share K-1/K of the forward window so SHOULD produce similar
predictions, but the loss doesn't require this.

Intervention A (this spec, minimum-scope): output smoothness
regularizer
  L_smooth[h] = λ[h] × mean_t (p[h](t) - p[h](t-1))²

With horizon-weighted λ (stronger for h6000 than h30): forces h6000
predictions to change slowly while leaving h30 responsive.

Implementation surface:
  - multi_horizon_heads.cu backward: extend grad_probs with smoothness
    gradient
  - perception.rs trainer step: prev_probs_d buffer, per-horizon (p[t]
    - p[t-1])² accumulation
  - heads.rs: SMOOTHNESS_LAMBDA constant per horizon
  - alpha_train.rs: --smoothness-base-lambda CLI flag

Validation gate (CRT.train Gate):
  MUST: per-horizon AUC ≥ baseline - 0.02 (no signal destruction)
  WIN: h6000 mean_run_len ≥ 100 events; h6000/h30 ratio ≥ 10× (horizons
       actually differentiated post-training)
  STRETCH: CRT.1 controller smoke shows mean PnL CORRELATED with
       conviction (vs anti-correlated in lnfwd)

Future work if intervention A doesn't pass WIN:
  B: horizon-conditional output structure (architecture change)
  C: curriculum on horizon (multi-day training)
  D: different label generation (majority-vote vs single-point)

Status: Design — awaiting user review before plan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 22:59:49 +02:00
jgrusewski
e27fc90b9b arch(crt-1): open_trade_state 24→64 byte expansion — atomic refactor (C1.1)
Per spec §7 (v3 architecture reset, promoted from v2 Phase B into CRT.1).
Single-cache-line layout. Every reader and writer of open_trade_state
migrates in this commit per feedback_no_partial_refactor.

New 64-byte layout (existing 24-byte fields preserved at original offsets):
  Offset  Size  Field
    0     8     entry_ts_ns                              (u64)
    8     4     entry_px_x100                            (i32)
   12     4     entry_size                               (i32)
   16     4     realized_at_open                         (f32)
   20     4     conviction_at_entry                      (f32)  ← NEW
   24     4     conviction_ema                           (f32)  ← NEW
   28    16     conviction_per_horizon_ema [4 × f32]    ← NEW
   44     4     pnl_adjusted_conviction_ema              (f32)  ← NEW
   48     4     peak_unrealized_pnl                      (f32)  ← NEW
   52     4     degradation_consecutive_events           (u32)  ← NEW
   56     4     disagreement_consecutive_events          (u32)  ← NEW
   60     1     horizon_idx_dominant_at_entry            (u8)   ← NEW
   61     3     pad

The 24-byte prefix is unchanged so downstream readers (stop_check_isv in
decision_policy.cu, max_hold event-rate check in resting_orders.cu) need
only update the stride constant. The new fields at offsets 20..63 are
populated by subsequent CRT.1 tasks (C1.2 multi-horizon conviction, C1.4
composite exit signal).

Open branch in pnl_track.cu zeros the new fields implicitly because
alloc_zeros on the device slot zero-initialises everything; subsequent
writes only touch the 0-23 byte range (existing fields). Close branch
reset-loop iterates OPEN_TRADE_STATE_BYTES which now zeros all 64.

Files changed:
  crates/ml-backtesting/src/lob/mod.rs  — pub const OPEN_TRADE_STATE_BYTES: usize = 64
  crates/ml-backtesting/cuda/pnl_track.cu  — #define OPEN_TRADE_STATE_BYTES 64
  crates/ml-backtesting/cuda/resting_orders.cu — comment + stride literal 24→64
  crates/ml-backtesting/tests/stop_controller.rs — open_trade_state_64_byte_layout test

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 20:25:05 +02:00
jgrusewski
7ae0a8d461 plan(crt-1): unified inventory controller implementation plan
Per spec v3 (commit 9ad76c4df). Supersedes the v2 Phase A plan
(2026-05-20-crt-phase-a-continuous-controller.md) which is now an
artifact — its load-bearing commits (A0.5 forward_step, A1
decision_stride deletion) are preserved; its A2 scalar conviction-EMA
work is REPLACED by the multi-horizon §4.4 formula here.

Six tasks (all under one Gate CRT.1, no inter-task gates):
  C1.1 open_trade_state 24→64 byte atomic refactor (spec §7)
  C1.2 multi-horizon ISV-weighted conviction (spec §4.4) replacing
       scalar EMA approach
  C1.3 no-trade band in seed_inflight (delta_floor config field)
  C1.4 composite exit_signal safety circuit-breaker (spec §4.3)
  C1.5 local compile + tests
  C1.6 cluster smoke + Gate CRT.1 validation

Gate CRT.1 acceptance = v2 §9 Gate 2 tiered MUST/WIN structure intact.

What ships in this plan:
  - Continuous evaluation (every event, via existing forward_step)
  - Multi-horizon ISV-weighted conviction (one unbounded factor =
    net_edge / (var + cost²); rest bounded; per pearl_one_unbounded_signal)
  - target_lots = direction × |conviction_ema| × envelope_max (no aggregate
    rescale; the v2 A2/A2.1 bug is structurally absent)
  - No-trade band: kernel skips seed when |target − effective| < delta_floor
  - open_trade_state 64-byte expansion for per-trade trajectory tracking
  - Composite exit_signal as safety circuit-breaker (primary exit is
    emergent target→0)

What stays out of scope (CRT.2 / CRT.3):
  - Adaptive max_lots / threshold / vol target (CRT.2)
  - Self-tuning percentile gate (CRT.2)
  - LoRA + EWC++ + shadow eval (CRT.3)

Status: ready for execution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 20:18:45 +02:00
jgrusewski
9ad76c4dfe spec(crt): v3 architecture reset — collapse Phase A+B into CRT.1 unified controller
Empirical trigger: Phase A as designed in v2 failed Gate 1 catastrophically
on both smoke vjmwc (commit 3d8f12deb) and lkrdf (commit fe2498769 with
amplification clamp). Both produced ~155k trades (62× baseline), 9100%
max-drawdown (175× baseline), and Sharpe -15.7. The clamp had effectively
zero impact on the outcome, confirming the bug is structural not formulaic.

Architectural finding: the v2 phase split (A: continuous control / B:
signal-driven position management) is artificial. They are the same
mechanism. A scalar EMA on max(|p-0.5|) cannot smooth direction jitter
between horizons; only the multi-horizon ISV-weighted conviction formula
(v2 §4.4, deferred to Phase B in v2) is structurally coherent.

User-validated mental model: signal vector at any moment IS a forward
prediction of the trade trajectory; optimal position = inventory implied
by current beliefs; trade actions are sparse because most events
produce only fractional adjustments to a stable optimum. Continuous
evaluation, discrete action.

v3 phase structure (replaces v2 A/B/C/D):
  CRT.1  Unified Inventory Controller (was A+B merged)
  CRT.2  Adaptive Risk Envelope (was Layer C, unchanged)
  CRT.3  Online Weight Adaptation (was Layer D, unchanged)

CRT.1 components:
  - forward_step every event (already shipped: A0.5)
  - decision_stride deleted (already shipped: A1)
  - Multi-horizon ISV-weighted conviction (§4.4 promoted from Phase B)
  - Wiener-α EMA on multi-horizon conviction (§4.2 promoted)
  - target_lots = direction × |conviction_ema| × envelope_max
  - No-trade band in seed_inflight: skip if |delta| < delta_floor (NEW)
  - open_trade_state 24→64 expansion (§7 promoted from Phase B)
  - Conviction-degradation exit emergent from target→0; composite-signal
    safety layer (§4.3) preserved as circuit-breaker

What survives from v2 commits on the branch:
  a0e81fbdf forward_step incremental SSM (A0.5)
  92f8b10ed forward_step_into eliminates GPU↔CPU round-trip
  045850e8f delete decision_stride field (A1)
  3d8f12deb buffer-level seed bit-identity test

What gets superseded in CRT.1 implementation:
  1d889d2de A2 scalar conviction-EMA with aggregate rescale — replaced
            by multi-horizon §4.4 formula
  fe2498769 A2.1 clamp on broken rescale — same reason

The three conviction-EMA device slots (conviction_ema_d et al.) STAY
in LobSimCuda — the EMA mechanism is reused on the multi-horizon
conviction scalar. The `final_size *= scale` rescale step is deleted.

Gate restructure:
  v2 Gate 1 (Layer A standalone) — REMOVED
  v2 Gate 2 (Layer B) — PROMOTED as Gate CRT.1
  v2 Gate 3/4 — unchanged as CRT.2/CRT.3

Status: Design v3 — awaiting plan rewrite before implementation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 20:15:28 +02:00
jgrusewski
2e87ed0daf memo(crt-a): forward_only cost investigation — Case 2 (stateless K-window)
forward_only requires the full K=seq_len window on every call and resets
Mamba2 h_s2 to zero each invocation — no cross-call state carry. Additionally,
the call is inside the stride=200 gate in harness.rs (spec §3.2 claim "already
every event" is incorrect as of HEAD). Moving to stride=1 without a forward_step
implementation would be a ~200x GPU cost increase. A0.5 must implement
forward_step with persistent h_s2, a dedicated K=1 CUDA graph, and session-reset
hook. Memo documents exact file paths, line numbers, required struct/method
changes, and open questions for the A0.5 implementer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 17:43:52 +02:00
jgrusewski
b508c38529 plan(crt-a): remove fallbacks — greenfields, resolve properly
Per user direction: no fallbacks, greenfields, kernel updates acceptable.

Changes:
  1. Task A0.5 ADDED — pre-planned task that fires if A0 investigation
     finds forward_only is stateless K-window (Case 2) or has periodic
     reset (Case 3). Refactors PerceptionTrainer to maintain persistent
     SSM state and expose forward_step(snapshot, b) that advances by 1
     event. Bit-identical to forward_only of equivalent window per a
     new golden test in incremental_forward.rs.

     This is NOT a fallback — it's a pre-planned task whose execution
     is determined by actual investigation outcome. Case 1 → A0.5 is
     a no-op. Case 2 or 3 → A0.5 fires in full. Either way the plan
     proceeds without user-approval pause.

  2. Task A2 reframed — Wiener-α conviction-EMA is a "load-bearing
     component of continuous control," not a "minimal Phase B subset."
     Without smoothing, event-rate trading is structurally incoherent.

  3. Task A2 test fallback REMOVED — instead of "if helpers don't
     exist, omit the unit test," the plan now says "add the helpers
     properly to the public API." Step 1 audits the LobSimCuda public
     API and adds missing accessors. Greenfields — accessors stay on
     the API permanently if testing needs them.

  4. Task A5 (conditional hyperactivity mitigation) DELETED. A2 is
     designed correctly the first time with Wiener-α floor at 0.4. If
     A4 cluster smoke shows hyperactivity, that's an A2 bug to fix
     properly, not a tuning knob to nudge.

  5. Scope contract updated — Phase A vs Phase B split is by code-path
     responsibility (continuous control infrastructure vs multi-horizon
     signal-driven policy), NOT by "shipping less to be safe."

  6. Notes for the Implementer — explicit "No fallbacks" section
     replaces the implicit-fallback language. STOP-and-notify-user for
     Case 2 deleted (A0.5 handles it inline).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 17:37:24 +02:00
jgrusewski
235961987d plan(crt-a): Phase A continuous controller implementation plan
Per docs/superpowers/specs/2026-05-20-continuous-reasoning-trader-design.md
(v2, commit 8ba8755b4). One plan per phase per writing-plans guidance —
Phase B/C/D plans written after each prior gate closes.

Phase A scope:
  A0 - Investigate forward_only cost model (read-only research,
       writes memo to docs/superpowers/memos/). STOP condition if
       trunk forward is stateless K-window per call (Case 2) —
       requires user approval to expand scope.
  A1 - Atomic delete of decision_stride (greenfields, no compat).
       Removes from SweepBase, ResolvedSimVariant, BatchedSimConfig,
       BacktestHarness, all YAMLs.
  A2 - Minimal Wiener-α conviction-EMA smoothing in decision_policy_*
       kernels. Three new device slots: conviction_ema_d,
       conviction_diff_var_ema_d, conviction_sample_var_ema_d. Floor
       at 0.4 per pearl_wiener_alpha_floor_for_nonstationary.
       First-observation bootstrap. Direction stays from raw alpha;
       magnitude/sizing uses smoothed value.
  A3 - Local compile + tests.
  A4 - Cluster smoke + Gate 1 validation against spec §3.6 acceptance.
       Memory entry on closure (green/red/yellow).
  A5 - Conditional hyperactivity mitigation (raise floor 0.4→0.6 OR
       add target-delta hysteresis). Fires only if Gate 1 reveals
       trade-count balloon.

Out of scope (deferred): full §4.4 multi-horizon conviction formula,
§4.3 conviction-degradation exit, §5 envelope, §6 LoRA adapter, §7
open_trade_state expansion — all Phase B/C/D.

Pearl conformance:
  - feedback_no_partial_refactor (atomic delete)
  - feedback_no_legacy_aliases (no compat shim)
  - feedback_push_before_deploy
  - pearl_wiener_optimal_adaptive_alpha
  - pearl_wiener_alpha_floor_for_nonstationary
  - pearl_first_observation_bootstrap
  - feedback_stop_on_anomaly (Gate 1 RED → diagnose, don't advance)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 17:33:16 +02:00
jgrusewski
8ba8755b48 spec(crt): v2 amendments from critical review — 12 issues + greenfields
Self-critical review of v1 (commit 0f3484325) found 12 issues. All
fixed inline. Spec marked Status: Design v2 with explicit changelog.

LOAD-BEARING FIXES
  1. §4.4 conviction cold-start: removed backwards "uniform fallback"
     branch that maintained trading authority when net_edge=0 across
     all horizons (exactly when model has no edge). Replaced with ε
     floor on net_edge_h. Single formula, smooth bootstrap, no regime
     switch.
  3. §4.3 exit logic: 5 OR conditions → 1 composite exit_signal scalar
     (max of degradation, disagreement, pnl_decay terms). SL/trail and
     8h circuit-breaker stay orthogonal as safety, not exit policy.
     Attribution per term recorded in §7 state for observability.
  6. §9 Gate 2: PF > 1.0 (2.5× improvement, unrealistic) → tiered
     MUST (no-regression) + WIN (real lift, four candidate criteria) +
     explicit STOP condition if MUST passes without WIN.
  7. §9.1.1: alpha-realization metric defined explicitly as
     realized_pnl / max(unrealized during signal-validity window).
     Was hand-waved in v1; now formulated.

TRACTABLE INLINE FIXES
  2. §7 open_trade_state: 128 → 64 bytes. Diagnostic-only fields
     (scale_up/down counts, max/min target reached) moved to a
     separate audit buffer (single source of truth keeps state lean).
  4. §5.1 envelope: multiplicative formula → additive bounded
     contributions. tanh(sharpe), min(dd/cap), clamp(vol). No single
     factor can crash envelope to zero. Floor (envelope_floor lots)
     and hard-cap (envelope_hard_cap) remain.
  5. §5.2 threshold: hand-waved "self-tune toward PF" → explicit
     3-arm bandit (lower, current, higher percentile) every K_eval
     closed trades, Wiener-α EMA target with floor, bounded
     [0.5, 0.95].
  8. §3.4 + §3.6: Layer A wall-time ≤ 5× → ≤ 2× (controller is
     light, 5× would indicate structural bug, not config tuning).
  9. §4.2: explicit Wiener-α formula for conviction EMA with floor
     at 0.4 per pearl_wiener_alpha_floor_for_nonstationary.
 10. §6.2.1: shadow eval defined explicitly with backtest-mode (last
     15% of fold) and live-mode (parallel-prediction-only) variants.
     Promotion criteria spelled out (PF, Sharpe, tail loss).
 11. §6.2: Layer D loss explicit (primary = value regression on
     realized PnL conditional on state; secondary = EWC++ regularizer
     toward base). Was three losses with no specified combination.
 12. §10.5: adapter regime over-fit risk added with sample-count
     down-weighting, cross-regime shadow eval, periodic adapter reset
     as hard backstop.

GREENFIELDS (per user direction)
  - §3.3: decision_stride field DELETED from YAML + Rust (not
    deprecated). No backwards-compat shim. Per
    feedback_no_legacy_aliases.
  - §7.1: open_trade_state 24→64 byte migration is atomic, no old
    layout shim.
  - §8: boundary matrix decision_stride row marked REMOVED, not
    DEPRECATED.

§13 pearl conformance: per-section pearl application (not bulk
citation). Added pearl_audit_unboundedness_for_implicit_asymmetry,
pearl_no_deferrals_for_complementary_fixes acknowledgement (layers
are sequenced amplifications, not parallel fixes), and the new
pearl_stop_checks_run_at_deadline_cadence from S2.

Status: Design v2 — awaiting user review before writing-plans.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 17:24:03 +02:00
jgrusewski
0f34843253 spec: continuous-reasoning trader architecture (4-layer rebuild)
Integrated spec for the continuous-reasoning trader rework. Replaces
the rule-based discrete policy with a 4-layer signal-driven architecture:

  Layer A: continuous control loop (every event, not every stride)
  Layer B: signal-driven position management (multi-horizon fusion,
           continuous sizing, conviction-degradation exit, open_trade_state
           expanded 24→128 bytes)
  Layer C: adaptive risk envelope (ISV-derived max_lots, threshold,
           target_annual_vol)
  Layer D: online weight adaptation (LoRA + EWC++, offline-batch
           per-session, shadow-eval gated)

Phased gates: A unlocks B; B is where alpha lives; C amplifies B;
D amplifies whatever's working. Each gate has explicit pass criteria.

Conviction definition: ISV-weighted multi-horizon agreement.
weight_h = max(pnl_ema_win - pnl_ema_loss, 0) / (var + cost^2).
Net edge x SNR per horizon, scale-normalised, cold-start uniform fallback.

Pearl conformance: 13 existing pearls referenced and respected
(ISV-driven anchors, first-observation bootstrap, Wiener-alpha floor,
blend-with-floor, z-score normalisation, one-unbounded multiplicand,
trade-level vol bootstrap, deadline cadence, single-source-of-truth,
atomic refactor, adaptive-not-tuned).

Hardcoded boundary preserved for hardware/exchange realities (latency,
cost, annualisation, instrument bounds). Everything else adaptive.

Status: design — awaiting user review before implementation plan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 17:11:46 +02:00