""" Structured Observability — compact JSONL logging for every decision. Records: - Every planner decision with features and diagnostics - Every risk gate decision - Every venue action - Every fill/discrepancy - Replayable audit trail """ from __future__ import annotations import json import time from dataclasses import dataclass, field from typing import Any, Dict, List, Mapping, Optional from malkhut.state import MarketWorldState from malkhut.actions import FulfilmentAction, PlannedPolicy, RiskDecision @dataclass(frozen=True, slots=True) class DecisionRecord: """One decision in the audit trail.""" ts_ns: int symbol: str action_kind: str action_side: Optional[str] action_price: Optional[float] approved: bool risk_reason: str plan_latency_ns: int policy_version: str root_entropy: float sims: int features: Mapping[str, float] = field(default_factory=dict) diagnostics: Mapping[str, Any] = field(default_factory=dict) class ObservabilityLogger: """ Compact JSONL logger for every decision. One line per decision. Replayable audit trail. """ def __init__(self, log_path: str = "decisions.log") -> None: self._log_path = log_path self._decisions: list[DecisionRecord] = [] self._total_decisions = 0 def log_decision( self, state: MarketWorldState, planned: PlannedPolicy, decision: RiskDecision, plan_ns: int, features: Optional[Mapping[str, float]] = None, ) -> None: """Log a decision to the audit trail.""" record = DecisionRecord( ts_ns=state.ts_ns, symbol=state.venue.symbol, action_kind=planned.selected_action.kind.value, action_side=planned.selected_action.side.value if planned.selected_action.side else None, action_price=planned.selected_action.price_ticks_from_best if planned.selected_action else None, approved=decision.approved, risk_reason=decision.reason, plan_latency_ns=plan_ns, policy_version="live", root_entropy=planned.diagnostics.get("entropy", 0.0), sims=planned.diagnostics.get("sims", 0), features=features or {}, ) self._decisions.append(record) self._total_decisions += 1 # Write to JSONL try: with open(self._log_path, "a") as f: f.write(json.dumps({ "ts": record.ts_ns, "sym": record.symbol, "act": record.action_kind, "side": record.action_side, "app": record.approved, "risk": record.risk_reason, "lat_ns": record.plan_latency_ns, "entropy": round(record.root_entropy, 4), "sims": record.sims, }, separators=(",", ":")) + "\n") except OSError: pass @property def total_decisions(self) -> int: return self._total_decisions def get_recent(self, n: int = 10) -> List[DecisionRecord]: return self._decisions[-n:]