# SP13 — Redefine Success for Predictive Skill (v3)

**Date**: 2026-05-04 (v3 — supersedes v2 in-place after architectural finding)

## v2 → v3 change (added during P0a.T3 dispatch)

P0a.T3 implementer's audit revealed `DirectionAction` enum doesn't exist — the action
space uses an 8-variant `ExposureLevel` (fused dir+mag) with cross-crate consumers
across 77 files / 200+ Hold sites. Eliminating Hold would cascade through 32+ test
files pinning the 8-variant invariant.

User insight (2026-05-04): "Why is Hold free anyway? MFT-style with Hold for when it's
really needed could be fine. Holding isn't free — infrastructure costs still money even
doing nothing. The model should learn to hold carefully."

v3 reframes: **Hold being FREE is the bug, not Hold itself**. Pricing Hold:
- Preserves legitimate MFT use cases (multi-bar conviction holds)
- Avoids 200+ site cross-crate cascade
- Tests a sharper hypothesis: "free Hold + CQL bias = lazy default; priced Hold forces deliberate use"
- Cuts P0a.T3 from ~250 LOC + 32-test cascade → ~120 LOC, no cross-crate refactor
- Replay buffer / fxcache compatibility preserved (no contract change)
- 4-way action space stays; HEALTH_DIAG val_dir_dist stays 4-bucket; no stale-doc
  cleanup needed

The data-investigation hypothesis ("data has signal, Hold hides it") is unchanged. The
test mechanism shifts from "remove Hold so policy must commit" to "price Hold so policy
must commit when no edge exists, can still hold deliberately when edge persists".
**Branch**: continues `sp11-reward-as-controlled-subsystem` (single coherent architectural lineage)
**Builds on**: SP11 (cap stability) + SP12 (event-driven density) + asymmetric loss aversion
**Anchor pearls**:
- `pearl_audit_unboundedness_for_implicit_asymmetry`
- `pearl_event_driven_reward_density_alignment`
- (NEW) `pearl_redefine_success_for_predictive_skill` — to be written post-validation
- (NEW) `pearl_skill_bonus_must_be_alpha_bounded` — to be written post-validation
- (NEW) `pearl_reward_quadrant_audit_required` — meta-pearl from v1 review

## v1 → v2 changes (reasons here for traceability)

| Issue | v1 | v2 |
|---|---|---|
| Alpha-vs-benchmark math broken — Long trades have alpha = −costs always (tautology against always-long benchmark) | reward = realized_pnl − benchmark_pnl_same_period | **Dropped.** Predictive-skill incentive carried by direction-skill bonus + correct-call asymmetry. Per-fold buy-and-hold delta tracked as HEALTH_DIAG metric only. |
| Phase 0 confounded Hold removal AND aux_w boost — couldn't tell which helped | Single P0 commit | **Split:** P0a Hold-only (cheap test of user hypothesis), P0b aux probe (only if 0a partial) |
| Skill bonus + luck-discount could produce positive reward for correct-direction expected-loser → reward gaming | α=β=1.0 raw constants | **Bounded:** `skill_bonus = sign × min(α_param, ratio × |alpha|)` with ratio=0.3 default. Worked examples below verify no negative-EV gaming. |
| 5-epoch gate too short for aux to converge → false-negative risk | 5-epoch absolute threshold | **10-epoch trajectory criterion:** dir_acc trending up AND final > 0.52 |
| Aux_w controller at hard cap forever in data-limited case → destabilises Q | `clamp(0.05, 1.5)` only | **Stagnation detector:** dual-EMA (fast/slow); if slow ≈ fast for ≥ N epochs, decay aux_w toward base |
| No worked examples of full reward composition | Inline formulas | **Worked-example matrix** for 4 trade-outcome quadrants (correct/wrong × winner/loser) |

## TL;DR

The reward function defines what "success" means to the model. SP1-SP12 lineage rewarded
**any positive bounded P&L** — including P&L from structural extraction (trail-stop
arithmetic on directionally-random bets at 46% win rate). SP13 rewards **predictive
skill specifically** via two surgical changes:

