"""VIOLET launcher shadow helpers for live BLUE factor sourcing. These helpers stay separate from the launcher module so they can be unit-tested without importing the full launcher import chain. """ from __future__ import annotations import logging import os LOGGER = logging.getLogger(__name__) def build_shadow_live_source( *, client_factory=None, selector_factory=None, source_factory=None, scan_history_factory=None, ): """Create the read-only BLUE live-factor mirror for the shadow path.""" if client_factory is None or selector_factory is None or source_factory is None or scan_history_factory is None: import hazelcast from .alpha_wrappers import VioletAssetSelector from .live_blue_source import LiveBlueScanHistory, source_live_blue_sizing_factors client_factory = client_factory or (lambda: hazelcast.HazelcastClient( cluster_name=os.environ.get("HZ_CLUSTER", "dolphin"), cluster_members=[os.environ.get("HZ_HOST", "localhost:5701")], )) selector_factory = selector_factory or VioletAssetSelector source_factory = source_factory or source_live_blue_sizing_factors scan_history_factory = scan_history_factory or LiveBlueScanHistory client = client_factory() return { "client": client, "scan_history": scan_history_factory(), "selector": selector_factory(), "live_source": source_factory, } def shadow_decision_step( shadow: dict, payload: dict, *, scan_number: int, now_ns: int, vel_div: float, vol_ok: bool, ) -> bool: """Run one shadow decision against the live BLUE factor plane.""" shadow["engine"].observe(payload, scan_number) live_source = shadow.get("live_source") factors = None if live_source is not None: live_result = live_source( shadow["client"], scan_history=shadow["scan_history"], selector=shadow["selector"], ) shadow["last_live_source"] = live_result factors = live_result.factors if factors is None: return False decision = shadow["engine"].decide( now_ns=now_ns, scan_number=scan_number, capital=shadow["capital"], vel_div=vel_div, vol_ok=vol_ok, factors=factors, ) if decision is None: return False return shadow["journal"].journal(decision, mono_ns=now_ns)