Files
rune/scripts/check_toolchain_parity.py
Nick f83396eecd feat(toolchain): port to FULL Rust — dichotomy wave
Contract BUILDER-2026-10-11T22:03:40Z-7aeb7a-factory-wave (#2479 tranche).
Faithful port of src/rune/toolchain.py: ToolchainConstraints known/unknown
classification, DecisionRecord normalization, _major_change,
resolve_toolchain (INSUFFICIENT_EVIDENCE gate, KEEP_EXISTING,
CHANGE_MAJOR, greenfield recommendation), CONSTRAINT_KEYS vocabulary.
Doc-envelope FFI (tl_* symbols, free via cs_free_string), ctypes loader
(tl_loader.py, TC_RUST_LIB override), facade delegation with
RUNE_TC_CORE=python pin. Frozen oracle truth: 43 cases across every
public function and branch (frozen_tc_truth.json), replayed VERBATIM by
branch_battery.rs (6 tests). Live parity 613/613 (truth + 570 fuzz,
seed 2479).
2026-10-12 00:37:26 +01:00

233 lines
9.0 KiB
Python

#!/usr/bin/env python3
"""Live parity: Rust toolchain kernel vs the Python oracle (dichotomy
wave #2479). Replays the frozen truth corpus PLUS seeded fuzz over every
public function and branch. The module is deterministic — no masking.
Both sides must agree on values AND on raising.
The facade reads RUNE_TC_CORE per call, so one process can flip between
the pinned oracle (RUNE_TC_CORE=python) and the kernel (unset).
"""
from __future__ import annotations
import json
import os
import random
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
LANGS = ["python", "rust", "typescript", "go", "zig", "", "C++",
"café-lang ☕", "r"]
KEYS = sorted([
"existing_language", "existing_runtime", "existing_framework",
"target_platform", "deployment", "performance_sensitive",
"latency_sensitive", "memory_constrained", "concurrency_model",
"interoperability", "gui_native", "expected_lifespan",
"user_constraints", "ecosystem_requirement"])
UNKNOWN_KEYS = ["wibble", "flurb", "custom_axis", "future_thing"]
VALS = ["linux", "server", "tokio", "svelte", True, False, None, 0, 1,
"", ["a", "b"], {"deep": True}, 2.5, "x" * 60]
KEYWORD_POOL = ["existing_language", "target_platform", "deployment",
"performance_sensitive", "latency_sensitive",
"memory_constrained", "gui_native", "wibble"]
def call_core(core, fn, *args, **kwargs):
from rune import toolchain as tc
old = os.environ.get("RUNE_TC_CORE")
os.environ["RUNE_TC_CORE"] = core
try:
return getattr(tc, fn)(*args, **kwargs)
finally:
if old is None:
os.environ.pop("RUNE_TC_CORE", None)
else:
os.environ["RUNE_TC_CORE"] = old
def toj(x):
return x.to_dict() if hasattr(x, "to_dict") else x
def constraints_of(core, kw):
return call_core(core, "ToolchainConstraints", **kw)
def run_pair(invoke):
"""invoke(core) runs the full call chain under one core pin. Returns
(ok, detail): ok True when values match, None when BOTH sides raised
(agreement on refusing), False on any divergence."""
outs = []
for core in ("python", "rust"):
try:
outs.append((toj(invoke(core)), None))
except Exception as e:
outs.append((None, repr(e)))
(py, py_err), (rs, rs_err) = outs
if (py_err is None) != (rs_err is None):
return False, f"py_err={py_err} rs_err={rs_err}"
if py_err is not None:
return None, f"both raised: {py_err}"
return py == rs, f"py={py!r} rs={rs!r}"
def fuzz_cases(rng):
out = []
# constraints classification
for _ in range(120):
kw = {}
for k in KEYWORD_POOL + rng.sample(UNKNOWN_KEYS, rng.randrange(0, 3)):
if rng.random() < 0.5:
kw[k] = rng.choice(VALS)
out.append(("constraints", kw))
# constraints_get
for _ in range(60):
kw = {rng.choice(KEYS + UNKNOWN_KEYS): rng.choice(VALS)}
key = rng.choice(list(kw) + ["missing_key"])
out.append(("constraints_get", kw, key,
rng.choice([None, "dflt", 0, False, ["x"]])))
# decision_record normalization
for _ in range(80):
fields = dict(
selected_language=rng.choice(LANGS),
runtime=rng.choice(["", "tokio", None]),
framework=rng.choice(["", "svelte", None]),
libraries=rng.choice([None, [], ["b", "a"], ["z", "a", "m"]]),
build_system=rng.choice(["", "cargo"]),
alternatives=rng.choice([None, [], [{"language": "go"}]]),
decisive_constraints=rng.choice([None, [], ["z", "a"]]),
rationale=rng.choice(["", "measured 10x"]),
evidence_refs=rng.choice([None, [], ["e2", "e1"]]),
confidence=rng.choice([0.0, 0.4, 0.5, 0.7, 0.8, 0.85]),
user_approval_required=rng.choice([True, False]),
resolution=rng.choice(["", "KEEP_EXISTING", "CHANGE_MAJOR",
"INSUFFICIENT_EVIDENCE"]),
)
fields = {k: v for k, v in fields.items() if rng.random() < 0.85}
out.append(("decision_record", fields))
# major_change
for _ in range(60):
out.append(("major_change", {"existing_language": rng.choice(LANGS)},
rng.choice(LANGS)))
# resolve_toolchain — every branch reachable
for _ in range(250):
kw = {}
if rng.random() < 0.8:
kw["existing_language"] = rng.choice(LANGS)
for k in ("existing_runtime", "existing_framework"):
if rng.random() < 0.3:
kw[k] = rng.choice(["tokio", "node", ""])
for k in ("target_platform", "deployment", "performance_sensitive",
"latency_sensitive", "memory_constrained", "gui_native"):
if rng.random() < 0.3:
kw[k] = rng.choice(VALS)
rkw = {}
if rng.random() < 0.6:
rkw["requested_language"] = rng.choice(
[l for l in LANGS if l] + [None])
if rng.random() < 0.4:
rkw["evidence_refs"] = rng.choice([[], ["b", "a"], ["only"]])
if rng.random() < 0.4:
rkw["performance_evidence"] = rng.choice([
{"significant_benefit": True, "basis": "measured"},
{"significant_benefit": False},
{"significant_benefit": None},
{}, {"other": 1},
])
out.append(("resolve_toolchain", kw, rkw))
return out
def main() -> int:
from rune import toolchain as tc
from rune import tl_loader
if not tl_loader.available():
print("Rust core not built — run cargo build --release in src/rust/toolchain")
return 2
total = passed = agreed_errors = 0
failures: list[str] = []
def record(label, ok, detail=""):
nonlocal total, passed, agreed_errors
total += 1
if ok is None: # both sides raised — agreement on refusal
agreed_errors += 1
passed += 1
elif ok:
passed += 1
else:
failures.append(f"{label}: {detail}")
def resolve_invoke(rkw):
return lambda c: call_core(
c, "resolve_toolchain",
constraints_of(c, rkw[0]), **rkw[1]).to_dict()
# 1. frozen truth corpus, replayed live on BOTH cores
truth = json.loads((Path(__file__).resolve().parents[1] / "src" / "rust" /
"toolchain" / "src" / "frozen_tc_truth.json").read_text())
for fn, cases in truth["cases"].items():
for case in cases:
name = f"truth:{fn}:{case['name']}"
inp = case["input"]
if fn == "constraint_keys":
py = sorted(tc.CONSTRAINT_KEYS)
rs = tl_loader.constraint_keys()
record(name, py == rs, f"py={py} rs={rs}")
elif fn == "constraints":
record(name, *run_pair(
lambda c, i=inp: constraints_of(c, i["kwargs"]).to_dict()))
elif fn == "constraints_get":
record(name, *run_pair(
lambda c, i=inp: constraints_of(c, i["known"])
.get(i["key"], i["default"])))
elif fn == "decision_record":
record(name, *run_pair(
lambda c, i=inp: call_core(c, "DecisionRecord", **i).to_dict()))
elif fn == "major_change":
record(name, *run_pair(
lambda c, i=inp: call_core(
c, "_major_change", constraints_of(c, i["known"]),
i["language"])))
elif fn == "resolve_toolchain":
rkw = {k: v for k, v in inp.items()
if k in ("requested_language", "evidence_refs",
"performance_evidence")}
record(name, *run_pair(resolve_invoke((inp["known"], rkw))))
# 2. seeded fuzz (kernel core vs freshly-pinned oracle per case)
rng = random.Random(2479)
for case in fuzz_cases(rng):
fn = case[0]
name = f"fuzz:{fn}"
if fn == "constraints":
ok, d = run_pair(lambda c, i=case[1]: constraints_of(c, i).to_dict())
elif fn == "constraints_get":
_, kw, key, default = case
ok, d = run_pair(lambda c, k=kw, key=key, df=default:
constraints_of(c, k).get(key, df))
elif fn == "decision_record":
ok, d = run_pair(lambda c, i=case[1]:
call_core(c, "DecisionRecord", **i).to_dict())
elif fn == "major_change":
_, kw, language = case
ok, d = run_pair(lambda c, k=kw, l=language: call_core(
c, "_major_change", constraints_of(c, k), l))
else: # resolve_toolchain
_, kw, rkw = case
ok, d = run_pair(resolve_invoke((kw, rkw)))
record(name, ok, f"case={case} {d}")
print(f"parity {passed}/{total} ({agreed_errors} agreed-error cases)")
if failures:
for f in failures[:12]:
print("FAIL", f[:400])
return 1
return 0
if __name__ == "__main__":
sys.exit(main())