1. **Eliminate Hold action** — model must commit. Tests user's hypothesis that Hold's
   availability has been hiding directional signal across the entire SP1-SP12 lineage.
2. **Direction-skill bonus + correct-call asymmetry** — bounded explicit reward for
   predicting direction correctly, bounded penalty for being wrong, and discount on
   wrong-direction lucky wins. Mathematically calibrated so the model cannot game by
   entering expected-loser trades.

Plus three structural supports:

3. **Aux head regression → binary classification** on next-30-bar return sign, wired
   into direction Q-head input.
4. **ISV-driven aux_w controller** (replaces SP11 inverted formula) with deficit-driven
   amplification AND stagnation detector to prevent destabilization in data-limited case.
5. **Per-fold buy-and-hold delta** tracked as HEALTH_DIAG metric (NOT reward signal).

~600-750 LOC across 4 commits (P0a, P0b, B, C). Greenfield refactor — replay buffer
compatibility intentionally broken.

**Hypothesis under test** (user, 2026-05-04): directional signal IS in the data; ~46% WR
persists because Hold hides it. P0a smoke decides cleanly:
- WR climbs > 0.52 with Hold removed → ✅ confirmed, hypothesis was right
- WR stays flat at 0.46-0.50 → P0b adds aux amplification as second probe
- Both P0a + P0b flat → ❌ data limit, halt SP13

## Background — why current system fails

50-epoch validation `train-multi-seed-pmbwn` and 22-epoch SP12 v3 validation
`train-multi-seed-vd2v5`:

- val_dir_dist: hold=0.45 (lazy default), Long+Short=0.36 (active bets)
- win_rate=0.4651 (slightly below random for direction)
- profit_factor=1.24 (winners 24% bigger than losers)
- aux_w stuck at 0.09 (suppressed by SP11-era `0.1 × health × (1 − sharpe)` controller)
- aux next_bar_mse=3.83 (regression error suggests poor signal extraction OR aux never got real gradient)

**The mechanism**: model picks Hold ~half the time. Of the ~37% directional bets, only
46% are correct. Profitability comes from trail-stop arithmetic, not predictive skill.

## The architectural shift

| Current "success" | Proposed "success" |
|---|---|
| Maximize positive bounded P&L | Maximize bounded P&L + bounded direction-skill credit |
| Hold is safe default | Must commit (no Hold option) |
| Wrong direction + lucky exit = full reward | Wrong direction + win = 0.3× reward (luck discount) |
| Aux head suppressed when sharpe high | Aux amplified when WR below target, decayed when stagnant |
| 1-bar reward horizon (next-bar MSE) | 30-bar prediction horizon (sign classification) |

## The five changes

### Change 1: Price Hold (replaces v2's "Eliminate Hold")

**Current**: 4-way direction `{Short=0, Hold=1, Long=2, Flat=3}` × magnitude × ord × urg.
Hold has zero cost: model can pick it indefinitely with no penalty. Combined with CQL's
conservative Q-bias (pulls toward minimum-variance action), Hold becomes the safe
default ~45% of the time across SP1-SP12.

**New**: same 4-way action space; **Hold action incurs a per-bar cost**. Adaptive,
ISV-driven controller targets a Hold-rate (~20% — MFT-ish, deliberate use). Cost
self-balances:

```
At each bar, if action == Hold:
    reward[bar] -= ISV[HOLD_COST_INDEX]

Controller (per-step, after action selection):
    observed_hold_rate = ISV[HOLD_RATE_OBSERVED_EMA_INDEX]   /* fast EMA of Hold-pick frequency */
    target_hold_rate   = ISV[HOLD_RATE_TARGET_INDEX]         /* default 0.20 */
    excess             = max(0, observed_hold_rate - target_hold_rate)
    hold_cost          = HOLD_COST_BASE × (1 + 5 × excess)
    hold_cost          = clamp(hold_cost, HOLD_COST_BASE × 0.5, HOLD_COST_BASE × 5.0)
    ISV[HOLD_COST_INDEX] = hold_cost
```

