
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x138ba2210>

    @pytest.mark.unit
    def test_periodic_router_persists_decision_so_bus_handler_converges(monkeypatch):
        """Bug 175b5b8e49db5cc51935827e355ad010ebc4cb24 — the periodic router and the LIVE bus handler must
        converge on ONE decision per level onset. The seam is ``TideReasoningDecisions``: the periodic
        router (this code path) writes via ``store_reasoning``, and ``_load_level_event_decision`` reads
        it back when the bus event fires. With a stubbed reasoner answering LONG, the bus handler's read
        of the SAME (trading_day, event_et, kind) MUST return the same LONG."""
        from rtrader.services import market_state_etf as mse
        from rtrader.services.signal_sim_engine import LEVEL_SUPPORT_BREAK, LEVEL_SUPPORT_RECLAIM
    
        monkeypatch.setattr(mse, "_reasoning_execute_enabled", lambda: True)
        monkeypatch.setattr(mse, "_decision_cutoff_time", lambda: _time(11, 0))
        monkeypatch.setattr(mse, "confluence_score", lambda *a, **k: 3)
        monkeypatch.setattr(mse, "account_short_enabled", lambda nick: True)
    
        # Stub the LIVE reasoner — same seam the bus handler's reason_live_level_events calls.
        from rtrader.services import tide_etf_reasoning as TER
        monkeypatch.setattr(TER, "reason_for_level_event",
                            lambda day, ev, **k: {
                                "decision": "SHORT", "confidence": 0.7, "scale": 1.2,
                                "rationale": "(stub short)", "story": "stub",
                                "pattern": "SUPPORT-BREAK", "parse_failed": False})
    
        # Stub the Mongo path so store_reasoning writes to the in-memory FakeDB the bus handler reads.
        db = _FakeDB()
        monkeypatch.setattr(CEL, "_open_db", lambda: db)
        monkeypatch.setattr(CEL, "_close_db", lambda d: None)
    
        # (1) PERIODIC ROUTER consult — origin='backtest' (this is the SIM path).
        out = CEL._sleeve_consolidated_level_event(
            DAY, datetime(2026, 7, 1, 10, 16, 0, tzinfo=ET), "down", "yuchao",
            signal_source=LEVEL_SUPPORT_BREAK, origin="backtest", dbf=lambda: db)
        assert out["source"] == "llm" and out["decision"] == "SHORT" and out["leg"] == "bear"
    
        # (2) BUS HANDLER read — _load_level_event_decision reads the same TideReasoningDecisions row.
        # live_only=True → returns ONLY origin='live' rows. The backtest row is structurally excluded;
        # the bus handler's own consult (reason_live_level_events) writes the LIVE row independently.
        live_rows = db["TideReasoningDecisions"].find(
            {"trading_day": DAY.isoformat(), "kind": "support_break", "et": "10:16",
             "$or": [{"origin": "live"}, {"origin": {"$exists": False}}]})
>       assert live_rows == [], "backtest row must NOT leak through the live-only filter"
E       AssertionError: backtest row must NOT leak through the live-only filter
E       assert <test_combine...t 0x13c791550> == []
E         
E         Full diff:
E         - []
E         + <test_combined_engine_live._FakeCursor object at 0x13c791550>

tests/services/test_combined_engine_live.py:2763: AssertionError
