Audit PARTIAL 7/13 -> FULL 13/13 (headline 74/192, fn 1358/2278). Ported: _need_dict (FFI need envelope, wired into cp_evaluate/cp_derive), _surface_text (evidence token union, set semantics). Renamed to exact Python names per the name-preserving law: _path_matches, _entity_matches, _obligation_when_hit (were path_matches/entity_matches/obligation_when_hit) and the evaluate_applicability _check closure — same bodies, exact names; precedent: structural_store _sorted/_ss_core, construction_decision _sc_core. Truth: frozen_cp_truth.json 70->96 cases (path_match/entity_match/ need_dict/surface_text/when_hit families, incl. re.split empty-quirk and truthy-not-True always cases); harness surface frozen_truth.json 18 cases (from_dict/revise stay in the bespoke battery — bare instances are not harness-serializable). Battery 14 tests replay all 96 VERBATIM. Verification (verification/summary.md, all exit 0): battery 14/14, clippy 0 warnings, harness verify 18/18, live parity 192/192 both pins (new check_construction_pattern_parity.py; class ops via cp_loader under the kernel pin), pytest 30/30 module + 20/20 consumers under RUNE_CP_CORE=python and auto.
194 lines
6.7 KiB
Python
194 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
|
"""check_construction_pattern_parity.py — live parity: construction_pattern
|
|
facade under BOTH core pins vs the frozen oracle truth (dichotomy wave).
|
|
|
|
Per pin (RUNE_CP_CORE=python and unset → Rust via cp_loader) it replays
|
|
every frozen case (frozen_cp_truth.json, 96 cases) through the LIVE
|
|
facade. The class surface (construct/revise) does not sit behind the
|
|
_core() seam — under the kernel pin those replay through cp_loader's
|
|
construct/revise (the cp_construct/cp_revise externs), proving the .so
|
|
accepts the same inputs and answers byte-identically.
|
|
|
|
Usage: python3 scripts/check_construction_pattern_parity.py
|
|
Exit 0 iff every case matches its frozen expected in BOTH pins.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import copy
|
|
import json
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
|
|
import rune.construction_pattern as cp # noqa: E402
|
|
from rune import cp_loader # noqa: E402
|
|
|
|
TRUTH = (
|
|
Path(__file__).resolve().parents[1]
|
|
/ "src" / "rust" / "construction_pattern" / "src" / "frozen_cp_truth.json"
|
|
)
|
|
|
|
FAILS = []
|
|
CHECKS = [0]
|
|
|
|
|
|
def call(pin: str, fn, *args, **kwargs):
|
|
old = os.environ.get("RUNE_CP_CORE")
|
|
os.environ["RUNE_CP_CORE"] = pin
|
|
try:
|
|
return fn(*args, **kwargs)
|
|
finally:
|
|
if old is None:
|
|
os.environ.pop("RUNE_CP_CORE", None)
|
|
else:
|
|
os.environ["RUNE_CP_CORE"] = old
|
|
|
|
|
|
def need_obj(d: dict):
|
|
return SimpleNamespace(
|
|
objective=d["objective"],
|
|
affected_surface=list(d["affected_surface"]),
|
|
constraints=dict(d["constraints"]),
|
|
)
|
|
|
|
|
|
def replay(op: str, inp: dict):
|
|
"""The oracle-path call for one case (kernel path = same facade fns;
|
|
construct/revise route through cp_loader under the kernel pin).
|
|
Inputs are deep-copied per call: the class revise() appends to a
|
|
revision_history list it shares with the caller (documented oracle
|
|
quirk) — without the copy the oracle-pin run would corrupt the case
|
|
for the kernel-pin run."""
|
|
inp = copy.deepcopy(inp)
|
|
if op == "construct":
|
|
return cp.ConstructionPattern(**dict(inp["kwargs"])).to_dict()
|
|
if op == "revise":
|
|
pattern = cp.ConstructionPattern.from_dict(dict(inp["pattern"]))
|
|
return pattern.revise(
|
|
reason=inp["reason"],
|
|
evidence=list(inp["evidence"]),
|
|
changes=dict(inp["changes"]) if inp.get("changes") is not None else None,
|
|
).to_dict()
|
|
if op == "propose":
|
|
if isinstance(inp["confidence"], list):
|
|
return [
|
|
cp.record_pattern_proposal(
|
|
inp["pattern_id"],
|
|
provider=inp["provider"],
|
|
model=inp["model"],
|
|
evidence=list(inp["evidence"]),
|
|
confidence=b,
|
|
)
|
|
for b in inp["confidence"]
|
|
]
|
|
return cp.record_pattern_proposal(
|
|
inp["pattern_id"],
|
|
provider=inp["provider"],
|
|
model=inp["model"],
|
|
evidence=list(inp["evidence"]),
|
|
confidence=inp["confidence"],
|
|
)
|
|
if op == "evaluate":
|
|
pattern = cp.ConstructionPattern.from_dict(dict(inp["pattern"]))
|
|
return cp.evaluate_applicability(
|
|
pattern, need_obj(inp["need"]), dict(inp["surface_result"]), dict(inp["authorities"])
|
|
)
|
|
if op == "derive":
|
|
pattern = cp.ConstructionPattern.from_dict(dict(inp["pattern"]))
|
|
return cp.derive_pattern_obligations(
|
|
pattern,
|
|
need_obj(inp["need"]),
|
|
dict(inp["surface_result"]),
|
|
dict(inp["authorities"]),
|
|
project_exceptions=list(inp.get("project_exceptions") or []),
|
|
)
|
|
if op == "combine":
|
|
return cp.combine_pattern_obligations(list(inp["derivations"]))
|
|
if op == "path_match":
|
|
return cp._path_matches(inp["signal"], inp["path"])
|
|
if op == "entity_match":
|
|
return cp._entity_matches(inp["signal"], inp["entity_id"])
|
|
if op == "need_dict":
|
|
return cp._need_dict(need_obj(inp["need"]))
|
|
if op == "surface_text":
|
|
return sorted(cp._surface_text(need_obj(inp["need"]), dict(inp["surface_result"])))
|
|
if op == "when_hit":
|
|
return list(
|
|
cp._obligation_when_hit(
|
|
dict(inp["obligation"]),
|
|
need_obj(inp["need"]),
|
|
dict(inp["surface_result"]),
|
|
dict(inp["authorities"]),
|
|
)
|
|
)
|
|
raise ValueError(f"unknown op {op}")
|
|
|
|
|
|
def replay_kernel_class_ops(op: str, inp: dict):
|
|
"""construct/revise through the live kernel externs (cp_loader)."""
|
|
inp = copy.deepcopy(inp)
|
|
if op == "construct":
|
|
return cp_loader.construct(dict(inp["kwargs"]))
|
|
if op == "revise":
|
|
payload = {
|
|
"pattern": dict(inp["pattern"]),
|
|
"reason": inp["reason"],
|
|
"evidence": list(inp["evidence"]),
|
|
}
|
|
if inp.get("changes") is not None:
|
|
payload["changes"] = dict(inp["changes"])
|
|
return cp_loader.revise(**payload)
|
|
raise ValueError(f"not a class op {op}")
|
|
|
|
|
|
def check(name: str, got, want):
|
|
CHECKS[0] += 1
|
|
g = json.dumps(got, sort_keys=True, default=str)
|
|
w = json.dumps(want, sort_keys=True, default=str)
|
|
if g != w:
|
|
FAILS.append(name)
|
|
print(f"FAIL {name}\n got: {g[:220]}\n want: {w[:220]}")
|
|
|
|
|
|
def run_case(c: dict, pin: str):
|
|
name = f"[{pin}] {c['name']}"
|
|
op, inp = c["op"], c["input"]
|
|
want = c["expected"]
|
|
try:
|
|
if pin == "kernel" and op in ("construct", "revise"):
|
|
got = call("auto", replay_kernel_class_ops, op, inp)
|
|
check(name, got, want)
|
|
return
|
|
got = call(pins[pin], replay, op, inp)
|
|
check(name, got, want)
|
|
except ValueError as e:
|
|
if isinstance(want, dict) and "error" in want:
|
|
check(name, str(e), want["error"])
|
|
else:
|
|
FAILS.append(name)
|
|
print(f"FAIL {name}: unexpected ValueError {e}")
|
|
except RuntimeError as e:
|
|
# kernel envelope errors (cp_loader) carry the oracle's exact text
|
|
if isinstance(want, dict) and "error" in want:
|
|
check(name, str(e), want["error"])
|
|
else:
|
|
FAILS.append(name)
|
|
print(f"FAIL {name}: unexpected RuntimeError {e}")
|
|
|
|
|
|
pins = {"oracle": "python", "kernel": "auto"}
|
|
|
|
if __name__ == "__main__":
|
|
if not cp_loader.available():
|
|
print("kernel .so not built — parity is vacuous without it")
|
|
sys.exit(1)
|
|
cases = json.loads(TRUTH.read_text())["cases"]
|
|
for c in cases:
|
|
for pin in ("oracle", "kernel"):
|
|
run_case(c, pin)
|
|
print(f"parity: {CHECKS[0] - len(FAILS)}/{CHECKS[0]} checks pass (both pins)")
|
|
sys.exit(1 if FAILS else 0)
|