Spec prod/docs/VIOLET_SPEC__CADENCE_CONTROL_PLANE.md + cadence.py: every scan-governed action (entry/sizing/each exit reason/each input plane) carries an INDEPENDENT, runtime-tunable Q knob surfaced in a control plane (HZ-backed, code defaults as floor). Evaluate-every-tick / actuate-at-Q; SL defaults insta, TP/entry default scan, OBF ~1s — all loosenable per-action. 9 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.7 KiB
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
- 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.
- 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.
- Per-action knobs are INDEPENDENTLY configurable. Changing TP's Q must not touch entry's Q. Each action is its own registry entry.
- 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. - 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-TYPESStrictModel-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)whereproviderreads the HZ map / env at runtime; code defaults seed it.due(action, now_ns, last_actuation_ns) -> bool(Q-boundary test, integrates withDeadlineScheduler).- 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.
seton one action leaves all others unchanged (independence).- control-plane override beats default; absence falls back to default (floor).
due/should_actuatehonors 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).