When the model abuses Hold (rate > target), cost rises until Hold becomes uneconomic.
When Hold rate ≤ target, cost relaxes to baseline (just realistic carry). The model
learns to use Hold deliberately for high-conviction multi-bar positions.

**Action semantics preserved**:
- `Hold` (action 1) — keep current position; pays per-bar cost; legitimate for MFT
- `Flat` (action 3) — close to no position; pays close-slippage once, then near-zero infra
- `Long`/`Short` — open/keep directional position; pays carry implicitly via P&L

**Affected** (~120 LOC, additive, no contract change):
- 3 new ISV slots: `HOLD_COST_INDEX`, `HOLD_RATE_TARGET_INDEX`, `HOLD_RATE_OBSERVED_EMA_INDEX`
- Hold-rate observer: small GPU kernel writing `(action == Hold ? 1.0 : 0.0)` to scratch,
  Pearls A+D smoothing into ISV[HOLD_RATE_OBSERVED_EMA_INDEX]
- Hold-cost controller: 5-line formula in `training_loop.rs` (per-step, after observer)
- Reward composition: per-bar subtraction at `experience_kernels.cu` reward composition
  site when action == Hold
- 2 GPU oracle tests for the controller (target tracking, deficit response)

**No changes to** (vs v2 P0a): action space dimension, `ExposureLevel` enum, Q-head dim,
C51/IQN bucket loops, action packing, replay buffer encoding, HEALTH_DIAG val_dir_dist
buckets, SP5 Pearl 8 trail-dist, 30+ stale-doc audit. The 4-way world stays.

**Tension with `pearl_event_driven_reward_density_alignment`** (acknowledged):
the pearl warns against per-bar reward shaping creating exposure-positive bias toward
dense-signal optimization. Per-bar Hold cost is the inverse case — exposure-NEGATIVE
shaping that models real economic carry cost (broker fees, margin interest, opportunity
cost). It's faithful reward modeling, not artificial shaping. ISV-bounded by controller
target so cost can't run away. We KEEP the pearl's intent (don't add fake dense rewards
the policy will optimize against the objective) while pricing a real economic friction.

### Change 2: Direction-skill bonus (bounded)

```
if action ∈ {Long, Short}:
    correct = (sign(action) == sign(realized_30_bar_return))
    skill_cap = SKILL_BONUS_CAP_RATIO_INDEX × |alpha_capped|   # default 0.3 × |alpha|
    if correct:
        skill_bonus = +min(DIR_SKILL_BONUS_ALPHA_INDEX, skill_cap)
    else:
        skill_bonus = -min(DIR_SKILL_BONUS_BETA_INDEX, skill_cap)
else:  # action == Flat
    skill_bonus = 0
```

**Calibration check** (worked examples — α=β=1.0, ratio=0.3, SP12 cap [-10,+5]):

| Trade outcome | alpha (capped) | skill_bonus | luck-disc | reward | sane? |
|---|---|---|---|---|---|
| correct-Long-winner (big win) | +5 | +min(1.0, 1.5) = +1.0 | n/a | +6.0 | ✓ |
| correct-Long-winner (small win) | +0.5 | +min(1.0, 0.15) = +0.15 | n/a | +0.65 | ✓ |
| correct-Long-loser (small loss) | −0.5 | +min(1.0, 0.15) = +0.15 | n/a | **−0.35** | ✓ still negative |
| correct-Long-loser (big loss) | −10 | +min(1.0, 3.0) = +1.0 | n/a | **−9.0** | ✓ still very negative |
| wrong-Long-winner LUCKY (big) | +5 | −min(1.0, 1.5) = −1.0 | × 0.3 | (5−1)×0.3 = **+1.2** | ✓ reduced from naive +6 |
| wrong-Long-winner LUCKY (small) | +0.5 | −min(1.0, 0.15) = −0.15 | × 0.3 | (0.5−0.15)×0.3 = +0.105 | ✓ |
| wrong-Long-loser | −10 | −min(1.0, 3.0) = −1.0 | n/a | **−11.0** | ✓ full penalty + skill penalty |
| Flat in any direction | 0 | 0 | n/a | 0 | ✓ |

