Files
rune/scripts/check_structural_store_parity.py
T
Nick 201530bfc4 feat(structural_store): close PARTIAL gaps — dichotomy wave
Port the 21 audit-missing functions name-preserving to the existing Rust
crate (gap.rs: _ss_core probe, _sorted, new_doc, _redact_secrets+_is_secret_key,
save, canonical_digest, snapshot, alias, spine_uri, resolve_uri,
_doc_authorities, authority_health, bind_task, task_entities, impact_frontier,
record_observed_impact, predicted_vs_observed, structural_events, diff,
dedupe_observations), 19 doc-envelope FFI ss_* entry points, ctypes loader
mirroring broker_loader.py, facade delegation with RUNE_STRUCTURAL_CORE=python
pin. Fix-first: promote ordering (unknown-id silent), alias-aware entity
resolution, resolve_status verbatim-status + pythonic conflicts, supersede
add_relationship law, authority_failed observation, SCHEMA_VERSION 1.

Frozen truth: 86 cases generated against the live oracle (pinned clock,
deep-copied inputs), embedded verbatim in cfg(test) battery. Live parity
100%: 4387 checks, 0 failures (frozen 86 + fuzz 4300 + live 1).

Contract BUILDER-2026-10-13T00:32:22Z-bc3aae-factory-gap-ss.
2026-10-13 10:25:49 +01:00

453 lines
20 KiB
Python

