#!/usr/bin/env python3 """Live parity: Rust task_gate kernel vs the Python oracle (dichotomy wave #2479). Replays the frozen truth corpus PLUS seeded fuzz over every public function and branch. Timestamps are frozen (no-clock law — the kernel takes `now` as an argument), so nothing is masked. The facade reads RUNE_TG_CORE per call, so one process can flip between the pinned oracle (RUNE_TG_CORE=python) and the kernel (unset). """ from __future__ import annotations import json import os import random import sqlite3 import sys import tempfile from datetime import datetime, timezone from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src" / "rune")) import task_gate as tg # noqa: E402 FROZEN_NOW = "2026-10-11T23:00:00.123456+00:00" class FrozenDatetime(datetime): """No-clock law: the facade's `now` stamps are wall clock, not logic — freeze them so the two core runs stamp identically.""" @classmethod def now(cls, tz=None): return datetime(2026, 10, 11, 23, 0, 0, 123456, tzinfo=timezone.utc) tg.datetime = FrozenDatetime TRUTH_PATH = Path(__file__).resolve().parents[1] / ( "src/rust/task_gate/src/frozen_tg_truth.json") def call_core(core, fn, *args, **kwargs): old = os.environ.get("RUNE_TG_CORE") os.environ["RUNE_TG_CORE"] = core try: return getattr(tg, fn)(*args, **kwargs) finally: if old is None: os.environ.pop("RUNE_TG_CORE", None) else: os.environ["RUNE_TG_CORE"] = old def fresh_db(): tmp = tempfile.mkdtemp(prefix="tg_parity_") tg.DB_PATH = os.path.join(tmp, "task_gate.db") db = tg.connect() db.close() def read_rows(): db = sqlite3.connect(tg.DB_PATH) db.row_factory = sqlite3.Row rows = [dict(r) for r in db.execute( "SELECT * FROM instructions ORDER BY id").fetchall()] db.close() return rows def seed_rows(rows): fresh_db() db = tg.connect() for i, r in enumerate(rows, start=1): db.execute( "INSERT INTO instructions (id, instruction, pattern, count," " threshold, status, promoted_at, created_at, last_seen)" " VALUES (?,?,?,?,?,?,?,?,?)", ( r.get("id", i), r.get("instruction", "seed %d" % i), r.get("pattern", r"\bseed\b"), r.get("count", 1), r.get("threshold", 3), r.get("status", "observing"), r.get("promoted_at"), r.get("created_at", FROZEN_NOW), r.get("last_seen", FROZEN_NOW), ), ) db.commit() db.close() def scenario_record(rows_before, text): """Run record under both cores on identical pre-state; compare (return, rows_after).""" seed_rows(rows_before) py_ret = call_core("python", "record_instruction", text) py_rows = read_rows() seed_rows(rows_before) rs_ret = call_core("auto", "record_instruction", text) rs_rows = read_rows() ok = (json.dumps(py_ret, sort_keys=True) == json.dumps(rs_ret, sort_keys=True) and json.dumps(py_rows, sort_keys=True) == json.dumps(rs_rows, sort_keys=True)) return ok, py_ret, rs_ret, py_rows, rs_rows def scenario_ctx(rows, context): seed_rows(rows) py = call_core("python", "check_context", context) rs = call_core("auto", "check_context", context) return (json.dumps(py, sort_keys=True) == json.dumps(rs, sort_keys=True)), py, rs def scenario_list(rows): seed_rows(rows) py = call_core("python", "list_gates") rs = call_core("auto", "list_gates") return (json.dumps(py, sort_keys=True) == json.dumps(rs, sort_keys=True)), py, rs def scenario_promote(rows, gate_id): seed_rows(rows) py = call_core("python", "promote_gate", gate_id) py_rows = read_rows() seed_rows(rows) rs = call_core("auto", "promote_gate", gate_id) rs_rows = read_rows() return (json.dumps(py, sort_keys=True) == json.dumps(rs, sort_keys=True) and json.dumps(py_rows, sort_keys=True) == json.dumps(rs_rows, sort_keys=True)), py, rs, py_rows, rs_rows def scenario_hook(event): py = call_core("python", "handle_hook", event, {"ignored": True}) rs = call_core("auto", "handle_hook", event, {"ignored": True}) return (json.dumps(py, sort_keys=True) == json.dumps(rs, sort_keys=True)), py, rs # ── fuzz pools ───────────────────────────────────────────────────────── WORDS = ["always", "check", "the", "benchmark", "before", "building", "ship", "it", "do", "stop", "now", "café", "über", "v1_2", "test_it", "fix", "bug", "release", "deploy", "kraken", "ab", "cd", "research", "first", "wash", "spin", "gate", "task"] PATTERNS = [r"\bcheck\b", r"\bship\b", r"\bbenchmark\b", r"do\ it", r"\balways\b|\bcheck\b", r"\bcafé\b", r"\bresearch\b", r"\bgate\b|\btask\b", r"\bfirst\b"] STATUSES = ["observing", "active", "retired"] def rand_text(rng): n = rng.randint(1, 8) sep = rng.choice([" ", " ", " ", " ", "! ", " ("]) return sep.join(rng.choice(WORDS) for _ in range(n)) + rng.choice( ["", "!", "?", " (now)"]) def rand_rows(rng, max_rows=4): rows = [] for _ in range(rng.randint(0, max_rows)): rows.append({ "pattern": rng.choice(PATTERNS), "count": rng.randint(1, 5), "threshold": rng.randint(2, 4), "status": rng.choice(STATUSES), "instruction": rand_text(rng), "promoted_at": FROZEN_NOW if rng.random() < 0.3 else None, }) return rows def fuzz_cases(rng): cases = [] for _ in range(300): cases.append(("record", rand_rows(rng), rand_text(rng))) for _ in range(250): cases.append(("ctx", rand_rows(rng), rand_text(rng))) for _ in range(200): cases.append(("list", rand_rows(rng, 5), None)) for _ in range(150): cases.append(("promote", rand_rows(rng), rng.randint(1, 5))) for _ in range(50): cases.append(("hook", None, rand_text(rng))) return cases def main() -> int: failures = [] def check(label, ok, detail=""): if not ok: failures.append((label, detail)) print(f"FAIL {label}\n{detail}") # 1. frozen truth corpus (facade level, both cores already proven by # gen_task_gate_truth.py --replay; re-run kernel level here live) truth = json.loads(TRUTH_PATH.read_text())["cases"] n_truth = 0 for group, cases in truth.items(): for case in cases: if group == "record_instruction": inp = case["input"] ok, p, r, pr, rr = scenario_record( inp["instructions"], inp["text"]) elif group == "check_context": inp = case["input"] ok, p, r = scenario_ctx(inp["instructions"], inp["context"]) elif group == "list_gates": ok, p, r = scenario_list( case["rows_after"] and _rows_before(case)) elif group == "promote_gate": inp = case["input"] ok, p, r, pr, rr = scenario_promote( [{"pattern": r"\bx\b"}], inp["id"]) else: # handle_hook ok, p, r = scenario_hook(case["input"]["event"]) n_truth += 1 check(f"truth:{group}:{case['name']}", ok, f"py={json.dumps(p, sort_keys=True)[:300]}\n" f"rs={json.dumps(r, sort_keys=True)[:300]}") # 2. seeded fuzz rng = random.Random(2479) n_fuzz = 0 for kind, rows, arg in fuzz_cases(rng): n_fuzz += 1 label = f"fuzz:{kind}:{n_fuzz}" if kind == "record": ok, p, r, pr, rr = scenario_record(rows, arg) elif kind == "ctx": ok, p, r = scenario_ctx(rows, arg) elif kind == "list": ok, p, r = scenario_list(rows) elif kind == "promote": ok, p, r, pr, rr = scenario_promote(rows, arg) else: ok, p, r = scenario_hook(arg) check(label, ok, f"py={json.dumps(p, sort_keys=True)[:300]}\n" f"rs={json.dumps(r, sort_keys=True)[:300]}\n" f"rows={json.dumps(rows, sort_keys=True)[:200]}") total = n_truth + n_fuzz print(f"parity: {total - len(failures)}/{total} " f"({n_truth} truth + {n_fuzz} fuzz seed 2479)") return 1 if failures else 0 def _rows_before(case): """list_gates truth carries rows_after (the full pre-state — listing never mutates); replay it as the seed state.""" return case["rows_after"] if __name__ == "__main__": sys.exit(main())