**Invariant verified**: every correct-direction loser has reward < 0; every
wrong-direction expected-loser has reward < 0. The model cannot game by entering
trades it expects to lose. ✓

### Change 3: Conditional correct-call asymmetry (luck-win discount)

```
if trade_pnl > 0 AND direction_was_wrong:
    reward *= LUCK_WIN_DISCOUNT_INDEX     /* default 0.3 */
# else: reward unchanged
```

Penalises specifically lucky wins (positive outcome despite wrong direction prediction).
Forces the model to optimize for correct prediction, not just positive outcome.

ISV-driven discount factor; default 0.3 starts strong, can be relaxed by ISV controller
if luck-win frequency drops below threshold.

### Change 4: Aux head — regression → binary classification

**Current**: `next_bar_return_mse` (regression). MSE 3.83 — worse than predicting zero.

**New**: `next_30_bar_return_sign` cross-entropy. 30 bars matches SP12 min_hold_target.

**Output**: 2-class softmax `[P(positive), P(negative)]`.

**Wired into direction Q-head**: aux signal `logit_diff = softmax[1] − softmax[0]` ∈
[−1, +1] is concatenated into direction Q-head input layer via ISV[`AUX_DIR_PREDICTION_INDEX`].
This explicitly couples predictive signal to action selection.

**No information leakage**: aux head trained with hindsight labels (sign of
price[t+30] − price[t], known at training time only). At inference, aux predicts using
state s_t alone — no future info accessible. Same as existing next_bar_mse pattern.

### Change 5: ISV-driven aux_w controller (with stagnation detector)

**Current** (SP11-era, broken):
```rust
let aux_w = (0.1 * learning_health * (1 - sharpe_tanh)).clamp(0.05, 0.3);
```
Lowers aux when sharpe is high — exactly wrong for the failure mode (high sharpe + low WR).

**New** (deficit-driven + stagnation):
```
target = ISV[TARGET_DIR_ACC_INDEX]                  # default 0.55
short  = ISV[AUX_DIR_ACC_SHORT_EMA_INDEX]           # fast EMA (α=0.3, ~3-epoch lookback)
long   = ISV[AUX_DIR_ACC_LONG_EMA_INDEX]            # slow EMA (α=0.05, ~20-epoch lookback)

deficit       = max(0, target - short)
improvement   = max(0, short - long)                # positive when short pulling ahead of long
stagnation    = max(0, 1 - improvement / max(0.005, deficit))
                # 1 = fully stagnant (no improvement); 0 = improving fast

aux_w = base * (1 + 5 * deficit) * (1 - 0.7 * stagnation)
aux_w = clamp(aux_w, base * 0.3, base * 3.0)        # never zero, never beyond 3× base
```

Rationale:
- When `short < target` and improving (`short > long`): aux_w rises strongly to push harder
- When `short < target` and stagnant (`short ≈ long`): aux_w decays back toward base — "we tried, it didn't help, stop destabilising Q"
- When `short ≥ target`: deficit = 0, aux_w = base. Steady-state.
- Hard floor at 0.3× base prevents complete suppression (different from SP11 inversion bug).

base = 0.5 in Phase 0b and beyond (SP11 base 0.05 was the suppression bug; lifting is
the fix, not a confounder, because the SP11 formula was structurally wrong).

**ISV slots used**: 372 (target), 373 (short EMA), 374 (long EMA). Both EMAs reset per fold.

## ISV slot allocation

11 new slots in `[372..383)` (8 from v2 + 3 added in v3 for Hold-pricing):