#!/usr/bin/env python3
"""Live parity: structural_store Rust kernels vs Python oracle (dichotomy
wave, contract BUILDER-2026-10-13T00:32:22Z-bc3aae-factory-gap-ss). Layers:
1. FROZEN — the frozen truth corpus (frozen_ss_truth.json, generated by
scripts/gen_structural_store_truth.py against the LIVE oracle with
RUNE_STRUCTURAL_CORE=python pinned) replayed through the ctypes FFI,
case by case, deep-copied inputs.
2. FUZZ — 300 seeded random docs (seed 2479) per kernel: oracle floor
vs FFI, exact compare; save() compared BYTE-EXACT on real files.
3. LIVE — dual-mode facade battery: the same facade calls executed with
RUNE_STRUCTURAL_CORE=python (oracle floor) and with the Rust core live;
results must be identical (timestamps masked).
Exit 0 only at 100% parity on the gap-closing functions.
"""
import calendar
import copy
import json
import os
import random
import string
import sys
import tempfile
import time as _time
import types
from pathlib import Path
os.environ["RUNE_STRUCTURAL_CORE"] = "python" # oracle floor for generation
REPO = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO / "src"))
from rune import structural_store as ss # noqa: E402
from rune import ss_loader # noqa: E402
FIXED_NOW = "2026-10-13T01:02:03Z"
FIXED_EPOCH = calendar.timegm(_time.strptime(FIXED_NOW, "%Y-%m-%dT%H:%M:%SZ"))
ss.now = lambda: FIXED_NOW
ss.time = types.SimpleNamespace( # snapshot() stamps straight off time.*
gmtime=lambda: _time.gmtime(FIXED_EPOCH),
strftime=lambda fmt, t=None: _time.strftime(fmt, t if t is not None else _time.gmtime()),
)
TRUTH = json.loads((REPO / "src" / "rust" / "structural_store" / "src"
/ "frozen_ss_truth.json").read_text())
fails: list[str] = []
counts = {"frozen": 0, "fuzz": 0, "live": 0}
def fail(layer, name, detail):
fails.append(f"[{layer}] {name}: {detail}")
print(f"FAIL[{layer}] {name}: {detail}")
if len(fails) <= 3:
import difflib
w, _, g = detail.partition(" got=")
want_s = w[5:] if w.startswith("want=") else w
a = want_s.replace(r"\u", "\\u").splitlines()
b = g.splitlines()
dump = "\n".join(difflib.unified_diff(a, b, lineterm=""))[:2000]
(REPO / "parity_first_failures.log").open("a").write(
f"=== {layer}/{name} ===\n{dump}\n")
def j(x):
return json.dumps(x, sort_keys=True, ensure_ascii=True)
def check(layer, name, want, got):
counts[layer] += 1
if j(want) != j(got):
fail(layer, name, f"want={j(want)[:300]} got={j(got)[:300]}")
# ── oracle dispatch (python floor) ──────────────────────────────────────────
def oracle_call(fn, a):
return {
"sorted": lambda: ss._sorted(a["value"]),
"new_doc": lambda: ss.new_doc(),
"redact": lambda: ss._redact_secrets(a["value"]),
"digest": lambda: ss.canonical_digest(a["doc"]),
"alias": lambda: _mut(lambda d: ss.alias(d, a["cid"], a["old"]), a["doc"]),
"spine_uri": lambda: ss.spine_uri(a["entity_id"]),
"resolve_uri": lambda: ss.resolve_uri(a["uri"]),
"doc_authorities": lambda: _mut(ss._doc_authorities, a["doc"]),
"authority_health": lambda: ss.authority_health(a["doc"]),
"bind_task": lambda: _mut(lambda d: ss.bind_task(
d, a["task_id"], a["entity_ids"], a["evidence_ref"]), a["doc"]),
"task_entities": lambda: ss.task_entities(a["doc"], a["task_id"]),
"impact_frontier": lambda: ss.impact_frontier(
a["doc"], a["seeds"], max_level=a.get("max_level", 2),
change_kind=a.get("change_kind")),
"observed_impact": lambda: _mut_ret(lambda d: ss.record_observed_impact(
d, a["task_id"], a["items"]), a["doc"]),
"predicted_vs_observed": lambda: ss.predicted_vs_observed(
a["predicted"], a["observed_doc"], a["seeds"]),
"events": lambda: ss.structural_events(a["doc"]),
"diff": lambda: ss.diff(a["old"], a["new"]),
"dedupe": lambda: _mut(ss.dedupe_observations, a["doc"]),
"promote_fix": lambda: _mut(lambda d: ss.promote(
d, a["id"], a["state"], a["ev"]), a["doc"]),
"resolve_status_fix": lambda: ss.resolve_status(a["entity"]),
"supersede_fix": lambda: _mut(lambda d: ss.supersede(
d, a["new"], a["old"], a["ev"]), a["doc"]),
}[fn]()
def _mut(call, doc):
d = copy.deepcopy(doc)
try:
ret = call(d)
except ValueError as exc:
return {"__err__": str(exc)}
return {"__doc__": d, "__ret__": ret}
def _mut_ret(call, doc):
d = copy.deepcopy(doc)
ret = call(d)
return {"__doc__": d, "__ret__": ret}
# ── FFI dispatch ────────────────────────────────────────────────────────────
def ffi_call(fn, a, tmp):
return {
"sorted": lambda: ss_loader.sorted_value(a["value"]),
"new_doc": lambda: ss_loader.new_doc(FIXED_NOW),
"redact": lambda: ss_loader.redact_secrets(a["value"]),
"digest": lambda: ss_loader.canonical_digest(a["doc"]),
"alias": lambda: _ffi_mut(
lambda d: ss_loader.alias(d, a["cid"], a["old"], FIXED_NOW), a["doc"],
ret_fn=lambda d: ss.entity(d, a["cid"])),
"spine_uri": lambda: ss_loader.spine_uri(a["entity_id"]),
"resolve_uri": lambda: ss_loader.resolve_uri(a["uri"]),
"doc_authorities": lambda: _ffi_mut(
ss_loader.doc_authorities, a["doc"],
ret_fn=lambda d: copy.deepcopy(ss._doc_authorities(d))),
"authority_health": lambda: ss_loader.authority_health(a["doc"]),
"bind_task": lambda: _ffi_mut(
lambda d: ss_loader.bind_task(
d, a["task_id"], a["entity_ids"], a["evidence_ref"], FIXED_NOW), a["doc"],
ret_fn=lambda d: ss.entity(d, f"task:{a['task_id']}") or {"id": f"task:{a['task_id']}"}),
"task_entities": lambda: ss_loader.task_entities(a["doc"], a["task_id"]),
"impact_frontier": lambda: ss_loader.impact_frontier(
a["doc"], a["seeds"], max_level=a.get("max_level", 2),
change_kind=a.get("change_kind")),
"observed_impact": lambda: _ffi_mut_ret(lambda d: ss_loader.record_observed_impact(
d, a["task_id"], a["items"], FIXED_NOW), a["doc"]),
"predicted_vs_observed": lambda: ss_loader.predicted_vs_observed(
a["predicted"], a["observed_doc"], a["seeds"]),
"events": lambda: ss_loader.structural_events(a["doc"]),
"diff": lambda: ss_loader.diff(a["old"], a["new"]),
"dedupe": lambda: _ffi_mut(ss_loader.dedupe_observations, a["doc"]),
"promote_fix": lambda: _ffi_mut(
lambda d: ss_loader.promote(d, a["id"], a["state"], a["ev"], FIXED_NOW), a["doc"],
ret_fn=lambda d: ss.entity(d, a["id"])),
"resolve_status_fix": lambda: ss_loader.resolve_status(a["entity"]),
"supersede_fix": lambda: _ffi_mut(
lambda d: ss_loader.supersede(d, a["new"], a["old"], a["ev"], FIXED_NOW), a["doc"],
ret_fn=lambda d: ss.entity(d, a["old"])),
}[fn]()
def _ret_entity(_updated_doc, d, cid):
return ss.entity(d, cid)
def _ffi_mut(call, doc, ret_fn=None):
"""FFI mutation contract: the kernel returns the UPDATED doc; the
oracle-shaped __ret__ is re-derived from it via ret_fn."""
d = copy.deepcopy(doc)
try:
out = call(d)
except RuntimeError as exc:
return {"__err__": str(exc).split("core error: ", 1)[-1]}
if isinstance(out, dict):
d = out
return {"__doc__": d, "__ret__": ret_fn(copy.deepcopy(d)) if ret_fn else None}
def _ffi_mut_ret(call, doc):
d = copy.deepcopy(doc)
out = call(d)
if isinstance(out.get("doc"), dict):
d = out["doc"]
return {"__doc__": d, "__ret__": out["result"]}
def frozen():
for fn, cases in TRUTH["groups"].items():
for c in cases:
a = copy.deepcopy(c["args"])
if fn == "save" or fn == "snapshot":
continue # byte-law layer below
try:
want = oracle_call(fn, a)
except ValueError as exc:
want = {"__err__": str(exc)}
got_raw = None
try:
got = ffi_call(fn, a, None)
except RuntimeError as exc:
got = {"__err__": str(exc).split("core error: ", 1)[-1]}
counts["frozen"] += 1
if j(want) != j(got):
fail("frozen", f"{fn}/{c['name']}", f"want={j(want)[:280]} got={j(got)[:280]}")
def byte_law(tmp):
"""save()/snapshot() must be byte-exact through the FFI."""
save_docs = TRUTH["groups"]["save"]
for c in save_docs:
d = copy.deepcopy(c["args"]["doc"])
name = c["name"]
try:
p = ss.save(d, tmp / f"py/{name}/store.json")
want = {"content": Path(p).read_text(), "doc": d}
except ValueError as exc:
want = {"err": str(exc)}
d2 = copy.deepcopy(c["args"]["doc"])
try:
out = ss_loader.save(d2, str(tmp / f"rs/{name}/store.json"))
got = {"content": Path(out["path"]).read_text(), "doc": out["doc"]}
except RuntimeError as exc:
got = {"err": str(exc).split("core error: ", 1)[-1]}
check("frozen", f"save-bytes/{name}", want, got)
for c in TRUTH["groups"]["snapshot"]:
a = copy.deepcopy(c["args"])
d = a["doc"]
p = ss.snapshot(d, tmp / f"py-snap/{c['name']}", a["label"], tag=a["tag"])
want = {"path": Path(p).name, "content": Path(p).read_text(), "doc": d}
d2 = copy.deepcopy(a["doc"])
out = ss_loader.snapshot(d2, str(tmp / f"rs-snap/{c['name']}"), a["label"], a["tag"], FIXED_NOW)
got = {"path": Path(out["path"]).name, "content": Path(out["path"]).read_text(), "doc": out["doc"]}
check("frozen", f"snapshot-bytes/{c['name']}", want, got)
# ── fuzz (seeded) ───────────────────────────────────────────────────────────
WORDS = ["m:a", "m:b", "m:c", "c:api", "task:T1", "é-unit", "x" * 40, ""]
RELTYPES = ["depends_on", "implements", "consumes", "consumed_by", "verified_by",
"obligated_canary", "deployed_as", "documented_by", "supersedes",
"historical_relation", "related_memory", "belongs_to", "weird_rel"]
SECRETS = ["token", "api_key", "password", "not_a_secret", "client_secret", "TOKEN"]
def rand_value(rnd, depth=0):
kind = rnd.randint(0, 6 if depth < 2 else 4)
if kind == 0:
return rnd.randint(-5, 10 ** 6)
if kind == 1:
return rnd.choice([0.5, 1.0, -2.25, 3.0, 1e-7, 1e21, 12345.6789])
if kind == 2:
return rnd.choice([True, False, None])
if kind == 3:
return "".join(rnd.choice(string.printable[:80] + "é☕φ") for _ in range(rnd.randint(0, 12)))
if kind == 4:
return rnd.choice(WORDS)
if kind == 5:
return [rand_value(rnd, depth + 1) for _ in range(rnd.randint(0, 4))]
return {rnd.choice("abcdefé") + str(i): rand_value(rnd, depth + 1)
for i in range(rnd.randint(0, 4))}
def rand_doc(rnd):
d = {"schema_version": 1, "generated_at": FIXED_NOW,
"entities": [], "relationships": [], "observations": []}
for i in range(rnd.randint(0, 5)):
e = {"id": rnd.choice(WORDS) or f"m:e{i}", "kind": rnd.choice(["module", "contract", "widget"]),
"maturity": rnd.choice(["DECLARED", "PRESENT", "WIRED", "REACHABLE", "EXERCISED", "LIVE_PROVEN"]),
"evidence_refs": sorted({f"ev-{rnd.randint(0, 3)}" for _ in range(rnd.randint(0, 2))}),
"observed_at": FIXED_NOW}
if rnd.random() < 0.3:
e["aliases"] = [f"m:old{i}"]
if rnd.random() < 0.3:
e["ext"] = {"conflicts": rand_value(rnd, 1)} if rnd.random() < 0.5 else {"n": i}
if rnd.random() < 0.3:
e["identities"] = {"p": rand_value(rnd, 1)}
if rnd.random() < 0.2:
e[rnd.choice(SECRETS)] = "s3cret"
d["entities"].append(e)
for _ in range(rnd.randint(0, 6)):
rel = {"type": rnd.choice(RELTYPES), "from": rnd.choice(WORDS), "to": rnd.choice(WORDS),
"evidence_refs": [f"ev-{rnd.randint(0, 2)}"] if rnd.random() < 0.8 else [],
"observed_at": FIXED_NOW}
if rnd.random() < 0.2:
rel["status"] = rnd.choice(["STALE", "CANDIDATE", "CURRENT"])
d["relationships"].append(rel)
for _ in range(rnd.randint(0, 4)):
o = {"kind": rnd.choice(["superseded", "k1"]), "x": rand_value(rnd, 1), "observed_at": FIXED_NOW}
d["observations"].append(o)
if rnd.random() < 0.4:
d["authority_generations"] = {
rnd.choice(["p1", "p2", "é-proj"]): {
"generation": rnd.randint(0, 5),
"observed": [f"{rnd.choice(RELTYPES)}|{rnd.choice(WORDS)}|{rnd.choice(WORDS)}"
for _ in range(rnd.randint(0, 3))],
**({"status": rnd.choice(["CURRENT", "DEGRADED", "UNKNOWN"])} if rnd.random() < 0.7 else {}),
} for _ in range(rnd.randint(1, 3))}
return d
def fuzz():
rnd = random.Random(2479)
for i in range(300):
d = rand_doc(rnd)
d2 = copy.deepcopy(d)
check("fuzz", f"sorted/{i}", ss._sorted(copy.deepcopy(d)), ss_loader.sorted_value(copy.deepcopy(d)))
check("fuzz", f"redact/{i}", ss._redact_secrets(copy.deepcopy(d)), ss_loader.redact_secrets(copy.deepcopy(d)))
check("fuzz", f"digest/{i}", ss.canonical_digest(copy.deepcopy(d)), ss_loader.canonical_digest(copy.deepcopy(d)))
check("fuzz", f"health/{i}", ss.authority_health(copy.deepcopy(d)), ss_loader.authority_health(copy.deepcopy(d)))
check("fuzz", f"events/{i}", ss.structural_events(copy.deepcopy(d)), ss_loader.structural_events(copy.deepcopy(d)))
d3 = copy.deepcopy(d)
ss.dedupe_observations(d3)
d4 = copy.deepcopy(d)
d4 = ss_loader.dedupe_observations(d4)
check("fuzz", f"dedupe/{i}", d3, d4)
seed = rnd.choice(WORDS) or "m:a"
ck = rnd.choice([None, "contract_change", "implementation_change", "contract_violation"])
check("fuzz", f"frontier/{i}",
ss.impact_frontier(copy.deepcopy(d), [seed], change_kind=ck),
ss_loader.impact_frontier(copy.deepcopy(d), [seed], change_kind=ck))
check("fuzz", f"diff/{i}", ss.diff(copy.deepcopy(d), d2), ss_loader.diff(copy.deepcopy(d), copy.deepcopy(d2)))
ent = rnd.choice(d["entities"]) if d["entities"] else {}
check("fuzz", f"resolve_status/{i}", ss.resolve_status(copy.deepcopy(ent)),
ss_loader.resolve_status(copy.deepcopy(ent)))
# mutation kernels
want = _mut(lambda x: ss.bind_task(x, "T1", ["m:a", "c:api"], "ev"), copy.deepcopy(d))
got = _ffi_mut(lambda x: ss_loader.bind_task(x, "T1", ["m:a", "c:api"], "ev", FIXED_NOW),
copy.deepcopy(d),
ret_fn=lambda x: ss.entity(x, "task:T1") or {"id": "task:T1"})
check("fuzz", f"bind_task/{i}", want, got)
want = _mut(lambda x: ss.alias(x, "m:b", "m:b-old"), copy.deepcopy(d)) if any(
e["id"] == "m:b" for e in d["entities"]) else {"__err__": "alias target not found: m:b"}
got = _ffi_mut(lambda x: ss_loader.alias(x, "m:b", "m:b-old", FIXED_NOW), copy.deepcopy(d),
ret_fn=lambda x: ss.entity(x, "m:b"))
check("fuzz", f"alias/{i}", want, got)
want = _mut(lambda x: ss.promote(x, "m:a", "WIRED", "ev-f"), copy.deepcopy(d))
got = _ffi_mut(lambda x: ss_loader.promote(x, "m:a", "WIRED", "ev-f", FIXED_NOW), copy.deepcopy(d),
ret_fn=lambda x: ss.entity(x, "m:a"))
check("fuzz", f"promote/{i}", want, got)
want = _mut(lambda x: ss.supersede(x, "m:b", "m:a", "ev-s"), copy.deepcopy(d))
got = _ffi_mut(lambda x: ss_loader.supersede(x, "m:b", "m:a", "ev-s", FIXED_NOW), copy.deepcopy(d),
ret_fn=lambda x: ss.entity(x, "m:a"))
check("fuzz", f"supersede/{i}", want, got)
items = [{"entity": rnd.choice(WORDS), "source": "t", "detail": rand_value(rnd, 1)} for _ in range(3)]
want = _mut_ret(lambda x: ss.record_observed_impact(x, "T1", copy.deepcopy(items)), copy.deepcopy(d))
got = _ffi_mut_ret(lambda x: ss_loader.record_observed_impact(x, "T1", copy.deepcopy(items), FIXED_NOW), copy.deepcopy(d))
check("fuzz", f"observed_impact/{i}", want, got)
def save_bytes_fuzz(tmp):
rnd = random.Random(4242)
for i in range(50):
d = rand_doc(rnd)
p1 = ss.save(copy.deepcopy(d), tmp / f"fz-py/{i}.json")
out = ss_loader.save(copy.deepcopy(d), str(tmp / f"fz-rs/{i}.json"))
b1 = Path(p1).read_bytes()
b2 = Path(out["path"]).read_bytes()
counts["fuzz"] += 1
if b1 != b2:
fail("fuzz", f"save-bytes/{i}", f"py={b1[:160]!r} rs={b2[:160]!r}")
check("fuzz", f"digest-bytes/{i}", ss.canonical_digest(copy.deepcopy(d)),
ss_loader.canonical_digest(copy.deepcopy(d)))
# ── live dual-mode facade battery ───────────────────────────────────────────
def facade_battery():
def run(tag):
import subprocess
code = r'''
import copy, json, os, sys, tempfile, calendar, time as _time, types
sys.path.insert(0, "src")
from rune import structural_store as ss
FIXED = "2026-10-13T01:02:03Z"
ss.now = lambda: FIXED
_EPOCH = calendar.timegm(_time.strptime(FIXED, "%Y-%m-%dT%H:%M:%SZ"))
ss.time = types.SimpleNamespace( # freeze snapshot()'s wall-clock stamp
gmtime=lambda: _time.gmtime(_EPOCH),
strftime=lambda fmt, t=None: _time.strftime(fmt, t if t is not None else _time.gmtime()),
)
out = {}
d = ss.new_doc()
ss.add_entity(d, id="m:a", kind="module")
ss.add_entity(d, id="m:b", kind="module")
ss.add_entity(d, id="c:api", kind="contract")
ss.add_relationship(d, type="implements", from_id="m:a", to_id="c:api", evidence_refs=["ev"])
ss.add_relationship(d, type="depends_on", from_id="m:b", to_id="m:a", evidence_refs=["ev"])
ss.alias(d, "m:b", "m:b-old")
ss.promote(d, "m:a", "WIRED", "ev-2")
ss.bind_task(d, "T1", ["m:a", "m:b"], "ev-t")
with tempfile.TemporaryDirectory() as td:
p = ss.save(d, td + "/s.json")
out["save_bytes_sha"] = __import__("hashlib").sha256(open(p, "rb").read()).hexdigest()
out["snapshot_name"] = ss.snapshot(d, td + "/sn", "l1", tag="cafe1234").name
out["digest"] = ss.canonical_digest(d)
out["entities"] = sorted(e["id"] for e in d["entities"])
out["via_alias"] = ss.entity(d, "m:b-old")["maturity"]
out["health"] = ss.authority_health(d)
out["frontier"] = ss.impact_frontier(d, ["c:api"], change_kind=None)
out["frontier_impl"] = ss.impact_frontier(d, ["m:a"], change_kind=None)
out["events_n"] = len(ss.structural_events(d))
out["diff"] = ss.diff(d, copy.deepcopy(d))
out["task_entities"] = ss.task_entities(d, "T1")
ss.dedupe_observations(d)
out["obs_n"] = len(d["observations"])
r = ss.record_observed_impact(d, "T2", [{"entity": "m:a", "detail": "x"}])
out["observed"] = r
out["pvo"] = ss.predicted_vs_observed(out["frontier"], d, ["c:api"])
print(json.dumps(out, sort_keys=True))
'''
env = dict(os.environ)
if tag == "python":
env["RUNE_STRUCTURAL_CORE"] = "python"
else:
env.pop("RUNE_STRUCTURAL_CORE", None)
r = subprocess.run([sys.executable, "-c", code], capture_output=True,
text=True, cwd=str(REPO), env=env, timeout=120)
if r.returncode != 0:
fail("live", tag, r.stderr[-300:])
return None
return json.loads(r.stdout)
py = run("python")
rs = run("rust")
if py is None or rs is None:
return
check("live", "dual-mode-facade", py, rs)
def main():
if not ss_loader.available():
print("Rust core not built — run cargo build --release in src/rust/structural_store")
return 2
with tempfile.TemporaryDirectory() as td:
frozen()
byte_law(Path(td))
fuzz()
save_bytes_fuzz(Path(td))
facade_battery()
total = sum(counts.values())
print(f"\nPARITY: {total} checks, {len(fails)} failures "
f"(frozen={counts['frozen']} fuzz={counts['fuzz']} live={counts['live']})")
return 1 if fails else 0
if __name__ == "__main__":
sys.exit(main())