parity: every public function of DriftCache ported branch-for-branch — compute_priority (log1p wash scaling capped at 1.0, rock/established boosts, Python truthiness coercion of None/0/0.5 fields), qualifies_ for_cache (the wash>=1 OR imp>=0.8 admission boundary), add (admission gate, stamping with verbatim rock_status incl. explicit None, the strict >200 eviction trigger), access (int/float access_count preservation, miss is a silent no-op), contains/get_priority (0.0 for unknown ids), update_from_store (row[0] or 0 / row[1] or 0.5 / row[2] or 'washable' coercions, deleted-row removal), get_stats (empty lattice, missing-priority 0.0, missing rock_status is washable). Dict-order law: entries are an insertion-ordered map (serde_json preserve_order) and the eviction sort is stable — ties evict in insertion order, exactly like the oracle's dict + sorted. No-clock law: now is a kernel argument; the sqlite read and the cache FILE IO stay in the facade. dc_loader mirrors broker_loader (doc- envelope FFI, cs_free_string, DC_RUST_LIB override, int<->str entry-id key round-trip); facade delegation with RUNE_DC_CORE=python pin and the legacy _ms_core chain preserved as second fallback. Frozen-truth freeze caught a REAL kernel diverge: serde_json's default float parser is 1 ulp off on priority floats — fixed-first with the float_roundtrip feature. Battery: 8 tests replay 48 frozen-oracle cases VERBATIM (include_str!). Live parity 48/48 facade + 45/45 kernel (load group is facade-only); clippy 0.
563 lines
20 KiB
Python
563 lines
20 KiB
Python
#!/usr/bin/env python3
|
|
"""gen_drift_cache_truth.py — freeze/replay parity truth for drift_cache
|
|
(dichotomy wave, #2479 tranche).
|
|
|
|
Freeze mode (default): exercises EVERY public function of the LIVE Python
|
|
oracle (src/rune/drift_cache.py) across every branch and records
|
|
(kernel_input, expected_kernel_output) pairs into
|
|
src/rust/drift_cache/src/frozen_dc_truth.json.
|
|
|
|
Replay mode (--replay): re-runs the SAME scenarios with the Rust core
|
|
delegation active and asserts both kernel-level (loader direct) and
|
|
facade-level (oracle methods delegate) parity against the frozen file.
|
|
|
|
Poisoned-truth law: freeze pins RUNE_DC_CORE=python AND
|
|
RUNE_MEMORY_CORE=python BEFORE any import (the module-level helpers carry
|
|
an older _ms_core delegation chain that must never poison the truth).
|
|
No-clock law: the wall clock is frozen by patching the datetime symbol in
|
|
drift_cache, so every kernel payload carries its own `now` and replay is
|
|
deterministic regardless of run date.
|
|
|
|
Boundary law: the sqlite read (update_from_store's per-id SELECT) and the
|
|
cache FILE IO (_load/_save, malformed-JSON reset) stay in the Python
|
|
facade — the kernel is a pure function of their results. The `load`
|
|
truth group is facade-only (no kernel symbol) and is skipped by the
|
|
kernel-level replay pass.
|
|
"""
|
|
import copy
|
|
import json
|
|
import os
|
|
import sqlite3
|
|
import sys
|
|
import tempfile
|
|
from datetime import datetime, timezone
|
|
|
|
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
TRUTH_PATH = os.path.join(REPO, "src", "rust", "drift_cache", "src", "frozen_dc_truth.json")
|
|
REPLAY = "--replay" in sys.argv
|
|
|
|
os.environ["RUNE_DC_CORE"] = "python" # poisoned-truth law (no-op pre-port, load-bearing post-port)
|
|
os.environ["RUNE_MEMORY_CORE"] = "python"
|
|
|
|
sys.path.insert(0, os.path.join(REPO, "src"))
|
|
sys.path.insert(0, os.path.join(REPO, "src", "rune"))
|
|
|
|
import drift_cache as oracle_mod # noqa: E402
|
|
|
|
if REPLAY:
|
|
from rune import dc_loader # noqa: E402 (kernel-level replay side)
|
|
|
|
FROZEN_UTC = datetime(2026, 10, 11, 12, 0, 0, 123456, tzinfo=timezone.utc)
|
|
FROZEN_NOW = FROZEN_UTC.isoformat()
|
|
|
|
|
|
class FrozenDatetime(datetime):
|
|
@classmethod
|
|
def now(cls, tz=None):
|
|
return FROZEN_UTC
|
|
|
|
|
|
oracle_mod.datetime = FrozenDatetime
|
|
|
|
|
|
class Case:
|
|
def __init__(self, name, fn):
|
|
self.name = name
|
|
self.fn = fn
|
|
|
|
|
|
def fresh_cache(entries_pre=None):
|
|
tmp = tempfile.mkdtemp(prefix="dc_truth_")
|
|
oracle_mod.CACHE_DIR = tmp
|
|
oracle_mod.CACHE_FILE = os.path.join(tmp, "drift_cache.json")
|
|
cache = oracle_mod.DriftCache()
|
|
if entries_pre is not None:
|
|
cache.entries = copy.deepcopy(entries_pre)
|
|
return cache
|
|
|
|
|
|
def stamped(priority, wash=0, rock="washable", access_count=0):
|
|
return {"priority": priority, "added_at": FROZEN_NOW, "last_accessed": FROZEN_NOW,
|
|
"access_count": access_count, "wash_count": wash, "rock_status": rock}
|
|
|
|
|
|
# ── compute_priority ─────────────────────────────────────────────────────────
|
|
|
|
def c_prio_default(rec):
|
|
rec("prio-default-empty-entry", {"entry": {}}, oracle_mod.compute_priority({}),
|
|
{"priority": oracle_mod.compute_priority({})})
|
|
|
|
|
|
def c_prio_wash20_imp1(rec):
|
|
e = {"wash_count": 20, "importance": 1.0}
|
|
rec("prio-wash20-imp1-washfactor-1", {"entry": e}, oracle_mod.compute_priority(e),
|
|
{"priority": oracle_mod.compute_priority(e)})
|
|
|
|
|
|
def c_prio_typical(rec):
|
|
e = {"wash_count": 7, "importance": 0.6}
|
|
rec("prio-wash7-imp06", {"entry": e}, oracle_mod.compute_priority(e),
|
|
{"priority": oracle_mod.compute_priority(e)})
|
|
|
|
|
|
def c_prio_rock(rec):
|
|
e = {"wash_count": 3, "importance": 0.7, "rock_status": "rock"}
|
|
rec("prio-rock-boost", {"entry": e}, oracle_mod.compute_priority(e),
|
|
{"priority": oracle_mod.compute_priority(e)})
|
|
|
|
|
|
def c_prio_established(rec):
|
|
e = {"wash_count": 3, "importance": 0.7, "rock_status": "established"}
|
|
rec("prio-established-boost", {"entry": e}, oracle_mod.compute_priority(e),
|
|
{"priority": oracle_mod.compute_priority(e)})
|
|
|
|
|
|
def c_prio_none_wash(rec):
|
|
e = {"wash_count": None, "importance": 0.9}
|
|
rec("prio-none-wash-coerced-0", {"entry": e}, oracle_mod.compute_priority(e),
|
|
{"priority": oracle_mod.compute_priority(e)})
|
|
|
|
|
|
def c_prio_none_imp(rec):
|
|
e = {"wash_count": 4, "importance": None}
|
|
rec("prio-none-imp-coerced-05", {"entry": e}, oracle_mod.compute_priority(e),
|
|
{"priority": oracle_mod.compute_priority(e)})
|
|
|
|
|
|
def c_prio_zero_imp(rec):
|
|
# falsy 0 → `or 0.5` coerces to 0.5
|
|
e = {"wash_count": 4, "importance": 0}
|
|
rec("prio-zero-imp-coerced-05", {"entry": e}, oracle_mod.compute_priority(e),
|
|
{"priority": oracle_mod.compute_priority(e)})
|
|
|
|
|
|
def c_prio_int_importance(rec):
|
|
e = {"wash_count": 2, "importance": 1}
|
|
rec("prio-int-importance", {"entry": e}, oracle_mod.compute_priority(e),
|
|
{"priority": oracle_mod.compute_priority(e)})
|
|
|
|
|
|
def c_prio_wash_beyond_20(rec):
|
|
# log1p scaling caps at 1.0 via min()
|
|
e = {"wash_count": 1000, "importance": 0.5}
|
|
rec("prio-wash1000-capped", {"entry": e}, oracle_mod.compute_priority(e),
|
|
{"priority": oracle_mod.compute_priority(e)})
|
|
|
|
|
|
def c_prio_unknown_status(rec):
|
|
e = {"wash_count": 2, "importance": 0.5, "rock_status": "muddy"}
|
|
rec("prio-unknown-status-no-boost", {"entry": e}, oracle_mod.compute_priority(e),
|
|
{"priority": oracle_mod.compute_priority(e)})
|
|
|
|
|
|
# ── qualifies_for_cache ──────────────────────────────────────────────────────
|
|
|
|
def _q_case(rec, name, entry):
|
|
rec(name, {"entry": entry}, oracle_mod.qualifies_for_cache(entry),
|
|
{"qualifies": oracle_mod.qualifies_for_cache(entry)})
|
|
|
|
|
|
def c_qual_below_both(rec):
|
|
_q_case(rec, "qual-wash0-imp05-denied", {"wash_count": 0, "importance": 0.5})
|
|
|
|
|
|
def c_qual_imp_boundary(rec):
|
|
_q_case(rec, "qual-imp-079-denied", {"wash_count": 0, "importance": 0.79})
|
|
|
|
|
|
def c_qual_imp_08_admitted(rec):
|
|
_q_case(rec, "qual-imp-08-admitted", {"wash_count": 0, "importance": 0.8})
|
|
|
|
|
|
def c_qual_wash_boundary(rec):
|
|
_q_case(rec, "qual-wash1-imp0-admitted", {"wash_count": 1, "importance": 0.0})
|
|
|
|
|
|
def c_qual_defaults(rec):
|
|
_q_case(rec, "qual-defaults-denied", {})
|
|
|
|
|
|
def c_qual_none_fields(rec):
|
|
_q_case(rec, "qual-none-fields-denied", {"wash_count": None, "importance": None})
|
|
|
|
|
|
def c_qual_proven(rec):
|
|
_q_case(rec, "qual-wash5-admitted", {"wash_count": 5, "importance": 0.3})
|
|
|
|
|
|
# ── add ──────────────────────────────────────────────────────────────────────
|
|
|
|
def _add_case(rec, name, entries_pre, entry_id, entry):
|
|
cache = fresh_cache(entries_pre)
|
|
cache.add(entry_id, entry)
|
|
admitted = entry_id in cache.entries
|
|
evicted = sorted(set(entries_pre) - set(cache.entries))
|
|
rec(name,
|
|
{"entries": entries_pre, "entry_id": entry_id, "entry": entry, "now": FROZEN_NOW},
|
|
None, {"entries": cache.entries, "admitted": admitted, "evicted": evicted})
|
|
|
|
|
|
def c_add_denied(rec):
|
|
_add_case(rec, "add-denied-unproven-unimportant", {}, 1,
|
|
{"wash_count": 0, "importance": 0.5})
|
|
|
|
|
|
def c_add_denied_important_but_unwashed(rec):
|
|
_add_case(rec, "add-denied-imp-below-08", {}, 2,
|
|
{"wash_count": 0, "importance": 0.5, "text": "x"})
|
|
|
|
|
|
def c_add_washable(rec):
|
|
_add_case(rec, "add-washable-admitted", {}, 3,
|
|
{"wash_count": 2, "importance": 0.5, "rock_status": "washable"})
|
|
|
|
|
|
def c_add_high_importance_unwashed(rec):
|
|
_add_case(rec, "add-important-unwashed-admitted", {}, 4,
|
|
{"wash_count": 0, "importance": 0.95})
|
|
|
|
|
|
def c_add_rock(rec):
|
|
_add_case(rec, "add-rock-stamped", {}, 5,
|
|
{"wash_count": 3, "importance": 0.7, "rock_status": "rock"})
|
|
|
|
|
|
def c_add_overwrites(rec):
|
|
pre = {7: stamped(0.9, wash=9, rock="rock", access_count=4)}
|
|
_add_case(rec, "add-overwrites-existing", pre, 7,
|
|
{"wash_count": 1, "importance": 0.4})
|
|
|
|
|
|
def c_add_rock_status_none(rec):
|
|
# explicit None rock_status is stored verbatim (entry.get returns None)
|
|
_add_case(rec, "add-rock-status-none-verbatim", {}, 8,
|
|
{"wash_count": 2, "importance": 0.5, "rock_status": None})
|
|
|
|
|
|
def c_add_boundary_200_no_evict(rec):
|
|
pre = {i: {"priority": (i + 1) / 200.0} for i in range(199)}
|
|
_add_case(rec, "add-at-200-no-evict", pre, 199,
|
|
{"wash_count": 20, "importance": 1.0})
|
|
|
|
|
|
def c_add_evict_lowest_20(rec):
|
|
pre = {i: {"priority": (i + 1) / 200.0} for i in range(200)}
|
|
_add_case(rec, "add-evicts-lowest-20", pre, 999,
|
|
{"wash_count": 20, "importance": 1.0, "rock_status": "rock"})
|
|
|
|
|
|
def c_add_evict_tie_stable_order(rec):
|
|
pre = {i: {"priority": 0.5} for i in range(196)}
|
|
for i in range(196, 200):
|
|
pre[i] = {"priority": 9.0}
|
|
_add_case(rec, "add-evict-tie-stable-insertion-order", pre, 999,
|
|
{"wash_count": 20, "importance": 1.0})
|
|
|
|
|
|
# ── access ───────────────────────────────────────────────────────────────────
|
|
|
|
def _access_case(rec, name, entries_pre, entry_id):
|
|
cache = fresh_cache(entries_pre)
|
|
cache.access(entry_id)
|
|
changed = entry_id in entries_pre
|
|
rec(name,
|
|
{"entries": entries_pre, "entry_id": entry_id, "now": FROZEN_NOW},
|
|
None, {"entries": cache.entries, "changed": changed})
|
|
|
|
|
|
def c_access_hit(rec):
|
|
pre = {11: stamped(0.5, access_count=3)}
|
|
_access_case(rec, "access-hit-reinforces", pre, 11)
|
|
|
|
|
|
def c_access_miss(rec):
|
|
pre = {11: stamped(0.5)}
|
|
_access_case(rec, "access-miss-noop", pre, 12)
|
|
|
|
|
|
def c_access_zero_count(rec):
|
|
pre = {13: stamped(0.5, access_count=0)}
|
|
_access_case(rec, "access-from-zero", pre, 13)
|
|
|
|
|
|
# ── contains / get_priority ──────────────────────────────────────────────────
|
|
|
|
def c_contains_hit(rec):
|
|
pre = {21: stamped(0.5)}
|
|
cache = fresh_cache(pre)
|
|
rec("contains-hit", {"entries": pre, "entry_id": 21},
|
|
cache.contains(21), {"contains": cache.contains(21)})
|
|
|
|
|
|
def c_contains_miss(rec):
|
|
cache = fresh_cache({})
|
|
rec("contains-miss", {"entries": {}, "entry_id": 21},
|
|
cache.contains(21), {"contains": cache.contains(21)})
|
|
|
|
|
|
def c_getprio_hit(rec):
|
|
pre = {31: stamped(0.625)}
|
|
cache = fresh_cache(pre)
|
|
rec("getprio-hit", {"entries": pre, "entry_id": 31},
|
|
cache.get_priority(31), {"priority": cache.get_priority(31)})
|
|
|
|
|
|
def c_getprio_miss(rec):
|
|
cache = fresh_cache({})
|
|
rec("getprio-miss-zero", {"entries": {}, "entry_id": 31},
|
|
cache.get_priority(31), {"priority": cache.get_priority(31)})
|
|
|
|
|
|
# ── update_from_store ────────────────────────────────────────────────────────
|
|
|
|
def make_db(rows):
|
|
db = sqlite3.connect(":memory:")
|
|
db.execute("CREATE TABLE entries (id INTEGER PRIMARY KEY, wash_count INTEGER,"
|
|
" importance REAL, rock_status TEXT)")
|
|
db.executemany("INSERT INTO entries VALUES (?,?,?,?)", rows)
|
|
return db
|
|
|
|
|
|
def _sync_case(rec, name, entries_pre, db_rows):
|
|
db = make_db(db_rows)
|
|
cache = fresh_cache(entries_pre)
|
|
rows = {}
|
|
for entry_id in list(cache.entries.keys()):
|
|
row = db.execute(
|
|
"SELECT wash_count, importance, rock_status FROM entries WHERE id = ?",
|
|
(entry_id,)).fetchone()
|
|
rows[str(entry_id)] = list(row) if row else None
|
|
cache.update_from_store(db)
|
|
rec(name, {"entries": entries_pre, "rows": rows}, None,
|
|
{"entries": cache.entries})
|
|
|
|
|
|
def c_sync_updates_priority(rec):
|
|
pre = {41: stamped(0.1, wash=1)}
|
|
_sync_case(rec, "sync-updates-priority-wash-rock", pre,
|
|
[(41, 15, 0.8, "rock")])
|
|
|
|
|
|
def c_sync_null_fields(rec):
|
|
pre = {42: stamped(0.9, wash=5, rock="rock")}
|
|
_sync_case(rec, "sync-null-row-fields-coerced", pre,
|
|
[(42, None, None, None)])
|
|
|
|
|
|
def c_sync_deleted_row_removed(rec):
|
|
pre = {43: stamped(0.5), 44: stamped(0.7)}
|
|
_sync_case(rec, "sync-deleted-row-removed", pre,
|
|
[(44, 2, 0.5, "washable")])
|
|
|
|
|
|
def c_sync_empty_cache(rec):
|
|
_sync_case(rec, "sync-empty-cache-noop", {}, [])
|
|
|
|
|
|
def c_sync_downgrade(rec):
|
|
pre = {45: stamped(1.0, wash=20, rock="rock")}
|
|
_sync_case(rec, "sync-downgrade-washable", pre,
|
|
[(45, 0, 0.1, "washable")])
|
|
|
|
|
|
# ── get_stats ────────────────────────────────────────────────────────────────
|
|
|
|
def _stats_case(rec, name, entries_pre):
|
|
cache = fresh_cache(entries_pre)
|
|
stats = cache.get_stats()
|
|
rec(name, {"entries": entries_pre}, stats, {"stats": stats})
|
|
|
|
|
|
def c_stats_empty(rec):
|
|
_stats_case(rec, "stats-empty", {})
|
|
|
|
|
|
def c_stats_mixed(rec):
|
|
pre = {
|
|
51: stamped(0.2, rock="rock"),
|
|
52: stamped(0.5, rock="established"),
|
|
53: stamped(0.9),
|
|
}
|
|
_stats_case(rec, "stats-mixed", pre)
|
|
|
|
|
|
def c_stats_all_washable(rec):
|
|
pre = {54: stamped(0.3), 55: stamped(0.4)}
|
|
_stats_case(rec, "stats-all-washable", pre)
|
|
|
|
|
|
def c_stats_missing_priority(rec):
|
|
pre = {56: {"rock_status": "rock"}, 57: stamped(0.8)}
|
|
_stats_case(rec, "stats-missing-priority-zero", pre)
|
|
|
|
|
|
def c_stats_missing_rock_status(rec):
|
|
pre = {58: {"priority": 0.5}}
|
|
_stats_case(rec, "stats-missing-rock-status-washable", pre)
|
|
|
|
|
|
# ── _load branches (facade-only: FILE IO stays in Python) ────────────────────
|
|
|
|
def c_load_missing(rec):
|
|
cache = fresh_cache()
|
|
rec("load-missing-file-empty", {"path": "missing"}, None,
|
|
{"entries": cache.entries})
|
|
|
|
|
|
def c_load_valid(rec):
|
|
tmp = tempfile.mkdtemp(prefix="dc_truth_")
|
|
oracle_mod.CACHE_DIR = tmp
|
|
oracle_mod.CACHE_FILE = os.path.join(tmp, "drift_cache.json")
|
|
pre = {61: stamped(0.5), 62: stamped(0.9, rock="rock")}
|
|
with open(oracle_mod.CACHE_FILE, "w") as f:
|
|
json.dump({str(k): v for k, v in pre.items()}, f, indent=2)
|
|
cache = oracle_mod.DriftCache()
|
|
rec("load-valid-int-keys", {"path": "valid"}, None,
|
|
{"entries": cache.entries})
|
|
|
|
|
|
def c_load_malformed(rec):
|
|
tmp = tempfile.mkdtemp(prefix="dc_truth_")
|
|
oracle_mod.CACHE_DIR = tmp
|
|
oracle_mod.CACHE_FILE = os.path.join(tmp, "drift_cache.json")
|
|
with open(oracle_mod.CACHE_FILE, "w") as f:
|
|
f.write("{not json")
|
|
cache = oracle_mod.DriftCache()
|
|
rec("load-malformed-reset", {"path": "malformed"}, None,
|
|
{"entries": cache.entries})
|
|
|
|
|
|
# ── driver ───────────────────────────────────────────────────────────────────
|
|
|
|
PRIO = [c_prio_default, c_prio_wash20_imp1, c_prio_typical, c_prio_rock,
|
|
c_prio_established, c_prio_none_wash, c_prio_none_imp, c_prio_zero_imp,
|
|
c_prio_int_importance, c_prio_wash_beyond_20, c_prio_unknown_status]
|
|
QUAL = [c_qual_below_both, c_qual_imp_boundary, c_qual_imp_08_admitted,
|
|
c_qual_wash_boundary, c_qual_defaults, c_qual_none_fields, c_qual_proven]
|
|
ADD = [c_add_denied, c_add_denied_important_but_unwashed, c_add_washable,
|
|
c_add_high_importance_unwashed, c_add_rock, c_add_overwrites,
|
|
c_add_rock_status_none, c_add_boundary_200_no_evict,
|
|
c_add_evict_lowest_20, c_add_evict_tie_stable_order]
|
|
ACCESS = [c_access_hit, c_access_miss, c_access_zero_count]
|
|
CONTAINS = [c_contains_hit, c_contains_miss]
|
|
GETPRIO = [c_getprio_hit, c_getprio_miss]
|
|
SYNC = [c_sync_updates_priority, c_sync_null_fields, c_sync_deleted_row_removed,
|
|
c_sync_empty_cache, c_sync_downgrade]
|
|
STATS = [c_stats_empty, c_stats_mixed, c_stats_all_washable,
|
|
c_stats_missing_priority, c_stats_missing_rock_status]
|
|
LOAD = [c_load_missing, c_load_valid, c_load_malformed]
|
|
|
|
GROUPS = [("compute_priority", PRIO), ("qualifies_for_cache", QUAL),
|
|
("add", ADD), ("access", ACCESS), ("contains", CONTAINS),
|
|
("get_priority", GETPRIO), ("update_entries", SYNC),
|
|
("get_stats", STATS), ("load", LOAD)]
|
|
|
|
# kernel symbol per group (None → facade-only group, skipped kernel-side)
|
|
KERNEL = {
|
|
"compute_priority": "dcc_compute_priority",
|
|
"qualifies_for_cache": "dcc_qualifies_for_cache",
|
|
"add": "dcc_add",
|
|
"access": "dcc_access",
|
|
"contains": "dcc_contains",
|
|
"get_priority": "dcc_get_priority",
|
|
"update_entries": "dcc_update_entries",
|
|
"get_stats": "dcc_get_stats",
|
|
"load": None,
|
|
}
|
|
|
|
# kernel-level replay: which slice of `expected` the unwrapped FFI value is
|
|
UNWRAP = {
|
|
"compute_priority": lambda w: w["priority"],
|
|
"qualifies_for_cache": lambda w: w["qualifies"],
|
|
"add": lambda w: w,
|
|
"access": lambda w: w,
|
|
"contains": lambda w: w["contains"],
|
|
"get_priority": lambda w: w["priority"],
|
|
"update_entries": lambda w: w["entries"],
|
|
"get_stats": lambda w: w["stats"],
|
|
}
|
|
|
|
|
|
def canon(x) -> str:
|
|
"""Content canonicalization: int dict keys become strings through the
|
|
JSON round-trip (the frozen file stores string keys), then a sorted
|
|
dump makes the comparison key-order independent."""
|
|
return json.dumps(json.loads(json.dumps(x)), sort_keys=True)
|
|
|
|
|
|
def main():
|
|
frozen = None
|
|
if REPLAY:
|
|
with open(TRUTH_PATH) as f:
|
|
frozen = json.load(f)["cases"]
|
|
|
|
groups = {}
|
|
for group, cases in GROUPS:
|
|
recs = []
|
|
|
|
def rec(name, inp, oracle_ret, expected, _r=recs):
|
|
entry = {"name": name, "input": inp, "return": oracle_ret,
|
|
"expected": expected}
|
|
if REPLAY:
|
|
old = next(c for c in frozen[group] if c["name"] == name)
|
|
for key in ("input", "return", "expected"):
|
|
got, want = entry[key], old[key]
|
|
if canon(got) != canon(want):
|
|
print(f"FACADE DIVERGE [{group}] {name} {key}")
|
|
print(" got: ", json.dumps(got, sort_keys=True, default=str)[:600])
|
|
print(" want:", json.dumps(want, sort_keys=True, default=str)[:600])
|
|
recs.append({"name": name, "diverged": key})
|
|
return
|
|
_r.append(entry)
|
|
|
|
for c in cases:
|
|
c(rec)
|
|
groups[group] = recs
|
|
|
|
total = sum(len(v) for v in groups.values())
|
|
if REPLAY:
|
|
facade_failures = sum(1 for v in groups.values() for c in v if "diverged" in c)
|
|
|
|
failures = 0
|
|
checked = 0
|
|
for group, cases in frozen.items():
|
|
sym = KERNEL[group]
|
|
if sym is None:
|
|
continue
|
|
for case in cases:
|
|
checked += 1
|
|
got = getattr(dc_loader, group)(**copy.deepcopy(case["input"]))
|
|
want = UNWRAP[group](case["expected"])
|
|
if canon(got) != canon(want):
|
|
failures += 1
|
|
print(f"KERNEL DIVERGE [{group}] {case['name']}")
|
|
print(" got: ", json.dumps(got, sort_keys=True)[:600])
|
|
print(" want:", json.dumps(want, sort_keys=True)[:600])
|
|
print(f"replay: facade {total - facade_failures}/{total}, "
|
|
f"kernel {checked - failures}/{checked}")
|
|
return 1 if (failures or facade_failures) else 0
|
|
|
|
doc = {
|
|
"frozen": datetime.now(timezone.utc).isoformat(),
|
|
"oracle": "src/rune/drift_cache.py",
|
|
"pin": "RUNE_DC_CORE=python + RUNE_MEMORY_CORE=python",
|
|
"note": "Frozen via scripts/gen_drift_cache_truth.py against the live "
|
|
"Python oracle; wall clock frozen at " + FROZEN_NOW +
|
|
". Battery embeds this VERBATIM. Group `load` is facade-only "
|
|
"(FILE IO stays in Python).",
|
|
"cases": groups,
|
|
}
|
|
os.makedirs(os.path.dirname(TRUTH_PATH), exist_ok=True)
|
|
with open(TRUTH_PATH, "w") as f:
|
|
# NO sort_keys: the entries maps are Python dicts whose INSERTION
|
|
# order is load-bearing (stable eviction tie-breaks) — sorting
|
|
# keys here would freeze a re-ordered kernel input.
|
|
json.dump(doc, f, indent=1)
|
|
f.write("\n")
|
|
print(f"frozen {total} cases → {os.path.relpath(TRUTH_PATH, REPO)}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|