Commit Graph

190 Commits

Author SHA1 Message Date
jgrusewski
9e6637c065 docs(claude): foxhunt-aware agents & skills design — phase 1 (12 items)
Pattern-cluster auditors + workflow-skill hybrid that internalize accumulated
project wisdom (15 feedback_*, 30+ pearl_*, 12+ project_* memory files; 30 SP
specs) into foxhunt-namespaced agents and skills under .claude/agents/foxhunt/
and .claude/skills/foxhunt/.

Phase 1: 5 auditor agents (isv-discipline, gpu-contract, reward-controller,
code-hygiene, sp-critical-reviewer) + 5 workflow skills (sp-spec-writer,
isv-slot-scaffolder, pearl-distiller, argo-deploy-helper, smoke-pilot) +
2 maintenance skills (memory-curator, stale-worktree-cleaner). Hook integration
is warn-only PostToolUse via a single thin router; agents read memory but only
pearl-distiller and memory-curator may write into memory/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 22:19:28 +02:00
jgrusewski
235e838422 feat(sp20): Phase 1.4 (Path C) — kernel-arg refactor + aggregation kernel + wire-up
Atomic Phase 1.4 commit per `feedback_no_partial_refactor`. Wires the
SP20 fused-producer chain (Stats → Aggregate → EMAs → Controllers) into
GpuExperienceCollector's per-rollout-step path with one new aggregation
kernel + Phase 1.2 EMA kernel-signature refactor + production wire-up
+ tests + audit/spec/plan amendments, all in one commit.

## Path C decision rationale

The Phase 1.2 EMA kernel originally took 9 scalar value-args. The Phase
1.4 wire-up site (per-rollout-step in GpuExperienceCollector) needs to
feed per-env GPU-resident trade signals (trade_close_per_sample,
step_ret_per_sample, hold_at_exit_per_sample, packed factored
actions_out) into the kernel — forbidden via host sync
(`feedback_cpu_is_read_only`) and via memcpy_dtoh/htod
(`feedback_no_htod_htoh_only_mapped_pinned`). Path C refactors the EMA
kernel to take a device struct pointer (`SP20EmaInputs* ema_inputs`) +
adds an aggregation kernel that writes the struct on the GPU. Phase 1.2
had zero production consumers yet, so the sig change + Phase 1.4 wire-up
land atomically.

## What this commit contains

1. **EMA kernel signature refactor** (Phase 1.2 → Path C):
   `sp20_emas_compute_kernel.cu` swaps 9 scalar args for a single
   `const SP20EmaInputs* __restrict__ ema_inputs` device pointer.
   Math semantics bit-identical. Rust `EmaInputs` mirrored as
   `#[repr(C)]` byte-for-byte; new `pack_inputs_into_f32_view` helper
   for tests + collectors that fill the struct via a mapped-pinned
   f32-aliased buffer.

2. **New aggregation kernel** (`sp20_aggregate_inputs_kernel.cu`):
   Per-env arrays (sliced to current rollout step) + sp20_stats outputs
   (p50/std) + aux_dir_acc_reduce_kernel output [0] → SP20EmaInputs
   struct. Block tree-reduce (4 stripes × bdim × 4 bytes shmem); no
   atomicAdd. Aggregation rules per spec §4.5:
   - is_close = OR over envs
   - is_win = (≥ 0.5 of closed envs were wins)
   - trade_duration = round(mean(hold_at_exit) over closed envs)
   - action_is_hold = strict majority (count*2 > n_envs) HOLD
   - alpha = 0.0  (Phase 2 forward ref — reward kernel)
   - per_bar_hold_reward = 0.0  (Phase 3.2 forward ref — Hold-cost dual)
   - aux_logits_p50/std/dir_acc forwarded from upstream

3. **Production wire-up in GpuExperienceCollector**:
   4 kernel handles + 4 mapped-pinned buffers (struct fields, alloc,
   init); per-rollout-step launch sequence after env_step (step 5c):
   `Stats → Aggregate → EMAs → Controllers`. All on the same stream;
   stream-implicit producer→consumer ordering. Gated on
   `isv_signals_dev_ptr != 0 && trainer_params_ptr != 0` (matches the
   existing SP14-β EGF chain pattern).

4. **Fold-boundary reset**:
   3 new RegistryEntry records (sp20_ema_inputs_buf,
   sp20_emas_internal_buf, sp20_emas_obs_count_buf) + 13 new dispatch
   arms in training_loop.rs (10 SP20 ISV slot resets + 3 buffer resets).
   Closes the pre-existing `every_fold_and_soft_reset_entry_has_dispatch_arm`
   regression (was failing on fresh check after the SP20 ISV slot
   registrations landed in commit 4249ebc96).

5. **HEALTH_DIAG emit**:
   Per-epoch `HEALTH_DIAG[N]: sp20_isv [loss_cap=… alpha_ema=…
   wr_ema=… hold_cost_scale=… target_hold_pct=… hold_pct_ema=…
   hold_reward_ema=… n_step=… aux_conf_threshold=… aux_gate_temp=…]`
   right after the existing q_disagreement_diag emit.

6. **Tests** (5 test files, all green on RTX 3050 Ti sm_86):
   - sp20_emas_compute_test.rs (4 GPU oracle): updated to use the
     post-Path-C buffer-arg API (mapped-pinned f32-aliased struct).
   - sp20_aggregate_inputs_test.rs (NEW, 6 GPU oracle): aggregation
     rule coverage including the Phase 2 / Phase 3.2 placeholder
     contract.
   - sp20_phase1_4_wireup_test.rs (NEW, 2 GPU oracle): end-to-end
     4-kernel chain integration test.
   - sp20_stats_compute_test.rs (4 GPU oracle): unchanged, regression.
   - sp20_controllers_compute_test.rs (7 GPU oracle): unchanged,
     regression.
   - 18 lib unit tests across the SP20 launchers.

7. **Phase 2 / Phase 3.2 forward references**:
   The `alpha` (Phase 2) and `per_bar_hold_reward` (Phase 3.2) fields
   are emitted as 0.0 placeholders and documented at:
   - `sp20_aggregate_inputs_kernel.cu:46-52` (in-kernel docstring)
   - `sp20_aggregate_inputs.rs:46-49` (launcher docstring)
   - `dqn-wire-up-audit.md` "Phase 2 / Phase 3.2 forward references"
   These are NOT stubs — Phase 2 / 3.2 will replace the kernel's 0.0
   writes with real signals atomically with their respective producers.
   The original Task 2.3 (host-side aggregation) is **subsumed** by
   the GPU-side aggregation kernel.

## Hard rules

- `feedback_no_partial_refactor` — kernel sig change + aggregation
  kernel + production wire-up + tests + audit/spec/plan amendments
  in one atomic commit
- `feedback_no_atomicadd` — aggregation kernel uses block tree-reduce
- `feedback_no_cpu_compute_strict` — every aggregation lives on GPU
- `feedback_no_htod_htoh_only_mapped_pinned` — every Phase 1.4 buffer
  is mapped-pinned with `cuMemHostAlloc(DEVICEMAP)` reachable via
  `dev_ptr`; no memcpy_dtoh/htod
- `pearl_first_observation_bootstrap` — counter-based bootstrap
  preserved; placeholder writes (0.0) keep the ALPHA / HOLD_REWARD
  EMAs at 0.0 sentinel until Phase 2 / 3.2 wire real producers
- `pearl_no_host_branches_in_captured_graph` — every kernel is single-
  block; no host branches; safe to capture in the per-step CUDA Graph
- `pearl_tests_must_prove_not_lock_observations` — integration test
  asserts invariants (slots populate, bounds respected) rather than
  locked observed values

## Verification

- `cargo check -p ml --features cuda` clean
- 18 lib unit tests for SP20 launchers pass
- 23 GPU oracle tests across 5 test files pass on RTX 3050 Ti (sm_86)
- `every_fold_and_soft_reset_entry_has_dispatch_arm` regression test
  now passes (was failing pre-change)
- 14 baseline lib test failures unchanged (none introduced by this
  commit)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 20:49:07 +02:00
jgrusewski
4e21c38b85 fixup(sp20): Phase 1.1 K=3 → K=2 retarget (production aux head is K=2)
The Phase 1.1 sp20_stats_compute kernel (de922c6a4) assumed the SP14-C aux
head emits 3-class logits {short, hold, long} with baseline 1/3. This was
an error in the SP19+20 spec — production aux head emits K=2 logits
{down, up} per gpu_aux_heads.rs:61 (AUX_NEXT_BAR_K = 2, established by
SP13 B1.1a). Wiring K=2 production aux into the K=3 kernel = OOB reads +
corrupt stats.

Retargets the kernel + launcher + tests to K=2 atomically per
feedback_no_partial_refactor:

- Kernel: SP20_K_CLASSES 3→2; SP20_UNIFORM_K3 0.333…→SP20_UNIFORM_K2 0.5f;
  Pass A reads 2 logits (was 3); aux_conf range [0, 1/2] (was [0, 2/3]).
- Launcher: AUX_K_CLASSES 3→2; renamed unit test
  aux_k_classes_is_three → aux_k_classes_matches_production_aux_head.
- Tests: rewrote CPU oracle for K=2 input shape and 0.5 baseline; updated
  expected p50/std ranges; redesigned heterogeneous-distribution test to
  use ramped (not lockstep) clusters — K=2's saturated softmax in the hot
  half collapses every row to bin 255, triggering the
  pearl_sp4_histogram_warp_tile_undercount trap; ramped clusters distribute
  bin indices across each warp's 32 lanes so the histogram path matches
  the CPU oracle within bin_width tolerance.

Phase 1.2 (sp20_emas_compute) and Phase 1.3 (sp20_controllers_compute)
consume the scalar [p50, std] outputs and do NOT carry the K dimension.
Both regression suites verified passing unmodified:
- sp20_emas_compute_test: 4 GPU + 1 unit, all pass.
- sp20_controllers_compute_test: 7 GPU, all pass.

Verification (RTX 3050 Ti, sm_86):
- sp20_stats_compute_test: 4 GPU oracle + 4 launcher unit, all pass.
- cargo check -p ml --features cuda: clean (pre-existing warnings only).

Spec + plan amended at top with "AMENDED 2026-05-09" notes; audit doc
Phase 1.1 entry has a "K=2 fixup" subsection documenting the change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 19:46:17 +02:00
jgrusewski
9d9ca3e6e7 spec(sp19+20): apply Q1(b) — Hold-reward EMA for Q-scale comparability
Q1 design decision (b): add explicit hold_reward_ema to center the per-bar
Hold reward, so Q(Hold) and Q(trade) targets are scale-comparable. The
marginal Q(trade) > Q(Hold) preference now comes from data variance in
each state (high-aux states pull Q(Hold) more negative), not from
structural scale asymmetry that depends on cost_scale magnitude.

Q2 decision: keep 4-quadrant fixed (no ramping partials). Already in spec.

Changes:
- §4.2 Hold opp-cost: dual emission documented — R_per_bar_centered
  (= R_per_bar - hold_reward_ema) for Q-target/replay tuple,
  R_per_bar uncentered for hold_baseline_buffer (Component 1 baseline)
- §4.5 Kernel 1: hold_reward_ema added (per-step on Hold-state bars only)
- §5 data flow: per-bar reward path shows the centered/uncentered split
- ISV slots: 9 → 10 (HOLD_REWARD_EMA_INDEX added)
- §8 footprint: Component 2 LoC 70 → 90 (+20 for dual emission)
- Total LoC estimate: 1620
2026-05-09 17:40:00 +02:00
jgrusewski
defbd0abe1 spec(sp19+20): patch 7 review issues
P1 (must-fix bugs/gaps):
1. ASYM_RATIO_INDEX → LOSS_CAP_INDEX with explicit formula in §4.1 and §4.5
   (was double source-of-truth — formula in §4.1, ISV slot orphaned)
2. Replay buffer schema change (per-bar aux_conf) added to §8 implementation
   footprint (~50 LoC additional, was invisible in original spec)
3. hold_baseline_buffer size specified = LOOKAHEAD_HORIZON_MAX = 30 bars (§4.2)
4. "4-tier gate" typo → "5-tier gate" in §7

P2 (clarifications):
5. alpha_ema centering documented as load-bearing for cold-start learning (§4.1)
   — not just for Q-target stability. EV is slightly negative at WR=46% with
   uninformed SP19 label; advantage-style centering rescues cold-start.
6. Q-scale asymmetry between Hold (uncentered) and trade (alpha_ema centered)
   documented as INTENTIONAL design choice (§4.2) — produces marginal
   Q(trade) > Q(Hold) preference that counteracts the Q(Hold) attractor.
   Behavioral test sp20_pure_noise validates the no-signal Hold default still works.

P3 (tightening):
7. Behavioral test thresholds tightened (§4.6):
   - sp20_pure_trend: WR > 70% → > 90%, PF > 2.0 → > 3.0
   - sp20_pure_noise: Hold% > 80% → > 95%, trades < 50 → < 20