| Slot | Name | Purpose | Reset |
|---|---|---|---|
| 372 | `TARGET_DIR_ACC_INDEX` | Directional accuracy target (default 0.55) | static |
| 373 | `AUX_DIR_ACC_SHORT_EMA_INDEX` | Fast EMA of dir_acc (α=0.3) | per-fold |
| 374 | `AUX_DIR_ACC_LONG_EMA_INDEX` | Slow EMA of dir_acc (α=0.05), stagnation detector | per-fold |
| 375 | `AUX_DIR_PREDICTION_INDEX` | Aux head logit_diff per bar (concat into Q-head input) | per-bar |
| 376 | `DIR_SKILL_BONUS_ALPHA_INDEX` | Skill bonus magnitude (correct direction), default 1.0 | per-fold |
| 377 | `DIR_SKILL_BONUS_BETA_INDEX` | Skill bonus penalty (wrong direction), default 1.0 | per-fold |
| 378 | `LUCK_WIN_DISCOUNT_INDEX` | Lucky-win discount factor, default 0.3 | per-fold |
| 379 | `SKILL_BONUS_CAP_RATIO_INDEX` | Cap ratio: bonus ≤ ratio × \|alpha\|, default 0.3 | per-fold |
| 380 | `HOLD_COST_INDEX` | Per-bar Hold cost (controller output), default `HOLD_COST_BASE` | per-fold |
| 381 | `HOLD_RATE_TARGET_INDEX` | Target Hold-pick rate (default 0.20) | static |
| 382 | `HOLD_RATE_OBSERVED_EMA_INDEX` | Observed Hold-pick rate EMA (α=0.05) | per-fold |

Defaults: `HOLD_COST_BASE = 0.001` (small per-bar; cumulative across long Hold runs
becomes significant). `HOLD_RATE_TARGET_DEFAULT = 0.20`. Sentinel for slot 382: 0.0
(zero observed Hold-picks at fold start).

Bump `SP5_SLOT_END = 383`, `ISV_TOTAL_DIM = 383`.

T1 already staged with `SP5_SLOT_END = 380` (8 slots). T3 v3 work bumps to 383
additively (slots 380-382 are net-new in T3, no T1 rework).

**Slot 371 (was BENCHMARK_PNL_CUMULATIVE_INDEX in v1) — DROPPED.** Per-fold buy-and-hold
delta is computed at fold-end in HEALTH_DIAG emit (trivial: `price[fold_end] −
price[fold_start]`). No kernel, no slot, no reward dependency.

## Implementation phases

### Phase 0a — Hold-pricing + dir_acc instrumentation (1 atomic commit, additive)

**Purpose**: clean test of user's hypothesis. Hold-pricing forces the policy to commit
when no edge exists (CQL bias can no longer make Hold the lazy default), while
preserving Hold for legitimate MFT use. aux_w controller, skill bonus, aux
classification all UNCHANGED from current main (those land in P0b/B/C).

- 11 ISV slots (372-382): dir_acc tracking (372-374), aux prediction (375), Layer C
  reward composition slots (376-379, populated but unused until Layer C), and the v3
  Hold-pricing trio (380-382).
- T1 (already staged): 8 slots scaffolding (372-379), state-reset registry, constructor
  static-init.
