# VIOLET Spec — Cadence Control Plane (per-action quantization) **Status:** BINDING for V3b (`prod/clean_arch/violet/cadence.py`). Authored 2026-06-13 from operator doctrine this session. Companion to the master charter (memory `violet_subsecond_rebuild_plan`, cadence-quantizer section) and `violet_v3_alpha_doctrine` (points #6, #8). This spec is the painstaking per-ACTION quantization layer the charter only sketched as a principle. --- ## 1. The inversion (why this layer exists) BLUE's clock **is** its architecture: every decision/action is denominated in 5–6 s scans. VIOLET inverts this — the architecture is the **event-driven / clockless (fastest-clock) reactor** (V0: `clock.py` mono_ns timebase + `DeadlineScheduler`). BLUE's scan-quantized behaviour is a **guest** hosted on that reactor. The scan cadence is a **quantization setting, not the architecture.** At first, every action is quantized at **Q = scan cadence** → bit-faithful BLUE replication (warts and all). Over time each action's Q can be **loosened independently** toward the reactor's faster clock. This is the certification-preserving path: champion params are 5 s-bar-denominated (BIBLE §22.3), so loosening is a deliberate, measured, per-action promotion — never a global flip. ## 2. Core rules 1. **Evaluate at fastest cadence; actuate at Q.** Every action is *evaluated* every reactor tick (the would-be action is computed and **shadow-logged** — this is the evidence trail). The action is only *actuated* when its quantization boundary Q is crossed. Shadow deltas (would-be-at-fast vs actuated-at-Q) are the data that justifies each loosening step. 2. **Adjustability is UNIVERSAL.** EVERY scan-governed activity carries its own tunable Q — entry, sizing, each exit reason (TP, catastrophic SL, ADVSL, v7), AND each input-plane consumption (scan, OBF, ExoF, EsoF, MARAS, ACB). No action is hardcoded at scan. SL merely **defaults** to a tighter Q; mechanically it is the same adjustable primitive as everything else. 3. **Per-action knobs are INDEPENDENTLY configurable.** Changing TP's Q must not touch entry's Q. Each action is its own registry entry. 4. **Knobs are SURFACED IN A CONTROL PLANE.** The registry is readable and writable at runtime for live inspection and tuning — not compile-time constants. HZ-backed at runtime (mirroring how BLUE reads `DOLPHIN_FEATURES["live_tp_threshold"]`); code defaults are the floor/fallback when the control plane is silent. 5. **Loosening is per-reason and measured.** Q steps down per action on its own schedule (e.g. 6s→3s→1s→500ms→100ms→50ms), each promotion gated on the shadow deltas from rule (1). SL-class first; TP later (TP at scan has desirable properties). ## 3. Per-action default Q table (initial state — all loosenable) | Action | Class | Default Q | Loosening intent | |---|---|---|---| | Catastrophic SL | exit/safety | **tight VIOLET / insta-exec** | sub-second from early (the versioned safety deviation) | | ADVSL | exit/safety | tight VIOLET (after min-bars gate) | sub-second early | | TP (mechanical) | exit | **scan** | loosen *cautiously* (desirable at scan) | | v7 discretionary exit | exit | scan | as-is first; per-reason later | | Entry | entry | scan | cautious (champion params 5s-denominated) | | Sizing (conviction) | sizing | scan | cautious (couples to entry) | | Scan plane consume | input | scan (~5–6s native) | n/a (source cadence) | | OBF plane consume | input | **~1s** (500ms purported / ~1s effective) | toward VIOLET clock — fastest service | | ExoF / EsoF / MARAS / ACB consume | input | scan | adjustable too, though sources are orders slower | Exit priority is INVARIANT regardless of Q: CATASTROPHIC/ADVSL > mechanical TP > discretionary (v7). A faster Q never lets a discretionary exit mask a mechanical one (the LINK −$1,248 bug). ## 4. Design (`cadence.py`) - **`Action`** — enum/identifier per row of §3 (CATASTROPHIC_SL, ADVSL, TP, V7_EXIT, ENTRY, SIZING, CONSUME_SCAN, CONSUME_OBF, CONSUME_EXF, CONSUME_ESOF, CONSUME_MARAS, CONSUME_ACB). - **`CadenceKnob`** (V-TYPES `StrictModel`-style, but mutable via the control plane) — per action: `q_ns` (actuation quantum; 0 ⇒ every reactor tick = "insta"), `evaluate_every_tick: bool` (default True), `enabled: bool`, `source: "default"|"control_plane"`. - **`CadenceControlPlane`** — the registry: `get(action) -> CadenceKnob`, `set(action, **overrides)` (independent per action), `snapshot() -> dict` (surfaced for inspection), `refresh_from(provider)` where `provider` reads the HZ map / env at runtime; code defaults seed it. `due(action, now_ns, last_actuation_ns) -> bool` (Q-boundary test, integrates with `DeadlineScheduler`). - **Evaluate/actuate split helper** — `should_actuate(action, now_ns) -> bool`; callers always evaluate, then consult this to actuate, and emit the shadow-delta telemetry on the gap. ## 5. Tests (V3b) - defaults match §3 table; SL defaults tighter than TP. - `set` on one action leaves all others unchanged (independence). - control-plane override beats default; absence falls back to default (floor). - `due`/`should_actuate` honors Q boundaries (insta Q=0 actuates every tick; scan Q actuates once per scan interval), property-tested with hypothesis. - evaluate-always invariant: evaluation count ≥ actuation count for every action. - exit-priority invariant unaffected by Q (a fast discretionary Q never preempts a mechanical exit in the ordering). ## 6. Related `violet_subsecond_rebuild_plan` (charter) · `violet_v3_alpha_doctrine` #6/#8 · `clock.py`/`DeadlineScheduler` (V0 substrate) · `decision_engine.py` (V3c consumer) · `prod/docs/TODO_TP_SCAN_CADENCE_BUGFIX.md` (TP-at-scan rationale).