// rl_band_turnover_controller.cu — Phase 4-B adaptive turnover-target
// controller.
//
// Spec: docs/superpowers/specs/2026-06-03-no-transaction-band-architecture.md
// §3.1 Option (c) + §3.4 (Schulman-bounded adaptive controller).
//
// Reads (from ISV):
//   * RL_BAND_ENABLED_INDEX                   (799) — master gate
//   * RL_BAND_FRAC_NOT_MASKED_OBSERVED_INDEX  (812) — per-step raw signal
//                                                    written by
//                                                    `rl_band_frac_aggregate.cu`
//   * RL_BAND_TURNOVER_EMA_INDEX              (809) — EMA state (owned)
//   * RL_BAND_CONTROLLER_BOOT_DONE_INDEX      (810) — bootstrap latch (owned)
//   * RL_BAND_TURNOVER_TARGET_ADAPTIVE_INDEX  (811) — output (owned)
//
// Writes (to ISV):
//   * 809 — updated EMA (first-observation bootstrap or Wiener-α blend)
//   * 810 — latched 1.0 on first observation
//   * 811 — adaptive target, clamped to [TARGET_MIN, TARGET_MAX]
//
// Control law (per the Phase 4-B dispatch & spec §3.1 Option c):
//   Healthy frac_not_masked target ≈ 0.4 (40% of batches free to trade,
//   60% inside band). Asymmetric Schulman-bounded adapter:
//
//     if frac_not_masked_ema > OVER_THRESHOLD   (too much trading):
//         target *= TIGHTEN_RATE   (push narrower band → fewer trades)
//     elif frac_not_masked_ema < UNDER_THRESHOLD (band saturated):
//         target *= LOOSEN_RATE    (push wider band → more trading)
//     else:
//         healthy band — leave alone.
//     clamp(target, TARGET_MIN, TARGET_MAX).
//
// Per `pearl_bootstrap_must_respect_clamp_range`: bootstrap 0.05 ∈
// [TARGET_MIN=0.01, TARGET_MAX=0.20]; controller never snaps to a clamp
// boundary on its first emission.
//
// Per `pearl_dead_signal_resurrection_discipline`: the band's output gates
// its own input (mask → fewer trades → narrower band drift → mask). The
// asymmetric LOOSEN escape rate is faster than the TIGHTEN rate so the
// controller can recover when frac_not_masked drops below 0.2.
//
// Per `feedback_no_atomicadd`: single-thread launch (1×1×1), no atomics.
// Per `feedback_no_nvrtc`: pre-compiled cubin via build.rs.

#include <stdint.h>
#include <math.h>

#define RL_BAND_ENABLED_INDEX                    799
#define RL_BAND_TURNOVER_EMA_INDEX               809
#define RL_BAND_CONTROLLER_BOOT_DONE_INDEX       810
#define RL_BAND_TURNOVER_TARGET_ADAPTIVE_INDEX   811
#define RL_BAND_FRAC_NOT_MASKED_OBSERVED_INDEX   812

// Tuning constants (all dimensionless).
#define BAND_CTRL_EMA_ALPHA        0.02f   // Wiener-α — ~50-step horizon
#define BAND_CTRL_OVER_THRESHOLD   0.60f   // frac_not_masked above → too much trading
#define BAND_CTRL_UNDER_THRESHOLD  0.20f   // frac_not_masked below → band saturated
#define BAND_CTRL_TIGHTEN_RATE     0.97f   // −3 %/step (slow narrow)
#define BAND_CTRL_LOOSEN_RATE      1.05f   // +5 %/step (faster escape per resurrection discipline)
#define BAND_CTRL_TARGET_MIN       0.01f   // never below 1 % of batches trading
#define BAND_CTRL_TARGET_MAX       0.20f   // never above 20 % — keeps band materially active

extern "C" __global__ void rl_band_turnover_controller(float* isv) {
    // Single-thread kernel — launched (1,1,1)/(1,1,1).
    if (threadIdx.x != 0 || blockIdx.x != 0) return;

    // Master gate: when the band is disabled, leave all owned slots
    // untouched so Phase 3D bit-equality is preserved.
    const float enabled = isv[RL_BAND_ENABLED_INDEX];
    if (enabled <= 0.5f) return;

    const float frac_now  = isv[RL_BAND_FRAC_NOT_MASKED_OBSERVED_INDEX];
    const float boot_done = isv[RL_BAND_CONTROLLER_BOOT_DONE_INDEX];

    // ── EMA update (first-observation bootstrap pattern) ─────────────
    float ema;
    if (boot_done < 0.5f) {
        // Replace EMA with current observation; latch flag. Necessary
        // because slot 809 sentinel-zeroes at trainer init and blending
        // zero with the first real measurement (likely ≈ 0.5 at warm
        // band-start) would lag the controller by ~50 steps before it
        // sees a representative value.
        ema = frac_now;
        isv[RL_BAND_CONTROLLER_BOOT_DONE_INDEX] = 1.0f;
    } else {
        ema = (1.0f - BAND_CTRL_EMA_ALPHA) * isv[RL_BAND_TURNOVER_EMA_INDEX]
            + BAND_CTRL_EMA_ALPHA * frac_now;
    }
    isv[RL_BAND_TURNOVER_EMA_INDEX] = ema;

    // ── Target update — asymmetric Schulman-bounded adapter ──────────
    float target = isv[RL_BAND_TURNOVER_TARGET_ADAPTIVE_INDEX];
    if (ema > BAND_CTRL_OVER_THRESHOLD) {
        // Trading too much → tighten band (smaller target → smaller
        // frac_not_masked goal → narrower bands chase the goal).
        target *= BAND_CTRL_TIGHTEN_RATE;
    } else if (ema < BAND_CTRL_UNDER_THRESHOLD) {
        // Band saturated → loosen (escape with faster rate per
        // resurrection discipline).
        target *= BAND_CTRL_LOOSEN_RATE;
    }
    // Healthy band — leave target alone.

    // Clamp; bootstrap 0.05 sits strictly inside [0.01, 0.20].
    target = fmaxf(BAND_CTRL_TARGET_MIN,
                   fminf(target, BAND_CTRL_TARGET_MAX));
    isv[RL_BAND_TURNOVER_TARGET_ADAPTIVE_INDEX] = target;
}