- T2 (already staged): `aux_dir_acc_reduce_kernel` + 6 GPU oracle tests.
- **T3 v3** (replaces v2's Hold-elimination): adds 3 Hold-pricing slots (380-382),
  Hold-rate observer kernel, Hold-cost controller, per-bar reward subtraction site,
  2 GPU oracle tests for the controller. ~120 LOC, NO contract change.
- T4: wire dir_acc launch + HEALTH_DIAG `aux_dir_acc` + `hold_cost` + `observed_hold_rate`
  emit per epoch.
- T5: atomic commit + 10-epoch L40S smoke.

**Phase 0a smoke** (10-epoch L40S):
- ✅ **Pass**: `val_dir_acc[ep9] > val_dir_acc[ep1]` AND `val_dir_acc[ep9] > 0.52` AND
  `val_win_rate[ep9] > 0.51` AND `observed_hold_rate ≤ 0.30` → hypothesis confirmed;
  priced Hold + remaining policy commits to directions; proceed Layer B.
- ⚠️ **Partial** (one trending up, the other flat, OR Hold rate stuck > 0.40 despite
  controller escalation): proceed P0b for aux amplification.
- ❌ **All flat** (dir_acc < 0.51 AND win_rate < 0.50 AND Hold-rate stuck high): proceed
  P0b. If P0b also fails, halt SP13 — data lacks signal at this timescale, plan SP14.

10 epochs (not 5) because aux head regression has been suppressed for entire SP1-SP12
lineage — needs time at unchanged aux_w to show whether signal is recoverable. Cost: ~€0.60.

**Why this is a sharper test than v2 elimination**: v2 forced ALL Hold-equivalent
behavior (the model couldn't pick Hold). v3 lets the model still pick Hold, but only
when the perceived edge justifies the cost. If model converges to a low Hold-rate (≤
30%) AND WR climbs, both conditions pass — clean confirmation. If model maintains high
Hold-rate despite escalating cost AND WR stays flat, the data lacks signal (the model
prefers paying for Hold over committing because committing has no edge).

### Phase 0b — Add corrected aux_w controller (only if 0a partial/red, atomic commit)

- Replace SP11 inverted formula at `training_loop.rs:4103-4104` with deficit-driven +
  stagnation formula from Change 5
- Bump aux_w base 0.05 → 0.5
- ~80 LOC

**Phase 0b smoke** (10-epoch):
- Same gate criterion as P0a
- If still red: halt SP13. Update `project_hold_hides_directional_signal_hypothesis.md`
  status to DISCONFIRMED. Pivot to data-augmentation work (separate spec, SP14).

### Layer B — Aux head regression → classification (atomic, only on green Phase 0)

- Aux head output dim 1 → 2 + softmax + cross-entropy loss
- Label generation: `sign(price[t+30] − price[t])` as 0/1 class
- `aux_dir_acc_reduce_kernel` adapted: read 2-class softmax instead of regression scalar
- Wire `logit_diff = softmax[1] − softmax[0]` → ISV[375] → direction Q-head input concat
  (in_dim grows by 1)
- ~120 LOC

### Layer C — Reward composition (atomic)

- ISV slots 376, 377, 378, 379 + state-reset registry
- Direction-skill bonus device-inline function (`compute_direction_skill_bonus_bounded`)
- Luck-win discount device-inline function (`compute_luck_win_discount`)
- Replace SP12 reward composition at exit-event sites: keep SP12 asymmetric cap, ADD
  skill bonus, ADD luck discount
- HEALTH_DIAG: add `per_fold_alpha_vs_buy_and_hold = total_pnl − (price[fold_end] −
  price[fold_start])` — METRIC ONLY, not reward signal
- 12+ GPU oracle tests for new reward math (extends SP12 sp12_reward_math_tests scaffold)
- ~150 LOC

### Layer D — 30-epoch validation + close-out (no commit until green)

- 30-epoch L40S full validation
- 3-action SP12 control run for fair baseline (~€1, optional but recommended for clean
  before/after comparison)
- If WR ≥ 55% AND profit_factor improvement ≥ 15% over 3-action SP12 control →
  close-out commit: audit doc + 2 new pearls (`pearl_redefine_success_for_predictive_skill`,
  `pearl_skill_bonus_must_be_alpha_bounded`) + meta-pearl
  `pearl_reward_quadrant_audit_required`
- If WR plateaus 50-54%: tune horizon (next_60 instead of next_30) or skill bonus α/β,
  re-validate

## Validation targets

**Phase 0a smoke (10-epoch)** — primary gate:
- val_dir_acc trajectory: monotone non-decreasing OR final > start
- val_dir_acc[final] > 0.52
- val_win_rate[final] > 0.51
- Hold action eliminated: val_dir_dist sums to 1.0 over {Long, Short, Flat}
- No NaN/inf
- mean(weights) controller still ≈ 1.0
- aux next_bar_mse trajectory: not blowing up

**Phase 0b smoke (10-epoch)** — secondary gate (only if 0a partial):
- aux_w controller responding: aux_w[ep5] > base when deficit > 0.05
- Stagnation detector engaging: aux_w decays toward base when short EMA ≈ long EMA
- Same dir_acc + win_rate criteria as 0a

**Layer B 5-epoch smoke**:
- Aux CrossEntropy loss decreasing
- val_dir_acc continues at or above Phase 0 level
- aux signal feeding Q-head (verify by perturbing aux output → direction action distribution shifts)

**Full validation (30-epoch)**:
- val_dir_dist: roughly {Long: 0.4, Short: 0.4, Flat: 0.2} — model committing
- **win_rate ≥ 0.55** PRIMARY SUCCESS CRITERION
- profit_factor ≥ 1.5 (SP12 baseline 1.24 × 21% improvement floor for "skill-driven" claim)
- per_fold_alpha_vs_buy_and_hold > 0 sustained (the dropped reward signal, now metric)
- max_drawdown < 0.005

## Operating principles (NON-NEGOTIABLE)

- **`feedback_no_partial_refactor`**: P0a, P0b, B, C each single atomic commit
- **`feedback_isv_for_adaptive_bounds`**: target_dir_acc, aux_w base/cap, bonus α/β,
  luck-discount, skill-bonus-cap-ratio — ALL ISV-driven
- **`feedback_no_quickfixes`**: this IS the proper fix per pearls
- **`feedback_no_feature_flags`**: behavior change unconditional
- **End-to-end training**: NO pre-training of aux head
- **`pearl_audit_unboundedness_for_implicit_asymmetry`**: SP12 asymmetric cap [-10,+5]
  preserved
- **`pearl_event_driven_reward_density_alignment`**: skill bonus + luck discount fire
  only at trade-exit events, not per-bar
- **(NEW) Reward-quadrant audit**: any reward composition change must include the 4-
  quadrant table (correct/wrong × winner/loser) showing all signs and inequalities. This
  rule will become `pearl_reward_quadrant_audit_required` post-validation.

## Estimated cost

**Implementation**: ~10-12h (~700 LOC across P0a + P0b + B + C + D close-out)
**P0a smoke**: ~€0.60 (10-epoch L40S)
**P0b smoke** (if needed): ~€0.60
**Layer B smoke**: ~€0.30 (5-epoch)
**30-epoch validation**: ~€1.00
**3-action SP12 control** (recommended): ~€1.00

**Total best case** (P0a green, no P0b): ~€2.90 + 10-12h
**Total worst case** (P0a red, P0b green): ~€3.50
**Total disconfirm case** (both P0a + P0b red): ~€1.20 — cheapest learn

## Open questions

1. **Aux prediction horizon**: 30 bars matches SP12 min_hold_target. If 30-epoch
   validation shows partial WR climb (50-54%), Layer D iteration tries 60-bar horizon.
2. **Skill-bonus cap ratio**: 0.3 chosen so worst-case bonus contribution to reward at
   |alpha|=10 is ±1.0 — comparable to one tick of P&L. ISV-driven, can be tightened to
   0.2 if Layer B/C reveals reward gaming.
3. **Stagnation detection threshold**: 0.7 decay coefficient is heuristic. Worth probing
   in Layer C smoke (read aux_w trajectory).
4. **Whether to keep SP12 min-hold penalty**: SP12's `min_hold_factor` curriculum still
   applies. Spec recommends KEEP — it's orthogonal to skill incentive and prevents
   compulsive churn. Re-evaluate at Layer D close-out.

## Decision needed

Approve SP13 v2 spec → update implementation plan to match → dispatch P0a implementer
chain → 10-epoch smoke → branch on outcome.