2026-05-09 17:35:36 +02:00
jgrusewski
730337375f spec(sp19+20): WR-first reward + multi-horizon label utilization
Combines SP19 (multi-horizon labels, already landed) + SP20 (WR-first
reward) into one spec per pearl_no_deferrals_for_complementary_fixes.

Goal: WR ≥ 55%, textbook PF ≥ 2.0, walk-forward stable, per-regime stable.

Six components, atomic ship per feedback_no_partial_refactor:
1. Reward kernel (event-driven, 4-quadrant, asymmetric clamp ramped from
   wr_ema, multi-horizon directional ground-truth check)
2. Hold opportunity-cost (per-bar, dual emission for real reward + Hold
   baseline buffer)
3. n-step credit distributor (uniform over trade duration, fixes SP18
   B-leg self-bootstrap bug at gpu_experience_collector.rs:4154)
4. Aux→Q confidence gate (sigmoid threshold, mean_a Q baseline avoids
   Hold-everywhere punishment)
5. 3 fused producer kernels (sp20_emas, sp20_controllers, sp20_stats)
   driving 9 ISV slots in [510..520)
6. 7 behavioral tests on RTX 3050 Ti, gating L40S deployment

~1,550 LoC total. Spec includes data flow, error handling philosophy,
4-tier test gate, success criteria with explicit failure modes.

Cross-references:
- pearl_event_driven_reward_density_alignment (design principle)
- pearl_audit_unboundedness_for_implicit_asymmetry (asymmetric clamp)
- pearl_separate_aux_trunk_when_shared_starves (aux gate leverages SP14-C)
- project_metric_pipeline_inflation_audit (WR honest, Sharpe honest, goal grounded)
- project_goal_wr_55_pf_2 (the goal this spec implements)
2026-05-09 17:17:12 +02:00
jgrusewski
44e1f58b69 docs(sp18): copy spec + plan to feat/sp18-combined branch
Mirrors the SP17 PP.1 plan-copy pattern. Plan and spec source-of-truth
live on the sister `feat/sp17-dueling-q-network` worktree; this commit
copies them onto the SP18 branch so subsequent commits reference local
paths.

Spec: docs/superpowers/specs/2026-05-08-sp18-reward-shape-hold-attractor-design.md
Plan: docs/superpowers/plans/2026-05-08-sp18-reward-shape-hold-attractor.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 00:25:06 +02:00
jgrusewski
5417e27567 docs(sp15): amend spec v2 — 10 fixes from second critical review
Second critical review pass found 10 more issues, 3 critical.
All addressed:

CRITICAL:
- §8.2 (3.1): ALPHA_SPLIT cold-start was unspecified — formula
   produces 0/0=0, not the claimed 0.5 sentinel. Now: ISV slot
   initialized DIRECTLY to 0.5 in trainer constructor; formula
   takes over only after both grad-norm EMAs accumulate N_warm
   non-zero observations
- §9.2 (3.5.4): plasticity now performs TWO-STEP recovery:
   (1) Flat all positions at fire bar (close current losing trade),
   (2) engage warmup cooldown forcing Hold for M_warm bars.
   Without step 1, forced Hold preserved the losing position
   that drove drawdown for the entire 200-bar warmup
- §12.2: stale "5-10%" baseline cost estimate updated to "15-25%"
   matching §6.4 (was contradicting earlier amendment)

IMPORTANT:
- §9.2 (3.5.5): DD_TRAJECTORY_DECREASING threshold 0.02 hardcoded
   → ISV-driven via new slot DD_TRAJECTORY_FLOOR (slot 441,
   25th percentile of running dd_pct distribution)
- §8.2 (3.5): HOLD_FLOOR_ALPHA tracked from rolling 95th percentile
   of |Q_dir| (NOT running max — was outlier-ratchet vulnerable)
- §9.2 (3.5.3): MEDIAN_STREAK_LENGTH formerly undefined in cooldown
   K formula → ISV-driven via new slot 442, running median of
   observed loss-streak lengths via two-heap algorithm
- §9.2 (3.5.2): asymmetric reward × α split compound interaction
   explicitly stated as intentional with POS_CAP as binding ceiling

NIT:
- §7.4: "2C: Group 3 (4 tests)" → "(5 tests)" (was off-by-one
   after 2.22 added)
- §9.2 (3.5.4): Xavier → Kaiming-He init for advantage head reset
   (architecturally appropriate for ReLU-gated activation chain)

ISV_TOTAL_DIM: 441 → 443 post-SP15 (added DD_TRAJECTORY_FLOOR
and MEDIAN_STREAK_LENGTH at slots [441..443)). 46 SP15 slots
total. File: 799 → 811 lines.

Spec is now consistent end-to-end with no contradictions between
sections, no hardcoded values violating feedback_isv_for_adaptive_
bounds, and no underspecified load-bearing parameters.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 08:58:28 +02:00
jgrusewski
f3cb0c78c2 docs(sp15): amend spec — 14 fixes from critical review
Critical review (combined fresh-eyes subagent + self-critique) flagged
14 issues. All addressed in this amendment.

CRITICAL (blocks implementation):
- §4.3 NEW: ISV slot allocation map [397..441) per phase, prevents
   parallel-dispatch ISV-table conflict (was: undefined, would break
   Approach B)
- §4.4 NEW: Phase 2A ↔ Phase 1.2 ABI contract (LobBar struct as
   canonical contract, Phase 2A defines first; Phase 1.2 conforms).
   Restores parallelism dependency
- §9.2 (3.5.4): plasticity injection now MANDATES cooldown engagement
   via PLASTICITY_WARM_BARS_REMAINING slot 438. NEW anchor test 2.22
   plasticity_cooldown_interlock prevents random-tail-output retrigger
   loop
