"""Ledger + candidate_scores writers. Log EVERY bar-clearer from day one (§6.6 denominator). date_logged = as_of (backtest rows carry historical dates so lead-time math is correct). The discourse_metric JSON is FROZEN here at log time — the resolver (separate forward pass) never edits it. Grant's rating lives in human_evaluations; the model never reads it pre-log (§6.7). """ from __future__ import annotations import hashlib import json def _sig_id(scorer: str, key: str, as_of: str) -> str: return "sig_" + hashlib.sha1(f"{scorer}|{key}|{as_of}".encode()).hexdigest()[:16] def _score_id(scorer: str, key: str, as_of: str) -> str: return hashlib.sha1(f"cs|{scorer}|{key}|{as_of}".encode()).hexdigest() def record_candidate_score(conn, result: dict, as_of: str, evidence: bool, promotion: bool) -> None: key = result.get("node_id") or result.get("conviction_id") or result.get("topic_canonical") or "" conn.execute( """INSERT OR REPLACE INTO candidate_scores (score_id, scorer, as_of, topic_canonical, node_id, conviction_id, score, cleared_evidence_bar, cleared_promotion_bar, inputs_json) VALUES (?,?,?,?,?,?,?,?,?,?)""", (_score_id(result["scorer"], key, as_of), result["scorer"], as_of, result.get("topic_canonical"), result.get("node_id"), result.get("conviction_id"), result["score"], int(evidence), int(promotion), json.dumps(result["inputs"])[:8000]), ) conn.commit() def log_candidate(conn, *, scorer: str, as_of: str, ledger_type: str, proposition: str, discourse_metric: dict, origin_conviction_id=None, origin_node_id=None) -> str: key = origin_node_id or origin_conviction_id or proposition signal_id = _sig_id(scorer, key, as_of) dm = {**discourse_metric, "scorer": scorer} conn.execute( """INSERT OR IGNORE INTO ledger (signal_id, type, proposition, date_logged, discourse_metric, model_confidence, origin_conviction_id, origin_node_id) VALUES (?,?,?,?,?,?,?,?)""", (signal_id, ledger_type, proposition[:1000], as_of, json.dumps(dm)[:8000], None, origin_conviction_id, origin_node_id), ) conn.commit() return signal_id