ShadowDecision already carries the V3.4 5-factor breakdown (base_leverage,
dc_lev_mult, regime_size_mult, market_ob_mult, esof_size_mult), but the journal
row and CH DDL dropped it — so a DARK soak would record decisions WITHOUT the
factor decomposition that V3.4 exists to expose. Thread it through:
- 22_violet_decisions.sql: 5 additive Nullable(Float64) breakdown columns. NULL
on the legacy base-only path; populated once the launcher feeds live factor
planes. Note added: on a pre-existing table use ALTER ... ADD COLUMN instead of
the CREATE IF NOT EXISTS (no live table yet — VIOLET is DARK, never soaked).
- shadow_journal.py: DecisionRow gains the 5 Optional[float] fields (ge=0.0,
finite-guarded); journal() populates them via getattr(..., None) so the
base-only path and duck-typed reject tests stay NULL/rejected rather than
raising on attribute access.
- test_violet_shadow_journal.py: breakdown round-trips on the full path; NULL on
base-only; a negative multiplier is rejected at the row guard. The existing
DecisionRow-fields == DDL-columns parity test still holds with the new columns.
violet-only; no shared-file edits; no soak. 29 violet tests green
(7 journal + 22 engine/DDL-apply).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
22_violet_decisions.sql (dolphin_violet.violet_decisions, DDL-first) +
shadow_journal.py: VioletDecisionJournal validates each actuated ShadowDecision
via DecisionRow (V-TYPES, allow_inf_nan=False) before the CH sink -- malformed
dies at source, never at the spool head. NEVER an order. DecisionRow field set ==
DDL columns (asserted). 4 tests pass. Launcher wiring + DARK soak = operator step.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>