- §9.2 (3.5.5): recovery curriculum replaced episode-level metadata
   (didn't exist in transition-level PER) with per-step DD_TRAJECTORY_
   DECREASING signal — same goal, no PER restructure

IMPORTANT (rework prevention):
- §9.2 (3.5.3): cooldown K threshold from running mean of per-trade
   PnL (NOT variance — variance is LOW during streaks, would delay
   cooldown when needed)
- §8.2 (3.5): Hold floor function form specified as bounded sigmoid
   with α/k/ε₀ ISV-driven; was "monotonic_inv_func" (undefined family)
- §6.2: cost kernel per-side semantics explicit. Half-spread × side_
   indicator at entry AND exit; commission_per_rt on close. Resolves
   round-trip vs per-side ambiguity
- §6.2: OFI impact λ initial value 2e-4 + ISV refit methodology;
   was "empirical fit from MES historical" (hand-wavy)
- §6.4: 8 baselines now mandate shared trunk forward; honest 15-25%
   overhead estimate (was 5-10%, optimistic)
- §12.3: Q9 burn policy explicitly honor-system, not enforcement.
   Mitigations described as deterrents not enforcement
- §10.6: Phase 4.5 thresholds CLI-config-driven with rationale,
   anchored empirically post-Phase-1 (was hardcoded > 1.0)

NIT:
- §9.2 (3.5.2): multiplier vs SP12 cap interaction explicit (applied
   BEFORE cap)
- §8.2 (3.1): α=0.7 hardcoded → ISV-driven from grad ratio (was
   feedback_isv_for_adaptive_bounds violation)
- §13: pearls reframed as candidates pending validation per discipline
   rule; pre-naming pressure removed

Test count: 21 → 22 (added 2.22). Phase 2 LOC: 2000 → 2100.
ISV_TOTAL_DIM: 396 → 441 post-SP15.

All 14 issues addressed in spec text. Ready for user review.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 08:51:35 +02:00
jgrusewski
fb5f2394d0 docs(sp15): trader discipline and recovery design spec
Design spec for SP15 — addresses the train-dd4xl downward spiral
post-mortem (8 epochs, trades 131k→64k, sharpe 79→44 with monotone
descent across active_frac, dir_entropy) and the walk-forward audit
finding (test_start..test_end slice generated but never consumed,
val IS the selection set, no sealed test).

Six phases over ~13-21 days:
- Phase 0: SP14 EGF ISV-driven retune (gate1=closed forever fix)
- Phase 1: honest numbers — unified sharpe kernel, cost-net (commission
   + spread + OFI-impact, dev/prod parity), drawdown reporting,
   8 counterfactual baselines, dd_pct as foundational state input,
   --holdout-quarters/--dev-quarters CLI, consume abandoned test slice
- Phase 2: 21 behavioral tests on dev RTX 3050 Ti, pre-commit hook
   gates argo-train.sh
- Phase 3: 5 trader teachings (r_quality/r_discipline split, explicit
   cost, quadratic DD, regret, confidence-aware Hold floor)
- Phase 3.5: 4 recovery mechanisms (asymmetric reward under DD,
   cooldown gate, plasticity injection, recovery curriculum in PER)
- Phase 4: L40S walk-forward Q1-Q7, Q8 final dev, sealed Q9 OOS

Cross-cutting discipline rule: no new pearl/controller/kernel/ISV
slot/reward term ships without a Phase 2 behavioral test that proves
the intended trader behavior. Hard rule, no exceptions.

Decisions captured from 7 clarifying questions answered 2026-05-06.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 08:32:37 +02:00
jgrusewski
40e737a181 docs(sp14): spec v3 — K=4 direction + ISV-adaptive β rate limiter
Two further user-flagged corrections from second-critical review:

1. Direction Q-head emits K=4 actions, NOT K=3.
   Authoritative source: state_layout.cuh:123-126
     #define DIR_SHORT 0   // open/maintain short
     #define DIR_HOLD  1   // keep current position (no-op)
     #define DIR_LONG  2   // open/maintain long
     #define DIR_FLAT  3   // close all to zero
   The "default: 3 — Short/Flat/Long" comment at gpu_dqn_trainer.rs:2438
   is stale pre-SP13. Production callsites all set branch_0_size: 4.
   SP13 added DIR_HOLD as a separate fourth direction action (Hold-pricing).
   The config default comment was never updated when DIR_HOLD landed.
   This is exactly the feedback_trust_code_not_docs failure mode — a
   single stale comment would have silently corrupted the q_disagreement
   signal (Hold/Flat being indices 1/3 instead of just Flat=1).

   Updates:
   - B.1 action-space context: 4 actions with Hold AND Flat both
     non-committal (Hold = keep position, Flat = exit to zero)
   - B.2.3 q_disagreement mapping: K=4↔K=2 with both Hold and Flat
     masked from disagreement signal (no new directional commitment
     to evaluate); only Short and Long picks contribute to disagreement
   - Edge case handling for all-Hold/all-Flat batches

2. Adaptive β rate limiter (was structural β=0.9).
   Per feedback_isv_for_adaptive_bounds, β should be signal-driven
   not hardcoded. v3 derives β from variance of α_grad_raw, mirroring
   the k_aux/k_q variance-driven steepness pattern in B.2.5.

   Formula:
     β = clip(β_base + variance_alpha_raw / variance_ref_alpha,
             [β_base, β_max])
     β_base = 0.5 (light smoothing baseline; ~2-step half-life)
     β_max  = 0.95 (heavy smoothing; ~20-step half-life)

   Stable α_grad_raw → β = β_base (preserves directional intent)
   Volatile α_grad_raw → β → β_max (dampens jitter)

   Adds 2 ISV slots:
     ALPHA_GRAD_RAW_VARIANCE_EMA_INDEX (Welford variance)
     BETA_RATE_LIMITER_ADAPTIVE_INDEX (current β value)

ISV slot count: 11 → 13 (net +2 for variance + adaptive β).
LOC estimate: ~1150 → ~1180 (negligible delta; 3 Welford
variances now in alpha_grad_compute_kernel instead of 2).

HEALTH_DIAG pearl_egf_diag emit updated to expose all three
adaptive scalars (α, β, k_aux/k_q) plus all three driving
variances (var_alpha, var_aux, var_q) for full observability.

Verified against current code at HEAD d243a6f08:
  - state_layout.cuh:123-126 (DIR_* enum truth source)
  - gpu_dqn_trainer.rs:2438 (stale K=3 comment confirmed)
  - branch_0_size: 4 in 5 production callsites
    (smoke_tests, gpu_iqn_head, gpu_backtest_evaluator)
  - DIR_HOLD usage in experience_kernels.cu:1298 + 14 other sites

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 17:21:17 +02:00
jgrusewski
d243a6f080 docs(sp14): spec v2 — critical review corrections
8 corrections from code-anchored critical review at HEAD eaf4adcb9:

1. Direction Q-head emits K=3 (Short/Flat/Long), not K=4. Aux head
   emits K=2 (down/up). Gate 2 q_disagreement now uses K=3↔K=2 mapping
   with Flat masking. Verified against gpu_dqn_trainer.rs:2438.

2. Forward launch order constraint added: aux forward must complete
   before direction Q-head forward (new serial dep). Cited
   pearl_canary_input_freshness_launch_order.

3. Adaptive sigmoid k formula fixed: v1 had unreachable k_max=50
   because formula caps k ≤ k_base. v2 uses max(..., k_min) with
   k_max = k_base implicit.

4. α_grad rate limiter promoted from nice-to-have to v1. Schmitt
   state-flip introduces sigmoid discontinuity. β=0.9 EMA smoothing
   added; new ALPHA_GRAD_SMOOTHED_INDEX slot.

5. q_disagreement_baseline drop: v1 had adaptive baseline as long-EMA
   (feedback loop risk). v2 uses structural 0.5 (analytic K=3-with-
   Flat-masked random alignment). Drop BASELINE_INDEX slot.

6. Backward gradient scaling clarified: α_grad scales dL/dx (input
   gradient flowing back to aux), NOT dL/dW (Q-head's weight grad).
   Q-head learns to use the wire freely; gate only controls upstream
   flow.

7. 4 hard rules added: feedback_no_hiding,
   feedback_no_htod_htoh_only_mapped_pinned,
   feedback_kill_runs_on_anomaly_quickly,
   pearl_canary_input_freshness_launch_order.

8. Smoke A2 explicit kill criteria table added (8 triggers).

Net ISV slot count unchanged (11), composition shifted: dropped
BASELINE, added SMOOTHED. Total impl cost ~1150 LOC (was ~1060).

Verified against current code:
  - TARGET_DIR_ACC_INDEX=372, AUX_DIR_ACC_SHORT_EMA_INDEX=373,
    AUX_DIR_PREDICTION_INDEX=375 (sp13_isv_slots.rs)
  - set_aux_weight clamp(0.05, 0.3) at gpu_dqn_trainer.rs:14722
    (confirms Bug 3 from Smoke A diagnostic)
  - mag_concat_qdir precedent at experience_kernels.cu:4560
    (direction-conditioning pattern; SP14's wire is the analog)
  - state_reset_registry pattern at lines 913-922 (canonical
    template for new EMA fold-reset entries)
  - branch_0_size = 3 in production config (the K=3 finding)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 17:11:04 +02:00
jgrusewski
eaf4adcb98 docs(sp14): spec — aux→Q wire + Earned Gradient Flow pearl
Designs the SP14 chain on top of SP13 Layer B (HEAD 6657e5626):

1. Sub-project A — stability fixes (3 small bugs found in Smoke A)
   - C51 atom-probability floor (ISV-driven from SP4 atom_pos_p99)
   - aux_w setter clamp lift [0.05, 0.3] → [0.15, 1.5]
   - Stagnation warmup gate at fold boundary

2. Sub-project B — the architectural piece (THIS spec)
   - Forward wire: aux_softmax_diff per-bar into direction Q-head input
     concat (in_dim+1, fingerprint bump, zero-init new column)
   - Earned Gradient Flow pearl — adaptive ISV-driven gradient gating:
     * Gate 1 (aux competence) — Schmitt-trigger hysteresis
     * Gate 2 (Q-head disagreement) — NEW signal, EMA per-step argmax
       mismatch
     * ISV-adaptive sigmoid steepness (variance-driven k_aux, k_q)
     * Per-epoch warmup ramp
     * Anti-gradient-hacking circuit breaker (mesa-opt defense)
   - 11 new ISV slots, 3 new GPU kernels, ~1060 LOC total
   - HEALTH_DIAG pearl_egf_diag observability line

3. Sub-project C — Adaptive LR (deferred until A+B effects measured)

Motivation from Smoke A diagnostic:
- aux_dir_acc reached 0.61 (signal extraction works)
- val_win_rate stuck 45-48% (no path to action selection)
- WR-flat-while-aux-varies = Q-head directional weights frozen
- 1109 GRAD_CLIP_OUTLIER events (chronic; not noise)

Three parallel diagnostic agents triangulated three interlocking root
causes:
- Slot 375 has zero readers (the wire was scoped but never built)
- C51 raw grad reaches 9.5e6, saturates SP7 budget controller
- aux_w controller muzzled by SP11-era [0.05, 0.3] clamp

The Earned Gradient Flow pearl is a new application of the codebase's
pearl pattern: ISV-driven adaptive controller, but applied to backward-
pass gradient flow instead of forward-pass features. The wire is one-
way (stop-gradient) by default; co-training is earned by both:
(a) aux head demonstrating label competence, AND
(b) Q-head showing it's actually fighting aux signal (informative
    disagreement above baseline).

Stability additions hardened against:
- Oscillation around target (Schmitt hysteresis)
- Numerical sigmoid saturation (argument clipping ±30)
- Stale variance EMAs across folds (state-reset-registry)
- Discontinuous warmup transitions (linear ramp)
- Mesa-optimization (gradient-hacking circuit breaker)
- Cold-start sentinel-state spurious gate openings (Pearl-A bootstrap)

Awaiting user review before invoking superpowers:writing-plans for
sub-project B (and a separate small plan for sub-project A).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 16:59:26 +02:00
jgrusewski
ba83fcd1f5 docs(sp13): v3 spec + plan — Hold-pricing replaces Hold-elimination
P0a.T3 v2 implementer's audit revealed `DirectionAction` enum doesn't exist; the
codebase uses an 8-variant fused `ExposureLevel` (ShortSmall/Half/Full, Hold,
LongSmall/Half/Full, Flat) with cross-crate consumers across 77 files and 32+
test files pinning the 8-variant invariant. Atomic Hold elimination would
cascade massively.

User insight (2026-05-04): Hold being FREE is the bug, not Hold itself. MFT
trading legitimately needs multi-bar holds; we want the model to use them
deliberately, not as a CQL-bias lazy default. Holding isn't free in the real
world — broker fees, margin interest, opportunity cost.

v3 reframes as Hold-pricing:
- 4-way action space stays; ExposureLevel::Hold stays; no cross-crate cascade
- 3 new ISV slots (380-382): HOLD_COST_INDEX, HOLD_RATE_TARGET_INDEX,
  HOLD_RATE_OBSERVED_EMA_INDEX
- Hold-rate observer: small GPU kernel + Pearls A+D smoothing
- Hold-cost controller: 5-line deficit-driven formula
  (excess > target → cost rises 1×→5× base; observed ≤ target → relax)
- Per-bar reward subtraction at action == DIR_HOLD site
- 2 GPU oracle tests for the controller

P0a.T3 cuts from ~250 LOC + 32-test cascade → ~120 LOC additive. T1+T2
already-staged work unchanged. T4/T5/Layer B/C/D structure preserved.

Tension with pearl_event_driven_reward_density_alignment acknowledged in
spec — per-bar Hold cost is exposure-NEGATIVE (away from Hold), models real
economic carry, ISV-bounded by controller. Inverse of the pearl's failure
mode. Faithful reward modeling, not artificial shaping.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 23:18:58 +02:00
jgrusewski
0ca45ef61d docs(sp13): v2 spec + plan after critical review
Spec v2 supersedes v1 with five major fixes from the critical review:
- Drop alpha-vs-benchmark reward (mathematically tautological — Long trades had
  alpha = -costs always against always-long benchmark)
- Split Phase 0 into 0a (Hold-only, clean test of user hypothesis) + 0b (aux
  amplification probe, only if 0a partial), avoiding the v1 confounded experiment
- Bound direction-skill bonus relative to |alpha| (cap_ratio × |alpha|) to prevent
  reward gaming on small-alpha correct-direction losers; spec adds 8-quadrant
  worked-example matrix verifying the no-negative-EV invariant
- Replace 5-epoch absolute-threshold gate with 10-epoch trajectory criterion to
  avoid false-negatives from aux head underconvergence
- Aux_w controller adds dual-EMA stagnation detector (decays toward base when
  no improvement) — prevents permanent destabilization of Q-head in
  data-limited case

Plan v2 mirrors spec changes:
- Phase 0a (Hold elimination + dir_acc instrumentation, ~300 LOC, 1 atomic commit)
- Phase 0b (aux_w controller replacement, conditional, ~80 LOC)
- Layer B (aux head regression -> binary classification, ~120 LOC)
- Layer C (skill bonus + luck discount with calibrated bounds, ~150 LOC)
- Layer D (30-epoch validation + 3 new pearls)

ISV slot allocation [372..380): drops slot 371 (BENCHMARK_PNL_CUMULATIVE),
adds 374 (AUX_DIR_ACC_LONG_EMA — stagnation), 379 (SKILL_BONUS_CAP_RATIO).

Plan integrates Explore-agent touch-list (50-70 sites) with corrected enum
ordering: Short=0, Long=1, Flat=2 (preserves codebase Short-first convention,
avoids 30+ stale-comment churn). Adds direction-bias signal handling at
experience_kernels.cu:5159 (Hold's 0.5 softening gate vanishes -> [1, 1, 0]).

Three new pearls planned for Layer D close-out:
- pearl_redefine_success_for_predictive_skill
- pearl_skill_bonus_must_be_alpha_bounded (calibration lesson)
- pearl_reward_quadrant_audit_required (meta-pearl)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 22:24:16 +02:00
jgrusewski
ecab584c32 spec(sp12): per-trade event-driven reward composition (v3)
Three architectural changes in one unified design to push the model from
HFT noise extraction (62% trade rate, sharpe-gaming) toward MFT alpha-hunting:

1. Asymmetric bounded cap (-10/+5) — restores loss aversion erased by
   SP11 symmetric cap. 2:1 ratio matches Kahneman/Tversky prospect theory.
   Anchor: pearl_audit_unboundedness_for_implicit_asymmetry.

2. Min-hold soft penalty with temperature curriculum — patience requirement
   at exit. Soft factor = deficit/(deficit+T), T anneals 50→5 over 50 epochs.
   Forces commitment without paralysis in early training.

3. Zero per-bar shaping (gate micro/opp_cost on events) — eliminates
   continuous-reward gradient that pulls toward continuous exposure.
   Anchor: pearl_event_driven_reward_density_alignment.

Combined: reward fires only on trade events with prospect-theory loss
aversion + commitment requirement. Pure per-trade event-driven Q-learning
properly aligned with per-trade P&L objective.

~50 LOC across 3-4 files. No new ISV slots in Phase 1 (constants only).
Cost ~€1.30 (€0.30 smoke + €1.00 30-epoch validation).

Empirical motivation: train-multi-seed-pmbwn 50-epoch on commit 6a259942e
showed sharpe-gaming pattern (PnL -30% over 8 epochs while sharpe held).
SP11 cap fix unmasked the per-bar shaping bias plus erased loss-aversion
that the unbounded loss path was implicitly providing.

Continues on sp11-reward-as-controlled-subsystem branch — SP12 is
architectural continuation of SP11, not separate work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 18:41:57 +02:00
jgrusewski
85069ba75e spec(sp11): amend B1b smoke-recovery — z-score normalization for mag-ratio canary
smoke-test-6wd2c on commit 61b2fa962 (B1b + 4 bug fix-ups) revealed a 5th
pathology not covered by the previous fix-ups: the mag-ratio canary's
linear-magnitude-ratio formula amplifies whichever component is
intrinsically largest, regardless of whether that's a useful signal.

Popart (trade P&L on segment_complete) is O(100) per fire while the
other 5 components (cf/trail/micro/opp_cost/bonus) are O(0.1-2). Even
with the slot 360 fix preventing total-reward contamination, popart's
intrinsic magnitude makes popart_mag / Σ ≈ 0.93. The controller blend
'winner_weight = ratio' then amplifies popart further. Smoke trajectory:
w_pop=2.0 → 2.44 → 2.57, curiosity_b=30 → 120 → 199, sharpe_ema=10.7 →
2.4 → 0.75 (cascading collapse).

Resolution: z-score normalization. Each component's magnitude divided
by its own running standard deviation before computing the ratio:

  popart_z = popart_mag_ema / max(sqrt(popart_var_ema), EPS_DIV) ≈ O(1)
  cf_z     = cf_mag_ema     / max(sqrt(cf_var_ema), EPS_DIV)     ≈ O(1)
  ...
  ratio[c] = component_z / Σ component_z   ≈ ~1/6 each when stable

Allocates 6 new ISV slots [361..367) for per-component variance EMAs.
Producers: extend popart_component_ema_kernel + reward_component_ema_kernel
to also emit variance via Welford's online algorithm (single-pass).
SP5_SLOT_END = 367, ISV_TOTAL_DIM = 367.

Carries forward main's slot 360 amendment (commit 52c0b7521 on main)
which the sp11 branch was missing, plus this z-score amendment.

Per-component gradient ratios (the original spec intent) don't fix this
either — in DQN there's no per-component gradient pathway; grad norm
scales with current_weight × magnitude, so it's the same bias. Z-score
normalization is the standard scale-invariant measure of significance
and matches what the SP11 controller is trying to express.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:30:52 +02:00
jgrusewski
9395b983cc spec(sp11): fix all 13 review issues — straight-up implementation
Critical bugs fixed:
1. Z-score formula: was mean(Δ)/mean(|Δ|), bounded to [-1,+1] — sigmoid
   never saturated, controller stuck in [0.27, 0.73]. Now true Z-score:
   delta_ema / sqrt(delta_var_ema) with two-pass var. Renamed canary
   slot 351 from VAL_SHARPE_STD_EMA to VAL_SHARPE_VAR_EMA.
2. Component weight renormalization added — Σweights = 1 enforced
   post-floor so PopArt's reward-magnitude EMA stays uncontaminated.
3. SABOTEUR_MIN bound violation — engagement-floor (0.1) was silently
   pulling output below 0.5 stated minimum. Added post-multiplication
   clamp to [SABOTEUR_MIN, SABOTEUR_MAX].
4. ratios[] undeclared — now __shared__ float ratios[6] block-loaded
   once from ISV.
5. §3.6 slot count off-by-five (15 → 20). All 20 slots get FoldReset
   entries; cold-start window behavior specified explicitly (no NaN path).

Architectural fixes:
6. Q-overconfidence concern addressed in scope — controller's
   REWARD_CF_WEIGHT_INDEX covers CQL conservatism. No SP11′ deferral;
   if T10 fails, extend controller outputs (e.g., target-update τ).
7. Curiosity recompute-at-replay specified (§3.5.1) — replay buffer
   stores base reward only; curiosity added per-tuple at replay time
   against current visit-count and current ISV. Eliminates stale-signal
   replay contamination that would worsen ep1-overfitting.

Smaller fixes:
8. Novelty signal specified — SimHash 42×16 projection → 16-bit code,
   1M-slot per-bucket count table, 1/sqrt(1+count). Hash table lives
   in state-reset registry (cleared at fold boundary).
9. Saboteur engagement operationally defined — per-bar
   |reward_with_saboteur − reward_without_saboteur| > EPS_ENGAGEMENT,
   block tree-reduce, EMA'd. EPS_ENGAGEMENT = 0.01 × PnL_EMA.
10. Duplicate sentence in §7 component-starvation paragraph removed.
11. Validation criteria strengthened (§9): aggregation across 9
    trajectories specified; primary metric = median peak-epoch ≥ 10
    (baseline median peak-epoch = 1); secondary = drop-from-peak ≤ 5%
    by ep20; tertiary = ep20 mean ≥ baseline ep1 mean. §9.3 fix-forward
    response codified — no rollback.
12. Phases split per project pattern — Layer A additive (3 commits:
    slots, canaries, controller), Layer B atomic consumer migration
    (1 commit, including replay-time curiosity), Layer C validation +
    close-out. No falsification gate; smoke is validation only.
13. Pearls A+D applied to controller outputs (§3.4.1) — chained
    apply_pearls_ad_kernel after controller writes scratch, smoothed
    values land in ISV. Consumers read smoothed slots.

Constants surviving (Invariant-1 anchors only, all rate-not-regime):
EPS_DIV, WEIGHT_HARD_FLOOR, SABOTEUR_MIN/MAX, CURIOSITY_PERMANENT_FRACTION,
CURIOSITY_BOUND_FRACTION, WEIGHT_FLOOR_FRACTION, ENGAGEMENT_FLOOR.

Spec is now full straight-up implementation; smoke validates Layer A
infrastructure and Layer B consumers, T10 validates SP11 success metric.
~1550 LOC across 3 commits in Layer A + 1 atomic commit in Layer B +
close-out in Layer C.
2026-05-04 00:20:25 +02:00
jgrusewski
d49fbbe4e2 spec(sp11): reframe curiosity as feature + add permanent floor
User correction: curiosity is the *fix* for the ep1-peak overfitting
pathology, not a hazard to defend against. Reframed §7 from "Risks"
to "Design notes" — curiosity bound is a signal-relative scale, not
a defensive cap.

Fix the contradiction this exposed in the formula: previous
`curiosity_pressure = stagnant_or_worse * curiosity_bound` went to
zero when improving, which would cancel the always-on exploration
the §7 narrative now relies on. Replace with permanent-floor pattern
per pearl_blend_formulas_must_have_permanent_floor:

  curiosity_floor    = 0.2 * curiosity_bound  (CURIOSITY_PERMANENT_FRACTION)
  curiosity_dynamic  = stagnant_or_worse * curiosity_bound
  curiosity_pressure = max(curiosity_dynamic, curiosity_floor)

Now curiosity is always ≥ 20% of bound (anti-overfitting baseline)
and rises toward the bound when stagnant (stagnation breaker).
Updated unit-test guidance to assert pressure > 0 even at z=+10.

CURIOSITY_PERMANENT_FRACTION=0.2 added to Invariant-1 fraction list.
Saboteur-rising-with-improvement reframed as adversarial-load feature
rather than over-stress risk.
2026-05-04 00:08:39 +02:00
jgrusewski
a72a23e6b1 spec(sp11): reward as controlled subsystem — fully ISV-driven
Brainstorm spec for SP11. Resolves the policy-stagnation pathology
surfaced in T10 train-multi-seed-xkjkb seed-0 ep0-14: model finds a
stable fixed point at ep1 (peak val sharpe 80.61), then OVERFITS to
it across remaining epochs (decline 80.61 → 70.58). Q-values grow but
val performance declines because reward function has no improvement
pressure.

Architecture (every input ISV-driven):
  - Z-score-driven adaptation (no hardcoded "improving" threshold):
      improvement_z = val_sharpe_delta_ema / max(val_sharpe_std_ema, EPS)
  - 10 ISV outputs: 6 component weights + curiosity_pressure +
    saboteur_intensity_mult + adaptive weight_floor + curiosity_bound
  - 5 ISV canaries: val_sharpe_delta + val_sharpe_std (Z-score noise
    estimate) + 6 per-component grad ratios + saboteur engagement +
    PnL magnitude EMA (signal-relative curiosity bound)
  - 4 new producer kernels (controller + 3 canary computers)
  - Audit + migrate hardcoded cf_weight=0.3 in mse_loss_kernel.cu:318
    and c51_loss_kernel.cu:789, plus other shaping multipliers
  - NEW reward dimension: curiosity bonus, bounded by PnL magnitude

Per pearl_controller_anchors_isv_driven: every threshold replaced with
sigmoid(z) — no constants encode "what counts as improving". Per
pearl_blend_formulas_must_have_permanent_floor: every weight has
adaptive floor preventing zero-out. Per pearl_engagement_rate_self_
correction: saboteur intensity self-corrects via engagement rate canary.
Per pearl_cold_start_exit_signal_or: improvement signal OR'd from
multiple canaries so single-signal-failure doesn't stall controller.

New pearl authored alongside spec: pearl_reward_as_controlled_subsystem
— meta-principle that every reward path degree of freedom is a unified
controller output. Subsumes controller-anchor pearl at the reward layer.

Scope: 20 ISV slots, 4 producer kernels, audit + migration of
hardcoded reward shaping constants, 1 atomic commit.
~1300-1700 LOC; ~2.5-3 hours subagent work.

Success metric: val_sharpe[ep20] > val_sharpe[ep1] (Fix 33-38 baseline
peaked at ep1; SP11 should shift peak later as model continues
learning).
2026-05-04 00:04:12 +02:00
jgrusewski
6ae9bc0055 spec(sp10): unconditional Thompson selector + ISV-driven temperature
Brainstorm spec for SP10. Resolves the val-Flat-collapse pathology that
persisted through Fix 33-37: the eval-time argmax in experience_action_
select picks Hold deterministically every val bar (dir_entropy=0,
trade_count=1 in 214k bars) regardless of controller state.

Architecture:
  - Delete `if (eval_mode)` argmax branch in direction-selector kernel
  - Use temperature-blended Thompson: q_eff = E[Q] + τ × (Thompson - E[Q])
  - τ = clamp(intent_eval_divergence / divergence_target, 0.5, 2.0)
  - τ self-corrects: collapse → high τ; healthy → low τ; permanent 0.5 floor
  - Reuses SP9's intent_eval_divergence_compute_kernel (extended, not new)

Per pearl_controller_anchors_isv_driven: τ is ISV-driven, no
hardcoded constants beyond Invariant 1 numerical anchors (clamp range).
Per pearl_blend_formulas_must_have_permanent_floor: MIN_TEMP=0.5 ensures
eval ALWAYS has stochasticity.

The pearl_thompson_for_distributional_action_selection was about Bellman
TARGET argmax (selector/target symmetry). It does NOT prohibit Thompson
at the rollout selector. SP10 amends the pearl to clarify.

Scope: 1 ISV slot, kernel modification (no new kernel — extend SP9's
producer), 1 consumer kernel rewrite, test update, pearl amendment,
audit doc Fix 38. ~300-500 LOC, single atomic commit per
feedback_no_partial_refactor.
2026-05-03 22:24:14 +02:00
jgrusewski
38d2408604 spec(sp9): Kelly cold-start warmup floor — fully ISV-driven design
Brainstorm spec for SP9 (Fix 37). Resolves the val-Flat-collapse pathology
surfaced in T10 train-multi-seed-wsnc6 ep1-3: training intent_dist_f=0.47
but eval_dist_f=0.00 because Kelly cap pinned eval mag to Quarter, with
trade_count=1 in 214k bars (chicken-and-egg: cap closed → no trades → no
Kelly samples → cap stays closed).

Architecture per pearl_controller_anchors_isv_driven and
pearl_cold_start_exit_signal_or:

  final_kelly_f = max(measured_kelly_f, warmup_floor)
  warmup_floor  = base_floor × (1 − combined_confidence)
  base_floor    = clamp(q_var_mag / q_var_mag_ema, MIN, MAX)
  combined_confidence = max(statistical, behavioral, temporal)

All thresholds ISV-driven via Pearl D Wiener-α; only Invariant 1 numerical
anchors (clamp ranges, EPS) remain as constants.

Scope: 9 new ISV slots, 6 new GPU producer kernels including a
mandatory eval_dist GPU migration (eliminating DtoH readback at
gpu_backtest_evaluator.rs:1353 per feedback_no_cpu_compute_strict).
~1000-1200 LOC, single atomic commit per feedback_no_partial_refactor.

Pearls authored as part of brainstorm:
  - pearl_controller_anchors_isv_driven (regime-encoded constants)
  - pearl_cold_start_exit_signal_or (OR'd signals across statistical /
    behavioral / temporal axes)
2026-05-03 20:33:36 +02:00
jgrusewski
8f484f3312 spec(sp7): v2 — flatness-gated targets + Wiener α + sentinel-bootstrap
Self-review found 4 violations of feedback_isv_for_adaptive_bounds in v1:
1. TARGET_CQL_OVER_IQN=2.0 hardcoded → flatness-modulated per-branch
   target_ratio = ANCHOR × (1 − flatness[b]). Realizes the original
   pearl_q_flatness_gates_conservatism brainstorm idea.
2. TARGET_C51_OVER_IQN=1.0 hardcoded → mirror direction:
   target_ratio = ANCHOR × flatness[b].
3. ALPHA_META=0.01 fixed → Wiener-optimal α from per-branch state-tracker
   EMAs (16 new ISV slots: diff_var/sample_var × 2 heads × 4 branches).
4. Cascaded floor cleanup: compute_adaptive_budgets line 3384 floors
   reads to 0.02/0.05 always, fighting the controller. Replaced with
   sentinel-aware bootstrap: cold-start uses BOOTSTRAP value, post-write
   takes ISV value verbatim.

Acceptance criteria rewritten to be ISV-relative where the corresponding
signal exists (q-spread vs NOISY_SIGMA, label_scale vs NOISY_SIGMA band).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 00:11:26 +02:00
jgrusewski
0808ebe3e5 spec(sp7): loss-balance controller — adapt CQL/C51 budgets to grad-norm ratio
Outcome-driven controller replacing Pearl 2's hardcoded-0 CQL budget and
floor-pinned C51 budget with multiplicative ratio adaptation. Targets
`grad_split_bwd cql/iqn = 2.0` and `c51/iqn = 1.0` per slice (trunk, dir,
mag). Slow EMA (α=0.01) consistent with existing controller pearls; Pearl
A sentinel-bootstrap on fold boundary; Pearl D Wiener-optimal smoothing.

Replaces ghost docstring at fused_training.rs:3409 ("0.10×(1−regime)×health"
formula was never implemented). Pearl 2 keeps owning IQN budget (reference
denominator) and FLATNESS_BASE (consumed by NoisyNet σ pearl); stops
writing CQL/C51/ENS slots so the new controller is the sole driver.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 00:03:14 +02:00
jgrusewski
ca6e0007da docs(sp6): brainstorm spec — per-branch consumer infrastructure refactor
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-02 01:36:48 +02:00
jgrusewski
6e6e0fa114 docs(sp5): remove duplicate Layer A close-out section (superseded by Layer D) 2026-05-01 19:54:02 +02:00
jgrusewski
af8fe57d1c docs(sp5): apply critical review fixes — Layer D, Kelly carve-out, Pearl 4 mitigations, acceptance loosened
Critical self-review surfaced 15 issues; user directed fixes:
  - "a no cpu path": KEEP host-EMA close-out (rule compliance) — but split
    into separate Layer D atomic commit (was mis-scoped as Layer A)
  - Pearl 4 kept: 3 concrete risks documented (constant-β proof break,
    β2 memory reset destabilization, ε numerical envelope), structural
    envelope bounds added, ALPHA_META halved, ε-only fall-back path defined
  - Pearl 6 Kelly cross-fold persistence carve-out: separate slot range
    280..286, NOT in SP4 fold-reset registry, Invariant 1 architectural exception
  - Commit ordering Pearls 1 → 3 → 2 → 4 → 5 → 6 → 8 → 1-ext (resolves
    Pearl 2 circular dependency on 1+3)
  - Acceptance criteria: correctness gates (must pass) + performance gates
    (loosened to "not catastrophically negative", within 2σ of pre-SP5)
  - Pearl 7 timing: explicitly Layer C step 4, post-Layer-B + 3-seed validation
  - Pearl 8 enumeration: 4 slots (TRAIL_DIST_PER_DIR per direction)
  - Pearl 9 collapsed: 0 slots (Thompson achieved via Pearl 1's atom adaptation)
  - Total slot count corrected: 110 (was 120-128 inconsistent)

Layer structure: A (additive, 8 commits) → B (atomic, 11 consumers) → C
(validation + Pearl 7 investigation) → D (host-EMA close-out, separate
atomic commit). Layer D split off from A's "close-out" because PnL
aggregation pipeline migration is its own architectural concern.

User final review pending before invoking writing-plans skill.
2026-05-01 19:50:49 +02:00
jgrusewski
3361944456 docs(sp5): resolve open questions Q1=c, Q2=a, Q3=a
Per user direction:
  Q1 (Layer A granularity): per-pearl commits (~9 commits, decision c)
  Q2 (Pearl 7 timing):     investigate-only in SP5; fix in follow-up
                           if Bin(2,0.5) persists post-Pearls-1-3 (decision a)
  Q3 (Pearl 4 Adam β):     include as designed; accept theoretical risk
                           with Pearls A+D + EPS_CLAMP_FLOOR mitigation;
                           Layer C smoke monitors for destabilization;
                           fall-back to ε-only if observed (decision a)

Spec section updated:
  - Layer A description: per-pearl commit structure
  - Pearl 7 framed as investigation-only with conditional follow-up
  - Pearl 4 documents theoretical caveat + mitigation + fallback

User final review pending before invoking writing-plans skill.
2026-05-01 19:39:59 +02:00
jgrusewski
5f1c1eec51 docs(sp5): comprehensive per-branch + per-group adaptation spec — close all hardcoded-multiplier deferrals
SP5 design covers every known adaptive-parameter deferral in the DQN
training loop in a single coherent project. After SP5: zero hardcoded
multipliers, every adaptive value ISV-driven via Pearls A+D.

9 pearls + 1 sweep close-out + 1 validation milestone:
  1-3. Per-branch atom span / loss budget / NoisyNet σ (52 slots)
  4.   Per-group Adam β1/β2/ε ISV-driven (24 slots)
  5.   Per-branch IQN τ schedule (20 slots)
  6.   Kelly cap signal-driven floors (6 slots)
  7.   dist_q/h/f Bin(2,0.5) audit + action_select fix (0-8 slots)
  8.   Trail stop signal-driven thresholds (6-8 slots)
  9.   Thompson direction-branch temperature (4 slots)
  1-ext. Per-branch C51 num_atoms (4 slots)
  Layer A close-out: 5 host-EMA host→GPU migrations
  Validation: 3-seed × 50-epoch acceptance gate

Total: 120-128 new ISV slots, ~5000-7500 LOC, 11-13 producer kernels,
~12 consumer migrations.

Layer A (additive infrastructure, ~15 commits) → Layer B (atomic
consumer migration, single coordinated commit) → Layer C (validation +
cleanup). Mirrors SP4's layer pattern.

Triggering data: train-multi-seed-cv2mw 50-epoch L40S baseline
(terminated F0 ep10) revealed magnitude head Q-flatness, eval collapse,
and frozen action distributions. Plus all SP4 close-out + sweep
deferrals folded in per user direction "no deferrals — make a single
plan based on ALL findings".

Spec at:
  docs/superpowers/specs/2026-05-01-sp5-magnitude-differentiation-and-eval-collapse-design.md

User review pending before invoking writing-plans skill.
2026-05-01 19:32:39 +02:00
jgrusewski
24accea774 fix(sp4): migrate fast/slow grad-norm EMA update to GPU per feedback_no_cpu_compute_strict
Layer C close-out C1 redesigned. Original plan claimed
grad_norm_slow_ema_pinned was orphan post-Mech-6 migration; verification
surfaced a SECOND live consumer (fold_warmup_factor_update, commit
4ef1d8ebb) that legitimately reads the slow EMA as cross-fold
steady-state baseline.

The actual defect: the EMA UPDATE at update_adaptive_clip:22720-22737
was host-side `(1-α)*prev + α*obs` arithmetic — exactly the pattern
feedback_no_cpu_compute_strict (saved 2026-05-01) strictly forbids.

Migrated:
  - New `update_grad_norm_emas_kernel.cu` — single-thread fused fast+slow
    EMA update kernel. Reads `grad_norm_buf[0]`, updates two mapped-pinned
    EMA scalars via dev_ptr. `__threadfence_system()` ensures the
    `fold_warmup_factor_kernel` consumer sees freshly-written values.
  - `launch_update_grad_norm_emas` Rust launcher chained on the
    producer's stream — graph-capture-compatible, no host sync.
  - update_adaptive_clip's host-side `unsafe { ... }` block replaced
    with the GPU launcher call (warn-and-continue on launch failure
    mirroring the launch_h_s2_rms_ema / launch_fold_warmup_factor
    per-step ISV producer pattern at training_loop.rs:3450/3464).

Preserved:
  - grad_norm_slow_ema_pinned mapped-pinned buffer (cross-fold persistent;
    legitimate consumer is fold_warmup_factor_update).
  - Fixed-α design (FAST_ALPHA=0.1, SLOW_ALPHA=0.001) — Pearls A+D
    adaptive α would defeat the cross-fold-baseline semantic the warmup
    factor depends on.
  - Cold-start sentinel (`prev ≤ 0.0` ⇒ assign obs directly) — same
    formula as the deleted host code.
  - Host-side update_adaptive_clip early-return guard — kernel only
    launches when observed_grad_norm is finite and > 0.
  - grad_norm_emas_step_count host counter — scalar control-flow
    metadata for warmup-window gating, not compute.

Plan/spec docs updated to remove stale "orphan" claim. State-reset
registry doc-comment + field doc-comments updated to reflect GPU-only
update path. fold_warmup_factor_kernel docstring no longer describes
its grad-norm EMA inputs as host-side-fed.

Build clean, sp4 + state_reset_registry lib tests pass (11/11), 16/16
SP4 producer GPU tests pass on RTX 3050 Ti. No behavior change — pure
architectural fix.

Refs: SP4 Layer C C1 redesigned (was: retire). Original plan
docs/superpowers/plans/2026-04-30-sp4-signal-driven-magnitude-control.md
lines 2184-2221.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 14:07:10 +02:00
jgrusewski
d8666a232f docs(sp4): fix all 11 review findings — spec is now source of truth
Resolved every issue from the critical self-review:

1. Param-group count: 7 → 8 throughout. Slot total: 36 → 40 (8 groups × 3
   per-group families = 24 + 16 single = 40). IQL high-tau and low-tau
   are 2 distinct param groups (separate buffers + Adam states), not
   one. The "(Resolved during implementation)" hand-wave deleted.

2. Reset semantics section rewritten — no longer references Xavier-
   derived bootstraps. Sentinel 0 per Pearl A; Wiener-state triples
   reset to 0 alongside ISV slots (160 reset entries total).

3-4. Weight decay and L1 lambda hardcoded α/bootstrap removed. Both
     now follow universal Pearls A+D contract (sentinel-detect +
     Wiener adaptive). For L1, the natural curriculum (λ ramps as
     gradient differentiates) emerges from the signal itself, no
     "Bootstrap = 0.0" constant needed.

5. ε naming collision resolved: ε_div = 1e-8 (Pearl D division-safety),
   ε_clamp_floor = 1.0 (Pearl A consumer cold-start floor). Distinct
   names, distinct purposes.

6. Per-signal kernel signature updated: removed stale `ema_alpha` arg,
   added `wiener_state` pointer + `wiener_state_offset` + uniform
   `alpha_meta` (structural meta-EMA constant).

7. Pearl C engagement counter race fixed. Replaced `clamp_engage_buf
   [group] += 1` (race) with proper register-then-tree-reduce pattern
   mirroring `dqn_grad_norm_kernel`. Per-thread register counter →
   block-shared tree-reduce → single block-leader writes to
   `clamp_engage_per_block_buf[engage_buf_offset + blockIdx.x]`.
   Host sums across blocks. No atomicAdd, consistent with feedback.

8. Pearl D state buffer allocation spelled out: `wiener_state_buf:
   MappedF32Buffer` of size 141 floats (47 slots × 3), per
   `feedback_no_htod_htoh_only_mapped_pinned`. Reset-registry entries:
   40 + 40×3 (new bound slots + Wiener triples) + 7×3 (retrofit
   existing producers' Wiener states) = 181 reset entries.

9. `grad_norm_slow_ema` retirement spelled out in Layer C: removed
   entirely (sole consumer Mech 6 migrates to ISV[GRAD_CLIP_BOUND]).
   Also documented Q_ABS_REF=16 and H_S2_RMS_EMA=96 transition to
   "monitoring-only ISV" — producers stay running, only the orphaned
   Mech 1/2/5/6/9/10 consumer reads removed.

10. Histogram bin range fixed: linear-spaced bins from 0 to step_max
    (avoids log(0) singularity from earlier log-spaced design).
    Linear is also better for p99: top-of-distribution gets ~0.4%
    resolution per bin. Degenerate "step_max == 0" branch handles
    all-zero signals gracefully (skip ISV update, leave previous bound).

11. Pearl D-subsumes-Pearl-A claim CORRECTED: mathematically wrong.
    At t=0, Pearl D's formula yields `x_mean[0] = 0`, not `x[0]`.
    Pearl A's first-observation replacement requires an explicit
    sentinel-detection branch in the producer. Both pearls are
    necessary and complementary; they are not hierarchical.

Slot count math now consistent: 1 target_q + 4 atom_pos +
3×8 per-group + 1 grad_clip + 1 h_s2 + 8 wd_rate + 1 l1_lambda = 40.
Producer count: 15 fused (1 + 4 + 8 + 1 + 1).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 21:36:11 +02:00
jgrusewski
074b4f0865 docs(sp4): fold all 4 pearls into spec — A, B, C, D
PEARL A — First-observation bootstrap: eliminates Xavier-derived
formulas (2.33 z-score, √2 std, √(2/K_in)) from the bootstrap section.
Sentinel ISV[X]=0 at fold reset; producer step 0 detects sentinel,
replaces directly with step_observation. Subsequent steps EMA-blend.
Consumer cold-start safety via .max(1.0) numerical floor only (Adam-ε
category, not magnitude). Truly zero magnitude constants in bootstrap.

PEARL B — Fused per-param-group statistics oracle: producer count
36 → 14. Per-group fused kernel reads (params, grads, adam_m, adam_v)
once and computes WEIGHT_BOUND, ADAM_M_BOUND, ADAM_V_BOUND, WD_RATE
in one multi-pass operation. Trunk's oracle adds Pass E for L1_LAMBDA
gradient-direction entropy. 4× memory bandwidth reduction. Cleaner
conceptual unit (per-param-group bounds = one oracle).

PEARL C — Engagement-rate self-correction: detects post-clamp
feedback-loop saturation. For in-kernel clamps, theoretical engagement
rate = 1% (top 1% by p99 definition). Producer-side rate-deficit EMA
detects sustained deviation; force-bumps bound to step_max when
detected. Resolves the "in-kernel feedback loop accepted" limitation
from first draft. Per-Adam-kernel block-shared-memory engagement
counter (no atomicAdd), block-wide reduce, host-side rate-deficit EMA.

PEARL D — Wiener-optimal adaptive α: replaces all hardcoded EMA rates
across 14 new producers AND 7 existing pre-SP4 producers. Per-step:
α* = diff_var / (diff_var + sample_var + ε_num). Theoretically optimal
under Wiener-filter analysis; subsumes Pearl A as t=0 edge case
(both vars=0 → α=1 → first-observation replacement). On stationary
signals: α→0 (smooth). On non-stationary: α→1 (track). Eliminates
the recursion problem (α controlled by signal stats, not another α).
ε_num = 1e-8 (Adam-ε numerical category). 3 floats of state per
producer. 36+ hardcoded α values eliminated codebase-wide.

Limitations section restructured: 6 of 8 first-draft limitations
RESOLVED by pearls (in-kernel feedback loop, smoke time-budget,
producer plumbing, stale-bound, 8 carved-out items, magnitude bootstrap
formulas). Remaining 6 limitations are genuinely irreducible (F0
launch-scheduling variance, Layer B atomic flip risk, novel-pearl
field-validation, equilibrium-formula non-stationarity, Pearl C
counter-state cost, Pearl D state cost).

SP4 now closes 100% of the magnitude/regularization/EMA-rate surface.
No hardcoded scalars remain in the entire bound/clamp/EMA chain.
Producer count: 14. ISV slot count: 36. Unit tests: 36.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 21:24:42 +02:00
jgrusewski
21b6058e07 docs(sp4): close all carve-outs — weight decay + L1 lambda fully signal-driven
Folds two new producer signals into SP4 scope, eliminating the earlier
"carved out" exception:

WEIGHT DECAY (per param-group, 7 new ISV slots):
  λ = |w·g| / max(||w||², ε)
Derived from equilibrium analysis of d/dt(||w||²) = 2(w·g) - 2λ||w||².
The equilibrium gradient-projection-onto-weight-direction divided by
weight norm. Same theoretical-derivation category as Adam β values.
EMA half-life α=0.005 (~140 steps). Bootstrap 1.0.

L1 LAMBDA (trunk only, 1 new ISV slot — NOVEL PEARL):
  λ = (mean(|g|) / mean(|w|)) × D
  where D = (log K - H_observed) / log K  is gradient-direction entropy deficit
  and H_observed = -Σ p[i]·log p[i],  p[i] = ||g[:,i]|| / Σ ||g[:,j]||

L1 regularization-strength derives from gradient-direction entropy
deficit across input features. When gradient is uniform across features
(D≈0): network hasn't differentiated, λ=0 (no pruning). When gradient
concentrates on few features (D≈1): network has identified what matters,
λ ramps up to prune the rest. Self-curriculum — L1 strength tracks the
emergence of feature differentiation.

This extends pearl_adaptive_moe_lambda (regularization strength = EMA-
tracked deficit of regularized quantity) to feature-redundancy domain.
Pearl-name candidate (post-validation): pearl_signal_driven_regularisation_strength.

Bootstrap λ=0 means cold-start = no L1 pruning; ramp-up only after
gradient differentiates. Worst-case behavior is "L1 disabled" — graceful.

Total ISV slot count: 28 → 36. Total producer kernels: 28 → 36.
Effort estimate: 3000-4000 → 3500-4500 LOC, 1-1.5 → 1.5-2 weeks.

Acknowledged limitations updated: removed item #8 (carve-out) since
no carve-outs remain. Added items for L1 pearl novelty (untested) and
weight decay equilibrium-formula non-stationarity. Both have graceful
worst-case behavior and explicit validation criteria (#10 and #11) to
detect anomalies.

SP4 now closes 100% of the magnitude/regularization surface — no
hardcoded scalars remain in the entire chain. AdamW config fields for
weight_decay and l1_lambda removed from HyperParams to prevent
accidental hardcoding regression.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 21:12:05 +02:00
jgrusewski
506f9443fe docs(sp4): fix critical issue J — post-clamp diagnostic dead path
Second-pass self-review found a new critical issue: under "diagnostic =
clamp engagement", post-clamp diagnostics (Mech 9 weights, Mech 5 Adam
m/v slots 36-43, slots 44-45) NEVER fire — because post-clamp |v| ≤
bound by construction, so producer-side comparison always returns false.

Fix: split diagnostic implementation by clamp location.
- Buffer-based clamps (Mech 1, 2, 10): diagnostic stays in producer
  (reads pre-clamp buffer, fires when step_max > bound).
- In-kernel clamps (Mech 6, 9): diagnostic lives INSIDE the Adam
  kernel at the clamp step. Each Adam kernel takes a `diag_slot` arg
  alongside `weight_clamp_max_abs`; on clamp engagement, writes
  `nan_flags_buf[diag_slot] = 1`. Idempotent per-thread store (all
  threads writing 1, race-free per existing convention). No atomicAdd.

Without this fix, slots 36-45 would be dead diagnostics under SP4 —
detecting nothing, providing no signal. With this fix, every diagnostic
slot fires on its corresponding clamp engagement regardless of where
the clamp lives.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 21:01:21 +02:00
jgrusewski
cfe57109e3 docs(sp4): revise spec — fix 8 critical issues from self-review
Self-review found multiple problems with the first draft. This revision
addresses all 8 critical issues:

1. P² parallelization claim was WRONG — P² is sequential.
   Replaced with dynamic-range histogram (3-pass single-block kernel:
   max-reduce → log-spaced bin → cumulative-from-top to find p99).
   256 bins → ~0.4% quantile precision; numerical-precision derivation
   in same theoretical-constant category as floating-point precision.

2. Bootstrap values had wrong magnitudes — used Xavier σ instead of
   p99 of max-element. Recomputed: WEIGHT_BOUND[group] = 2.33 ×
   √(2/K_in[group]); H_S2_BOUND = 2.33 × √2 ≈ 3.3. All bootstraps
   now from theoretical p99 under Xavier-init, not std.

3. EMA half-life of 700 steps (α=0.001) didn't converge in 5-epoch
   smoke. Revised α=0.005 (~140 steps) for weight/Adam producers —
   reaches ~99% convergence within one fold's training. Smoke now
   validates steady-state behavior, not just bootstrap.

4. Weight decay, L1 lambda, CLIP_MULTIPLIER were listed in scope but
   undesignable in producer-consumer pattern. CARVED OUT explicitly:
   weight decay + L1 λ → separate research-spec; CLIP_MULTIPLIER and
   MIN_CLIP subsumed by SP4's GRAD_CLIP_BOUND slot.

5. F0 ≥ 45 acceptance criterion was uncertain. Revised to F0 ≥ 37.5
   (matches the 1e30-effectively-unclamped diagnostic smoke). The
   ~8-point F0 variance from launch-scheduling-shift is independent
   of clamp value; SP4 cannot guarantee F0=45 even with ideal design.

6. Per-param-group p99 plumbing concretized: each producer takes
   (offset, length) launch args; main DQN params buffer sliced into
   trunk/value/branch using existing param_sizes layout knowledge.

7. Pre/post-clamp feedback loop EXPLICIT: producer runs BEFORE
   consumer for buffer-based clamps (h_s2, target_q, atom_pos —
   in captured graph immediately before clamp). Producer runs AFTER
   for in-kernel clamps (weights, Adam m/v — feedback loop accepted
   with documented soft-anchor dynamics). No more hand-waving.

8. Unit test strategy: per-producer kernel test with synthetic
   Gaussian input → known p99 ≈ 2.33 → assert |computed - 2.33|<5%.
   28 tests total. Catches algorithm bugs before L40S smoke.

Effort estimate revised UP: 3500-4500 LOC (from 2-3000), 1.5-2.5 weeks
(from 1-2). 28 producer kernels + 28 unit tests is more plumbing than
first draft assumed.

Self-review limitations section now explicit about: in-kernel feedback
loop accepted, smoke time-budget marginally validates Adam EMAs, F0
may not return to 45, Layer B atomic-flip risk, plumbing density,
stale-by-one-step bound, histogram-precision is design choice not
tuning, carved-out items remain hardcoded post-SP4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 20:59:24 +02:00
jgrusewski
e00736ce9b docs(sp4): signal-driven magnitude control design spec
Comprehensive design replacing every hardcoded magnitude multiplier in
the SP3 mechanism stack (Mechs 1, 2, 5, 6, 9, 10) plus pre-SP3 mechs in
the same magnitude-control surface, per feedback_isv_for_adaptive_bounds
and feedback_adaptive_not_tuned.

Core principle: the BOUND lives in an ISV slot, computed by a producer
kernel as p99 EMA of observed signal magnitude. Consumer reads the slot
and clamps directly — no multiplier between ISV read and clamp. Cold-
start ε from theoretical-init bootstrap (Xavier, etc.) — same theoretical-
constant category as Adam β values, not tuning knobs.

Architecture:
- 28 new ISV slots (7 base bounds × per-param-group split where appropriate)
- Per-signal P² (Jain-Chlamtac) quantile producer kernels
- Diagnostic = clamp engagement (sticky flag from producer's max-comparison)
- Migration in 3 layers: additive infra → atomic consumer flip → smoke

Out of scope: theoretical/structural constants (Adam β, Xavier formula,
attention 1/√d, hidden_dim, num_atoms). EMA rates stay as documented
statistical-design parameters (half-life ≈ observation time-window).

Estimated: ~2-3000 LOC across 3 layers, 1-2 weeks, 1 L40S smoke.

Awaiting user spec review before writing implementation plan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 20:49:09 +02:00
jgrusewski
ed727b51c0 spec(dqn): SP2 + SP3 combined — F0 regression fix + Q-learning structural stability
Designs the fixes for the two SP1 leftover issues identified at SP1 closure
(commit 047f175fb):

1. SP2 — F0 regression fix (instrumentation overhead optimization):
   Consolidate the 11 per-step check_nan_f32 launches in
   run_nan_checks_post_backward into a single fused-reduce kernel
   (dqn_nan_check_fused_f32_kernel). 1 launch instead of 11; expected
   F0 return to ~55 baseline (was regressing to ~35 across 3 SP1 smokes).

2. SP3 — Q-learning structural stability (5 mechanisms):
   - M1: Target-Q clipping at single-point source, ISV-driven
     (10 × isv[Q_ABS_REF].max(1.0))
   - M2: C51 atom-position growth bounds, same ISV bound
   - M3: Hard target sync at fold boundary (DQN + IQN + ensemble target nets)
   - M4: Adam EMA reset at fold transitions (comprehensive — all Adams)
   - M5: Embedded Adam-centric diagnostic instrumentation (slots 36-47):
     Adam m/v max-abs (slots 36-43), weight max-abs (44-45),
     target_q post-clip (46), atom span (47). Sticky-bit threshold
     checks; SP2's fused kernel is extended (NOT replaced) to handle
     all 24 slots in 24 blocks.

Architecture: SP2 lands first; Gate 1 must pass (F0 ≥ 53.08 + slot
24-35 firing topology unchanged) before SP3 starts. SP3's 5 mechanisms
ship in ONE commit per feedback_no_partial_refactor (related fixes
for the same root pathology — Q-target inflation drives Adam EMA
saturation drives weight drift drives cuBLAS overflow). Gate 2
evaluates the full SP1 7-criterion bar.

Operating principles (mandatory):
- ISV-driven design for every dynamic bound (no hardcoded clip values)
- ε floor on ISV multiplier per SP1 pearl (`isv.max(1.0)`)
- Combined RELATED commits per feedback_no_partial_refactor
- Permanent diagnostic infrastructure (always-on, no debug flags)
- No deferrals — anomalies during impl get fixed within scope

Validation cost: SP2 ~1-2 days + 1 smoke; SP3 ~3-5 days + 1-2 smokes.
Combined ~1000 LOC across 2 commits.

Spec covers: architecture, components (SP2 fused kernel + SP3 5
mechanisms), data flow, ISV slot bindings, validation gates, error
handling failure modes, operating principles, anti-patterns,
sequencing constraints, effort estimate, handoff conditions.
2026-04-30 08:36:32 +02:00
jgrusewski
792812baa1 spec(dqn): SP1 numerical stability — revise per critical review
9 substantive issues addressed inline:

1. ISV-driven design elevated from 'if applicable' to MANDATORY for
   all dynamic bounds in SP1 fixes. Numerical-stability ε bounds are
   the only carve-out (Invariant 1). Hardcoded tuning constants for
   dynamic ranges explicitly rejected.

2. F0 Sharpe regression criterion changed from absolute (≥55) to
   ratio-based (≥95% of latest baseline; floor 53.08 currently).
   Prevents iterative erosion across multiple fix commits.

3. 'F1 trending positive' replaced with concrete monotone-improvement
   test: Best Sharpe at last epoch ≥ Best Sharpe at first epoch of
   the same fold.

4. Pass criterion distinguishes NaN-CLAMPED-TO-ZERO (failure) from
   'Genuine grad collapse' (legitimate observation, permitted) per
   the existing infrastructure from commit d1808df14.

5. Multi-source NaN scenarios explicitly supported — γ + β may
   identify multiple kernels; SP1 fixes ALL within the same cycle.

6. F0-safety paper-review gate added BEFORE smoke validation. Audit
   doc carries 'F0 risk' (low/medium/high) per proposed fix; high-
   risk fixes get math-on-paper inspection before consuming L40S.

7. Audit doc structure now requires 'ISV bound option' column —
   forces ISV-first thinking at audit stage, not as afterthought.

8. Anti-patterns expanded: micro-clamping (per-op clamping that hides
   upstream causes); combining unrelated fixes (anti-pattern of the
   rich-commit principle); hardcoded constants for dynamic bounds.

9. 48-slot allocation explicitly justified (24 used + 12 new + 12
   headroom) AND marked reviewable by SP2 if right-size differs.

All 5 design sections preserved structurally; revisions integrated
inline. Per brainstorming skill: spec self-review fixes applied
without re-review cycle.
2026-04-29 22:59:00 +02:00
jgrusewski
dab5990287 spec(dqn): SP1 numerical stability investigation — F1 NaN root-cause
Brainstorm session 2026-04-29 produced this spec scoping Sub-project 1
of a three-sub-project decomposition addressing the F1 ep2 NaN
explosion that persists across 9+ defensive layers in Plan C Phase 2
(commits e445d07a..e9096c7be).

Decomposition:
- SP1 (this spec): F1 NaN root-cause; γ audit + β always-landing
  instrumentation + surgical fix + multi-fold validation
- SP2 (future): numerical stability framework — codify guards
- SP3 (future): Q-learning structural stability — target-Q clip,
  pessimistic ensemble, atom-range governance

Operating principles:
- No deferrals (anomalies fixed within SP1, not punted)
- Combined fixes — rich commits (per feedback_no_partial_refactor)
- Always-landing diagnostic instrumentation (24-slot nan_flags_buf
  expands to 48; permanent regression sentinel)
- F0 Best Sharpe ≥ 55 preserved (no regression on the working path)

Pass criterion: all 3 folds train 5 epochs, F0+F1+F2 Sharpe ≥ 0,
zero NaN-CLAMPED-TO-ZERO log lines, all 48 flag slots remain zero.

5 design sections approved iteratively: Architecture, Components,
Data Flow, Error Handling, Testing. Next: writing-plans produces
implementation plan.
2026-04-29 22:53:13 +02:00
jgrusewski
8a535681b7 spec(dqn): Plan C Phase 2 ACTIVE — UCB asymmetry regression triggers Thompson resumption
The original PAUSED state was motivated by measurement-artefact hunt
exit. The bug-hunt cycle is complete (commits a86fba2b1, b8788511c).
A new pathology has surfaced: ff00af68a's UCB count bonus activation
causes selector/target asymmetry → F0 Q-drift kill at epoch 2 →
F1+F2 cascade. Verified by paired DIAG smokes (smoke-test-qlz7t fail,
smoke-test-wmsht pass).

Thompson sampling on C51+IQN distributions eliminates the asymmetry
by construction (sample from learned distribution; no augment-then-
argmax step). Net code-surface decrease — replaces eps-greedy +
Boltzmann + UCB with one principled mechanism.

Plan C Phase 2 execution begins on branch plan-c-phase-2-thompson.
2026-04-29 16:02:53 +02:00
jgrusewski
fc4addaabf spec+plan(moe): correct gate input dim 42 → STATE_DIM=128
T1.6 implementer correctly identified that the gate input dim is
ml_core::state_layout::STATE_DIM=128, not the literal 42 the spec/plan
incorrectly stated. The 42-dim figure was the bar-feature subset; the
actual state vector is 128-dim (42 features + portfolio + MTF + OFI
padded to 128 for cuBLAS alignment).

Updated spec §3 architecture diagram, §4.1 gate subnetwork description
+ parameter count (3,272 → 8,776), and plan header architecture line.
Implementation in commit 28c707f6a is correct; this commit just makes
the spec match the implementation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:22:40 +02:00
jgrusewski
76e55047ec spec+plan(moe): add no-HtoD/HtoH constraint; mapped pinned only
Per feedback_no_htod_htoh_only_mapped_pinned.md (newly recorded): every
CPU<->GPU path in this redesign uses mapped pinned memory exclusively.
No cudaMemcpy HtoD, no Vec-to-Vec defensive copies, including in test
code. CPU is strictly read-only on the production surface.

Plan changes:
- New Task 2.0 promotes MappedF32Buffer / MappedI32Buffer from
  distributional_q_tests.rs local definitions to a shared
  crates/ml/src/cuda_pipeline/mapped_pinned.rs module so all kernel
  test wrappers (Test 0.F, upcoming MoE tests) share one
  implementation. Adds write_from_slice helper for direct host_ptr
  write (no memcpy).
- Task 2.1 test wrapper rewritten to allocate mapped pinned buffers
  + write to host_ptr + read GPU-written output via host_ptr. No more
  memcpy_stod / memcpy_dtov in test code.

Spec: new section 6.4 codifies the mapped-pinned-only constraint and
references the shared module + reference implementation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:46:41 +02:00
jgrusewski
8629a9e7c2 spec(dqn): MoE replacement for vestigial RegimeConditionalDQN
End-to-end investigation (2026-04-27) confirmed RegimeConditionalDQN is
vestigial decoration — 3 heads constructed at training start but only
trending_head ever receives gradient updates. GpuDqnTrainer (the actual
production GPU trainer) has zero references to RegimeType/regime
routing; experience replay inserts go to trending_head.memory only;
ranging_head and volatile_head stay at random init for the entire
training run. Several support APIs (get_count_bonuses_branched, config,
get_state_dim) hardcode-delegate to trending_head, ignoring the regime
split entirely. Per `feedback_no_hiding.md` (wire up or delete) and the
user's preference to fix not delete: the design wires regime
conditioning properly via Mixture-of-Experts replacing the vestigial
3-head architecture.

Pearl introduced and saved as `pearl_learned_gate_subsumes_handcoded.md`:
when the network already sees the heuristic's inputs, a learned gate
strictly subsumes any hand-coded discretization. This is the
load-bearing rationale — ADX/CUSUM are already at state indices 40/41,
so threshold-based regime classification is a strict information
bottleneck the gate can recover and improve on.

Design summary:
- Architecture: shared GRN trunk -> K=8 small expert MLPs (256->64->256
  bottleneck per expert, ~33k params each) -> learned gating network
  (state[42]->64->8 softmax) -> mixed h_s2 -> existing branching heads
  + C51 + IQN dual head. Soft full mixture (no top-k hardcoding); gate
  emerges peaky or flat from data. Anti-collapse load-balancing aux
  loss with default lambda=0.01 (configurable hyperparameter, not a
  kernel constant) prevents init-noise-dominated single-expert lock-in
  without forcing uniform utilization. User-confirmed signal:
  "collapses don't recover well in this codebase".
- 9 new ISV slots (118-126: per-expert utilization EMA + gate entropy
  EMA), GPU-driven producer per
  `pearl_cold_path_no_exception_to_gpu_drives.md`.
- 3 new small CUDA kernels (moe_mixture_forward/backward,
  moe_load_balance_loss) + 1 ISV producer; everything else is cuBLAS-
  reusable. CUDA Graph capture compatible.
- Atomic deletion (no fallback): regime_conditional.rs (~700 LOC),
  RegimeType enum, classify_from_features, RegimeMetrics,
  RegimeClassConfig, 4 DQNConfig regime threshold fields, per-regime
  3-file checkpoint format. DQNAgentType becomes thin wrapper over
  single DQN. Old checkpoints fail loudly with layout-fingerprint
  mismatch.
- 5-layer testing strategy (unit kernels, smoke, gate-differentiation
  validation, L40S production validation with explicit kill criteria,
  architecture-hash backward-incompat).

Out of scope (explicit): top-k routing, per-expert action heads,
hierarchical MoE, regime-conditional CountBonus/NoisySigma broadcast,
expert warm-start from existing trending checkpoint, CVaR action
selection on mixed C51 distribution.

Precondition: the in-progress use_* flag cleanup + count_bonus
[f32; N] refactor lands as its own commit before MoE implementation
begins, per `feedback_no_partial_refactor.md`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:31:28 +02:00
jgrusewski
0e8804a770 spec(dqn): reframe Thompson rollout PAUSED (not SUPERSEDED)
Retracts the prior SUPERSEDED footer (commit 42ffd6aad). The technical
proposal still stands — Thompson sampling on C51+IQN distributions is
the canonical action selector for distributional RL (Bellemare 2017,
Dabney 2018). Phase 0 tests and the Aggregation Contract are sound
math/engineering regardless of the measurement-bug findings.

What changed is the URGENCY framing, not the validity. The val-Flat-
collapse / Short-collapse observations cited as motivating evidence
were partly distorted by three measurement bugs (a86fba2b1 + b8788511c)
in the diagnostic infrastructure. The "ship Thompson NOW because
val_dir_dist collapses to 80%+ Hold/Flat" narrative dissolves; the
"Thompson is the principled action selector for our distributional
model" narrative stands.

Sequencing: PAUSED pending evidence from a fresh L40S 30-epoch
baseline (train-f8h6q, 2026-04-27 12:20) on post-fix code. The
baseline is a bug-hunting expedition — kill on anomaly, diagnose,
fix, re-run per feedback_stop_on_anomaly.md. Once healthy baseline
established, Phase 2 ships as principled improvement with clean A/B
against the trustworthy post-fix metrics.

Plans B / C / D are PAUSED, not cancelled. Files remain in
docs/superpowers/plans/. Resumption gate: post-fix baseline run is
bug-free or all surfaced bugs are addressed.
2026-04-27 12:27:22 +02:00
jgrusewski
42ffd6aadc spec(dqn): mark Thompson rollout SUPERSEDED — motivating pathology was a measurement artefact
The val-Flat-collapse / Short-collapse / C51 expected-Q bias hypothesis
that motivated the 4-plan distributional-RL Thompson rollout was
largely a measurement artefact in the diagnostic infrastructure, not
a real policy pathology. Three layered bugs in actions_history_buf
init + reader + mag_stats attribution conspired to inflate val_dir_dist
Short, inflate active_frac, and pin wr_h/wr_f to zero. After fixing
all three (commits a86fba2b1 + b8788511c), val_dir_dist matches
val_picked_dir_dist within ~5pp — no collapse, diverse picks.

Status footer added to the spec documenting:

  - what was actually wrong (3 measurement bugs)
  - what the post-fix data shows (mild passivity bias from early
    training, not a structural collapse)
  - what is preserved (Phase 0 unit tests as latent infrastructure
    for any future distributional Q-head; Aggregation Contract
    pearl as a sound engineering invariant)
  - what is cancelled (Plans B / C / D — Phase 1 audit, Phase 2
    Thompson integration, Phase 3 long verification — superseded)
  - future revival condition (if fresh L40S 30-epoch on post-fix
    code shows val_picked_dir_dist itself collapsing toward Hold/Flat,
    reopen)

Plan files remain in docs/superpowers/plans/ as historical record.
2026-04-27 12:23:34 +02:00
jgrusewski
021bb0ef73 spec(dqn): pivot Phase 0+2 tests to GPU-direct (no CPU mirror)
User correctly identified that CPU mirror function tests don't test
the production GPU code path. A bug shared between mirror and kernel
(translated identically wrong) would slip through. Mirror tests + a
single GPU bridge test were a weak compromise.

GPU-direct testing strategy:
  - All Phase 0 kernel-correctness tests (0.A, 0.B, 0.C, 0.D, 0.F):
    launch tiny test-only kernels with the SAME math the Phase 2
    production kernel will use; assert properties of the output.
  - Test 0.E (synthetic edge discovery): stays CPU. It tests an
    ALGORITHMIC PROPERTY of Thompson exploration (does it discover
    edge if edge exists?), not a kernel correctness property.
  - All Phase 2 unit tests (2.A-2.D): GPU-direct against the
    modified production kernel.
  - Phase 0.F (real checkpoint extraction): unchanged — already GPU.

Local development uses RTX 3050 GPU (per memory user_dev_environment.md).
CI runs --ignored flag to skip GPU tests on CPU-only runners.

Time budget: Phase 0 was 1-2 days (CPU mirror); now 2-3 days
(GPU-direct, includes kernel wrapper setup half-day).

Other delta:
  - Phase 0 deliverable file renamed: distributional_q.rs ->
    distributional_q_tests.rs (no mirror functions, just tests +
    kernel wrappers).
  - Phase 2 unit tests rephrased to launch production kernel rather
    than compare against CPU mirror.
  - L1 verification gate runtime: seconds -> minutes (GPU launch
    overhead per test).

The user's intuition was right: testing production directly is the
honest approach. Mirror was an optimization that traded correctness
for speed; with local GPU available the optimization isn't needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 00:27:46 +02:00
jgrusewski
25ba051157 spec(dqn): critical review pass — fix all majors, mediums, minors
Self-review identified 8 major + 8 medium + 15 minor issues. All fixed:

MAJORS:
  M1 Test 0.A: clarified — compare argmax(E[Q]), Boltzmann(E[Q]),
     and Thompson sampling distributions; assertion is on relative
     ordering across all three.
  M2 IQN quantile count: replaced hardcoded `5` with N_IQN_QUANTILES
     constant (defined per task #147 fixed-quantile design).
  M3 "Converged checkpoint" definition: ≥60 epochs trained AND
     val_sharpe stabilised (no >10% change over last 10 epochs).
     Cites prior 60-epoch validation runs (task #80, train-7rgqd).
  M4 R3 reframed: replaced "no issue" handwave with explicit
     by-design tradeoff acknowledgment + cost analysis. Wasted
     exploration is the cost of finding out whether edge exists.
  M5 Test 0.D σ_long=0.05 justified: chosen to match expected order
     of magnitude given typical |return| ~ 50bps; Phase 0.F
     validates against real checkpoint.
  M6 rng_ctr post-increment: clarified — matches existing pattern
     at experience_kernels.cu:858 (no behaviour change).
  M7 train_active_frac instrumentation: NEW Phase 2 deliverable —
     existing HEALTH_DIAG only has val_active_frac, but L3 verifies
     training-time active_frac. Spec now explicitly adds this
     ~10-line metrics.rs change as a Phase 2 deliverable.
  M8 eps_dir cleanup code-level detail: explicit reference to
     experience_kernels.cu lines 814-865; remove eps_dir from both
     static EPS_FLOOR clamp AND adaptive boost block; verify
     variable can be removed from kernel signature via grep.

MEDIUMS:
  Med1 Current C51/IQN combination: clarified that compute_expected_q
       blends per training schedule; Phase 2 replaces with explicit
       0.5*E_C51 + 0.5*E_IQN equal weighting; Phase 0.F verifies.
  Med2 Eval mode phrasing: "eval mode already sets eps=0 in existing
       kernel" — no semantic override, factually correct.
  Med3 Magnitude σ claim: clarified — magnitude branch likely has σ
       bias in OPPOSITE direction (Full has larger σ; UCB would
       prefer Full and worsen saturation). Empirical verification
       deferred. Phase 0.F should also report per-magnitude σ.
  Med4 Hierarchical sampling claim corrected: it's not about
       balancing 50/50 (already 50/50). It's about decoupling
       cluster-best decisions; clarified.
  Med5 n_atoms vs N_IQN_QUANTILES: clarified — n_atoms variable per
       config (currently 51); N_IQN_QUANTILES fixed at 5.
  Med6 Conviction code: removed pseudo-code; references existing
       implementation at experience_kernels.cu:1091; provides
       implementation hint for E[Q] reuse.
  Med7 Q-target propagation: clarified — uses full distribution
       (C51 atom projection / IQN quantile regression), not just
       E[Q]. Thompson modifies action selection only.
  Med8 References: added Thompson 1933 (original), Bellemare 2017
       (C51), Dabney 2018 (IQN) for theoretical foundations.

MINORS:
  Min1 Date updated to 2026-04-27.
  Min2-3 Argmax monotonic /2 simplified out — argmax(a+b) =
         argmax((a+b)/2). Code clarity improved.
  Min4 P(argmax picks Long) = 0 deterministic; reframed assertion.
  Min5 Test 0.F structural assertions added: σ_C51[FLAT] < 0.01 ×
       σ_C51[LONG]; same for IQN; E[Q_FLAT] > E[Q_LONG]; argmax
       picks FLAT; Thompson P(LONG)+P(SHORT) ≥ 0.20.
  Min6 -INFINITY → CUDART_INF_F (CUDA convention).
  Min7 dir_idx scope: comment notes it's declared earlier in kernel.
  Min8 action_select args: explicit — three buffers exist on GPU
       but not currently passed; new params, no new buffers.
  Min9 Phase 0 time math: 5 hours tests + 1 hour enumeration + 2
       hours 0.F + (3 hours runtime if checkpoint training needed,
       runs in parallel). Honest budget.
  Min10 "Two-stream" → "5-Layer Gate" header.
  Min11 Plan 5 reference uses full path consistently.
  Min12 Plan B time budget: explicit 1 day if pass; 2-5 days if bug.
  Min13 active_frac: clarified Long+Short combined, not per direction.
  Min14 train_active_frac: now in Phase 2 deliverables (see M7).
  Min15 "20 mechanisms" → 21, with sub-counts in section headers.

Spec now 615 lines, comprehensive coverage of:
  - Pearl + theoretical foundation
  - Problem statement (with bias-might-be-correct caveat)
  - Architecture (Thompson at training, argmax at eval)
  - Train vs eval distinct selectors with behavior-change disclosure
  - Direction-only scope with magnitude σ-bias warning
  - Conviction stays E[Q]-based (no Kelly cap jitter)
  - Interaction matrix: 21 mechanisms in 3 categories
  - 6 v2 enhancements documented + deferred
  - Phase 0/1/2/3 with tests, exit gates, time budgets
  - 5-layer verification + train_active_frac instrumentation
  - 8 risks with mitigations + 5 stop conditions
  - What v1 doesn't touch (referencing interaction matrix)
  - References (Thompson 1933, Bellemare 2017, Dabney 2018, etc.)
  - Aggregation contract (project-wide pearl, enforced)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 00:08:02 +02:00
jgrusewski
308c54484e spec(dqn): full interaction matrix + outside-the-box Thompson improvements
Per-user direction: every existing mechanism in the DQN system MUST be
explicitly considered for interaction with Thompson, and Thompson itself
MUST be examined for system-specific improvements beyond vanilla.

INTERACTION MATRIX (3 categories, 20 mechanisms):

Category 1 — Compose with Thompson (no change required):
  Counterfactual reward, B.2 novelty bonus, PopArt, Saboteur,
  Curiosity, NoisyNets/VSN, Distillation, CQL, Polyak target EMA,
  HER, PER, Replay warm-start, Multi-fold validation harness.

Category 2 — Trivially adapt to Thompson (one-line changes):
  D7/N7 contrarian sign flip (negate the SAMPLE), cosine epsilon
  schedule (still applies to mag/ord/urg), per-sample epsilon (IQL
  expectile gap), adaptive Boltzmann tau (still applies to mag/ord/urg).

Category 3 — Take precedence over Thompson (hard constraints):
  Plan-based action lock (Thompson sample discarded if plan active),
  per-magnitude Kelly cap, trail stop, capital floor breach.

Critical insights from the audit:

1. NoisyNets is ALREADY a form of training-time Thompson at the
   parameter level. Output-space Thompson stacks on top —
   total exploration = parameter-space ⊗ output-space (multiplicative).

2. Curiosity is ORTHOGONAL to Thompson — Thompson explores actions
   whose Q is uncertain; curiosity explores states whose dynamics
   are uncertain. Both axes desirable; no conflict.

3. Plan lock takes precedence; same as currently with Boltzmann.

OUTSIDE-THE-BOX v2 ENHANCEMENTS (deferred to follow-up specs):

  v2.1 Triple-source Thompson (C51 + IQN + Ensemble) — incorporate
       the existing ensemble Q-head as 3rd uncertainty source.
  v2.2 Persistent Thompson (anti-churn for HFT) — bias sampling
       toward current direction, ISV-driven; reduces tx_cost from
       Long/Short oscillation across bars.
  v2.3 CVaR-aware eval (risk-adjusted deployment) — eval picks
       argmax(E[Q] − λ·CVaR_α[Q]); risk-aware decision making for
       production with real capital.
  v2.4 Information-Directed Sampling (Russo & Van Roy 2014) — picks
       action minimizing regret²/info_gain; more efficient than
       vanilla Thompson when learning saturates.
  v2.5 Hierarchical Thompson on (trade vs no-trade) → (which
       direction) — addresses 50/50 structural advantage of no-trade.
  v2.6 Composition with curiosity-driven exploration — explicit
       coupling beyond reward-side composition.

Each v2 enhancement gets its own spec/plan when prioritised. Vanilla
Thompson is v1; ships first; verified independently.

ALSO FIXED (from earlier self-review):

  - Pearl claim softened: only the C51 Hold/Flat bias is directly
    attributed; other historic bugs had different mechanisms.
  - TFT entry removed from contract table — TFT is a Variable
    Selection Network (feature processor), not a Q-head. Replaced
    with generic "Future Q-head additions" placeholder.
  - Eval direction = argmax E[Q] explicitly flagged as a behavior
    change from current Boltzmann-with-tau (val_dir_dist will be
    more concentrated than current).
  - Phase 0.E budget reduced (1-2 hours, not 1 day) — synthetic
    bandit is ~50 lines of Rust, not full RL training loop.
  - Phase 0 enumerates existing checkpoints before training new one.
  - Architecture diagram parenthesis fixed.
  - Conviction implementation note: compute E[Q] once, reuse for
    conviction AND eval-mode argmax — no redundant computation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 23:51:17 +02:00
jgrusewski
1c63c32297 spec(dqn): reframe Phase 3 as direction-branch dead-code cleanup
User correctly challenged the "band-aid removal" framing. All fixes
shipped during the val-Flat-collapse investigation addressed real bugs
at their respective layers and should be preserved:

  - Kelly cap warm-branch (0c9d1ee39): post-decision physics layer.
    Thompson-independent. KEEP.
  - Train Return display + Sharpe annualization (non-tau parts of
    7a3d88646): display/metric layer. Thompson-independent. KEEP.
  - Direction Boltzmann tau-floor (tau part of 7a3d88646) +
    adaptive eps_dir floor (d54b49efc): gates inside direction-branch
    action selection. Phase 2 replaces direction-branch action
    selection wholesale (eps-greedy + Boltzmann → Thompson), so these
    direction-only code paths become structurally unreachable.

The latter two are NOT band-aids being removed because Thompson is
better. They are dead code being cleaned up because Thompson replaces
the surrounding mechanism. Magnitude/order/urgency branches keep their
existing eps-greedy + Boltzmann + tau-floor + EPS_FLOOR paths intact.

Reframed Phase 3 deliverable: "direction-branch dead-code cleanup"
with explicit rationale (per feedback_no_legacy_aliases.md and
feedback_no_partial_refactor.md). 0.5 day budget instead of 1.

Also clarified eval action selection: argmax of (E[Q_C51]+E[Q_IQN])/2
is correct. Bellman backup is a Q-learning UPDATE rule, not an
action-selection rule. Once Q is learned, optimal policy is greedy
argmax of learned Q. Online Bellman lookahead at eval would require
a forward model of market dynamics — not available, not standard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 23:41:05 +02:00
jgrusewski
5c325f8947 spec(dqn): pivot to Thompson sampling — distributional RL action selection
Revises the C51-bias spec after deeper review surfaced 12 design gaps,
of which 4 were critical:

  1. Train-only vs train+eval ambiguity — UCB at eval would conflate
     "model recommends Long" with "model is uncertain about Long",
     inflating reported edge. CRITICAL for trading where eval drives
     real capital decisions.
  2. Thompson sampling is more principled than UCB:
       - parameter-free (no κ to tune)
       - uses distribution directly without scalar reduction
       - naturally explore-exploit balanced via distribution shape
  3. c51_alpha is the wrong blend weight (it's C51-vs-MSE-warmup, not
     C51-vs-IQN). Equal-weight average of C51 and IQN samples is the
     structural choice — no tuned blend weight needed.
  4. The bias might be CORRECT BEHAVIOUR — model rationally choosing
     Flat when no edge has been discovered. Phase 0 must include a
     synthetic-edge test (controlled MDP with KNOWN positive Long
     expected value) to verify Thompson can discover edge if it exists.

Other gaps fixed:
  - Eval at argmax E[Q] (not Boltzmann, not Thompson)
  - Pearl wording broadened to cover ensembles + future methods
  - Ensemble Q-head added to aggregation contract table
  - Explicit caveat: NEVER extend Thompson to magnitude branch (would
    worsen existing magnitude saturation)
  - Phase 0.F uses CONVERGED checkpoint (≥30 epochs), not 2-epoch run
  - L4 long smoke (30 epochs, ~1 hour) added — Thompson edge discovery
    needs longer feedback loop than 5 epochs
  - Phase 3 explicitly removes eps-floor + tau-floor band-aids
    (Thompson replaces direction Boltzmann; band-aids become dead code)
  - Conviction stays E[Q]-based, not sample-based (avoid Kelly cap
    jitter from stochastic samples)

Architecture (Thompson only, no UCB):
  TRAINING: dir_idx = argmax(0.5 × (sample_C51(d) + sample_IQN(d)))
            magnitude/order/urgency: existing Boltzmann + ε-greedy
  EVAL:     dir_idx = argmax(0.5 × (E[Q_C51] + E[Q_IQN]))
            magnitude/order/urgency: existing Boltzmann (eval mode)

Direction-branch ε-greedy + Boltzmann are REMOVED — Thompson is the
exploration mechanism. No new GPU buffers; existing C51 atoms + IQN
quantiles passed to action_select.

5-7 days active work across 4 sub-plans; each gets its own
writing-plans cycle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 23:35:40 +02:00