Files
rune/scripts/check_construction_mode_parity.py
T
Nick 662dfa8af1 feat(construction_mode): close PARTIAL gaps — dichotomy wave
Oracle src/rune/construction_mode.py 412 lines / 5 fns: 2/5 -> 5/5 per
scripts/dichotomy_audit.py (name-preserving match). The three audit-named
gaps land as ported Rust functions: ConstructionMode::to_dict (the ten-key
projection in the oracle's insertion order, construction_mode.py:155-167),
ConstructionMode::from_dict (known-keys-only + dataclass defaults, mode
required — TypeError message carried on the err envelope,
construction_mode.py:169-187), and _contains_any (renamed to the oracle's
name; lowering moved inside to mirror the Python signature). classify_mode
now routes every return through the ported ConstructionMode struct.

PARITY BUGS FIXED WITH PROOF (frozen truth from the live oracle):
1. Early-return defaults law — the override/NOT_CONSTRUCTION/empty-objective
   branches left target_project/project_authority/proposed_by_model at
   their dataclass defaults in the oracle but leaked kwargs through in the
   port (construction_mode.py:249-259/276/279-286). Frozen cases
   'what is a chord' + target_project/model_proposal and '' + kwargs pin
   the None/None/None shape.
2. model_proposal falsy collapse — 'model_proposal if model_proposal else
   None' (construction_mode.py:400): empty dict now collapses to None.
3. Evidence list rendering — Rust {:?} emitted double quotes where Python
   repr emits single ('affected surface known: [\'module:deploy_stage\']');
   py_repr_list/py_repr_tuple reproduce the oracle's repr, including the
   MODES tuple in the invalid-override ValueError text.
4. Lexicon set-iteration order — the oracle's lexicons are Python sets, so
   multi-hit evidence order is hash-random per process (dichotomy quirk);
   truth is frozen under PYTHONHASHSEED=0 (generator re-execs itself,
   memory_consolidation precedent) and the Rust const arrays reproduce
   the seed-0 order.

COLLATERAL (fix-first): construction_decision's frozen truth was frozen
with RUNE_CM_CORE unpinned, so its construction_mode trace captured the
old divergent Rust output (double-quoted evidence). gen_construction_decision_truth.py
now pins RUNE_CM_CORE=python (transitive-core law) + the seed; truth
regenerated (11 evidence lines corrected), byte-identical verify green,
cd battery 18/18.

Truth: scripts/gen_construction_mode_truth.py via the shared freeze_truth.py
harness + rig scripts/cm_freeze_seams (pins RUNE_CM_CORE +
RUNE_CONSTRUCTION_DECISION_CORE for the file-scope branch's transitive
classify_surface import; binds the cm_classify_to_dict seam — classify_mode
returns a dataclass instance, the frozen contract is its to_dict projection).
39 harness cases (36 classify + 3 to_dict receivers) + 5 from_dict
projections + 3 error shapes in the battery envelope; freeze_truth --verify
byte-identical. Battery replays the corpus VERBATIM via include_str!
(frozen_cm_truth.json + frozen_cm_truth.verify.json pinned together by
envelope_cases_match_verify_file) with FULL structural equality.

FFI: cm_to_dict / cm_from_dict doc-envelope externs; cm_loader gains
to_dict/from_dict bindings. Live parity scripts/check_construction_mode_parity.py:
100% — 36 classify + 3 to_dict + 5 from_dict + 4 error shapes, rust == oracle
== frozen (92 comparisons; oracle side subprocessed under PYTHONHASHSEED=0).
clippy --all-targets -D warnings clean (construction_mode +
construction_decision + construction_interface). Python suite green in BOTH
pins (RUNE_CM_CORE=python and delegation live): 138 passed each.

Audit: construction_mode 5/5 FULL (85/192 modules at FULL).
2026-10-14 05:09:26 +01:00

191 lines
6.7 KiB
Python

#!/usr/bin/env python3
"""check_construction_mode_parity.py — live parity: the Rust construction_mode
core (.so via cm_loader) vs the live Python oracle, 100% on the full gap
surface (dichotomy wave #2479 gap close).
Corpus: the frozen envelope (frozen_cm_truth.json — 36 classify cases, 3
to_dict receiver cases, 5 from_dict projections, 3 error shapes) replayed
against BOTH sides live, plus the invalid-override ValueError crash-parity
case (un-freezable through the shared harness; compared here).
The oracle side runs in a SUBPROCESS with PYTHONHASHSEED=0 — the oracle's
lexicons are Python sets, so multi-hit evidence order is hash-random per
process and the deterministic contract is the seed-0 order the Rust
arrays reproduce (memory_consolidation precedent; the truth generator
re-execs itself under the same seed).
Usage: python3 scripts/check_construction_mode_parity.py
Exit 0 iff every case matches on BOTH sides (and the frozen expected).
"""
from __future__ import annotations
import json
import os
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
from rune import cm_loader # noqa: E402
ENV = ROOT / "src" / "rust" / "construction_mode" / "src" / "frozen_cm_truth.json"
ORACLE_HELPER = r"""
import json, sys, os
sys.path.insert(0, %(src)r)
os.environ["RUNE_CM_CORE"] = "python"
os.environ["RUNE_CONSTRUCTION_DECISION_CORE"] = "python"
from rune import construction_mode as cm
CASES = json.load(sys.stdin)["CASES"]
out = {"classify": [], "to_dict": [], "from_dict": [], "errors": {}}
for case in CASES["classify"]:
d = cm.classify_mode(case["objective"], **case["kwargs"])
out["classify"].append(d.to_dict())
for inst in CASES["to_dict"]:
obj = cm.ConstructionMode.__new__(cm.ConstructionMode)
for k, v in inst.items():
setattr(obj, k, v)
out["to_dict"].append(obj.to_dict())
for d in CASES["from_dict"]:
out["from_dict"].append(cm.ConstructionMode.from_dict(d).to_dict())
try:
cm.ConstructionMode.from_dict({"evidence": []})
except TypeError as e:
out["errors"]["from_dict_missing_mode"] = str(e)
try:
cm.ConstructionMode.from_dict(123)
except TypeError as e:
out["errors"]["from_dict_non_dict"] = str(e)
_broken = cm.ConstructionMode.__new__(cm.ConstructionMode)
_broken.mode = "X"
try:
_broken.to_dict()
except AttributeError as e:
out["errors"]["to_dict_missing_attribute"] = str(e)
try:
cm.classify_mode("anything", explicit_override="MAGIC_MODE")
except ValueError as e:
out["errors"]["classify_invalid_override"] = str(e)
print(json.dumps(out))
"""
FAILS = [0]
def check(label: str, got, want) -> None:
if json.dumps(got, sort_keys=True) != json.dumps(want, sort_keys=True):
FAILS[0] += 1
print(f"MISMATCH {label}")
print(f" rust: {json.dumps(got, sort_keys=True)[:220]}")
print(f" oracle: {json.dumps(want, sort_keys=True)[:220]}")
else:
print(f"ok {label}")
def run_oracle(cases: dict) -> dict:
env = {**os.environ, "PYTHONHASHSEED": "0"}
code = ORACLE_HELPER % {"src": str(ROOT / "src")}
r = subprocess.run(
[sys.executable, "-c", code],
input=json.dumps({"CASES": cases}), capture_output=True, text=True,
env=env, timeout=120,
)
if r.returncode != 0:
print("ORACLE SUBPROCESS FAILED:", r.stderr[-500:])
sys.exit(1)
return json.loads(r.stdout)
def rust_classify(objective: str, kwargs: dict) -> dict:
payload = {"objective": objective}
for k, v in kwargs.items():
if v is not None:
payload[k] = v
return cm_loader._call(payload)
def main() -> int:
env = json.loads(ENV.read_text())
classify_cases = []
for case in env["cases"]:
if case["fn"] != "cm_classify_to_dict":
continue
classify_cases.append({
"objective": case["args"][0],
"kwargs": case["kwargs"],
"frozen": case["expected"],
})
to_dict_insts = [
{k: v for k, v in case["recv"].items() if k != "cls"}
for case in env["cases"] if case["fn"] == "ConstructionMode.to_dict"
]
to_dict_frozen = [
case["expected"]["result"]
for case in env["cases"] if case["fn"] == "ConstructionMode.to_dict"
]
from_dict_inputs = [c["args"] for c in env["from_dict_cases"]]
from_dict_frozen = [c["expected"] for c in env["from_dict_cases"]]
oracle = run_oracle({
"classify": [{"objective": c["objective"], "kwargs": c["kwargs"]}
for c in classify_cases],
"to_dict": to_dict_insts,
"from_dict": from_dict_inputs,
})
for i, case in enumerate(classify_cases):
got = rust_classify(case["objective"], case["kwargs"])
check(f"classify[{i}] {case['objective'][:40]!r}", got, oracle["classify"][i])
check(f"classify[{i}] frozen {case['objective'][:40]!r}", got, case["frozen"])
for i, inst in enumerate(to_dict_insts):
got = cm_loader.to_dict(inst)
check(f"to_dict[{i}] {inst['mode']}", got, oracle["to_dict"][i])
check(f"to_dict[{i}] frozen {inst['mode']}", got, to_dict_frozen[i])
for i, d in enumerate(from_dict_inputs):
got = cm_loader.from_dict(d)
check(f"from_dict[{i}]", got, oracle["from_dict"][i])
check(f"from_dict[{i}] frozen", got, from_dict_frozen[i])
shapes = env["error_shapes"]
for key, call in [
("from_dict_missing_mode", lambda: cm_loader.from_dict({"evidence": []})),
("from_dict_non_dict", lambda: cm_loader.from_dict(123)),
("to_dict_missing_attribute", lambda: cm_loader.to_dict({"mode": "X"})),
]:
try:
call()
print(f"MISMATCH {key}: rust returned ok, oracle raises")
FAILS[0] += 1
except RuntimeError as e:
check(f"error[{key}]", str(e), shapes[key])
# invalid override: the facade path raises ValueError before the FFI
# (cm_loader mirrors the oracle's own validation message).
try:
cm_loader.classify("anything", explicit_override="MAGIC_MODE")
print("MISMATCH classify_invalid_override: no ValueError")
FAILS[0] += 1
except ValueError as e:
check("error[classify_invalid_override]", str(e),
oracle["errors"]["classify_invalid_override"])
total = (2 * (len(classify_cases) + len(to_dict_insts) + len(from_dict_inputs))
+ 3 + 1)
if FAILS[0]:
print(f"\nPARITY FAILED: {FAILS[0]} mismatches")
return 1
print(f"\nPARITY OK: 100% — {len(classify_cases)} classify + "
f"{len(to_dict_insts)} to_dict + {len(from_dict_inputs)} from_dict "
f"+ 4 error shapes, rust == oracle == frozen ({total} comparisons)")
return 0
if __name__ == "__main__":
sys.exit(main())