Port the 11 audit-missing functions of src/rune/structural_model.py to the Rust crate (name-preserving, logic-1:1): workspace_record, workspace_advance, _init_project, save, reload, add, relate, state_owned, verification_surfaces, canonical_state_owner, supersession — plus a true deployment_surfaces (the old deployment_surfaces_for stub is gone, no callers), the lowercase-stale filters on dependencies/consumers, and the full §18F impact_closure shape the W6-era port had reduced. Fix-first: the delegation seam imported rune.sm_loader — the SHARD_MANAGER loader (no query symbol), so every kernel call died into the Python floor. The seam now targets stm_loader (the crate's sm_* FFI, RUNE_STM_CORE=python pins the floor). workspace_advance keeps the oracle's in-place law. Verification (verification/structural_model-summary.md): frozen truth 70 cases byte-identical (gen_structural_model_truth.py --verify), battery 16/16 with truth embedded via include_str!, live parity 4620/4620 (frozen replay + 300-doc seeded fuzz + dual-mode facade), clippy -D warnings clean, Python suites green in both pins (pre-existing 2F/12E verified identical on the stashed base). Audit: 9/20 PARTIAL -> 21/21 FULL.
398 lines
17 KiB
Python
398 lines
17 KiB
Python
#!/usr/bin/env python3
|
|
"""Freeze the dichotomy-wave gap-closing truth corpus for
|
|
src/rune/structural_model.py (issue #2479 tranche).
|
|
|
|
LAW (poisoned-truth): RUNE_STM_CORE=python and RUNE_STRUCTURAL_CORE=python
|
|
are pinned at the top — truth is generated by the PYTHON ORACLE ONLY.
|
|
Inputs are deep-copied before every oracle call (no leakage between
|
|
cases). The clock is pinned by monkeypatching structural_store.now (the
|
|
no-clock law on the Rust side: `now` is always an argument).
|
|
|
|
The frozen corpus is embedded VERBATIM in the crate's cfg(test) battery
|
|
(src/frozen_battery.rs, include_str!) and replayed by
|
|
scripts/check_structural_model_parity.py through the live FFI.
|
|
|
|
--verify regenerates from the live oracle and byte-compares the frozen
|
|
file (layer 3 of the verification ladder: truth consumed, not
|
|
self-generated).
|
|
"""
|
|
|
|
import copy
|
|
import json
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
os.environ["RUNE_STM_CORE"] = "python" # poisoned-truth law
|
|
os.environ["RUNE_STRUCTURAL_CORE"] = "python" # the floor's store core too
|
|
|
|
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 structural_model as sm # noqa: E402
|
|
|
|
FIXED_NOW = "2026-10-13T01:02:03Z"
|
|
ss.now = lambda: FIXED_NOW # pin the clock the floor's add/save stamps use
|
|
|
|
OUT = REPO / "src" / "rust" / "structural_model" / "src" / "frozen_sm_truth.json"
|
|
|
|
groups: dict[str, list] = {}
|
|
|
|
|
|
def case(group, name, args, expect):
|
|
groups.setdefault(group, []).append({"fn": group, "name": name, "args": args, "expect": expect})
|
|
|
|
|
|
def receiver(doc, project_id="proj", path="/tmp/sm-frozen/structural.json"):
|
|
"""A StructuralModel with a pre-set doc — no filesystem in __init__."""
|
|
m = sm.StructuralModel.__new__(sm.StructuralModel)
|
|
m.store_dir = None
|
|
m.project_id = project_id
|
|
m.path = path
|
|
m.doc = doc
|
|
return m
|
|
|
|
|
|
def ent(id, kind="component", maturity="DECLARED", **kw):
|
|
e = {
|
|
"id": id,
|
|
"kind": kind,
|
|
"maturity": maturity,
|
|
"tags": [],
|
|
"identities": {},
|
|
"evidence_refs": [],
|
|
"observed_at": FIXED_NOW,
|
|
}
|
|
e.update(kw)
|
|
return e
|
|
|
|
|
|
def rel(t, f, to, **kw):
|
|
r = {"type": t, "from": f, "to": to, "evidence_refs": ["ev"], "observed_at": FIXED_NOW}
|
|
r.update(kw)
|
|
return r
|
|
|
|
|
|
def fixture_doc():
|
|
"""The Scaffolding-shaped reference model (entities + every rel class)."""
|
|
d = {
|
|
"schema_version": 1,
|
|
"generated_at": FIXED_NOW,
|
|
"entities": [
|
|
ent("project:proj", "project"),
|
|
ent("domain:core", "domain"),
|
|
ent("component:alpha", "component"),
|
|
ent("component:beta", "component"),
|
|
ent("component:gamma", "component", aliases=["component:old"]),
|
|
ent("interface:api", "interface"),
|
|
ent("state_store:s1", "state_store"),
|
|
ent("state_store:s2", "state_store"),
|
|
ent("test_suite:t1", "test_suite"),
|
|
ent("deployment_surface:d1", "deployment_surface"),
|
|
],
|
|
"relationships": [
|
|
rel("belongs_to", "domain:core", "project:proj"),
|
|
rel("belongs_to", "component:alpha", "domain:core"),
|
|
rel("belongs_to", "component:beta", "domain:core"),
|
|
rel("belongs_to", "interface:api", "domain:core"),
|
|
rel("belongs_to", "state_store:s1", "project:proj"),
|
|
rel("belongs_to", "test_suite:t1", "project:proj"),
|
|
rel("belongs_to", "deployment_surface:d1", "project:proj"),
|
|
rel("depends_on", "component:beta", "component:alpha"),
|
|
rel("depends_on", "component:gamma", "component:beta", status="stale"),
|
|
rel("provides", "component:alpha", "interface:api"),
|
|
rel("consumes", "component:beta", "interface:api"),
|
|
rel("consumes", "component:gamma", "interface:api", status="STALE"),
|
|
rel("owns", "component:alpha", "state_store:s1"),
|
|
rel("owns", "component:beta", "state_store:s2"),
|
|
rel("verified_by", "component:alpha", "test_suite:t1"),
|
|
rel("deployed_by", "component:alpha", "deployment_surface:d1", status="stale"),
|
|
rel("supersedes", "component:alpha", "component:beta"),
|
|
rel("supersedes", "component:gamma", "component:alpha"),
|
|
rel("legacy_of", "component:alpha", "component:gamma"),
|
|
rel("connects_to", "deployment_surface:d1", "test_suite:t1"),
|
|
],
|
|
"observations": [],
|
|
}
|
|
return d
|
|
|
|
|
|
def q_case(group, doc, eid, call):
|
|
args = {"doc": copy.deepcopy(doc), "eid": eid}
|
|
case(group, f"{eid}", args, {"ok": call(receiver(copy.deepcopy(doc)), eid)})
|
|
|
|
|
|
def main():
|
|
doc = fixture_doc()
|
|
m = receiver(copy.deepcopy(doc))
|
|
|
|
# ── query kernels (incl. the stale filters) ─────────────────────────
|
|
for eid in ["domain:core", "project:proj", "component:alpha", "component:beta", "component:old"]:
|
|
q_case("children", doc, eid, lambda mm, e: mm.children(e))
|
|
q_case("beneath", doc, "domain:core", lambda mm, e: mm.beneath(e))
|
|
q_case("beneath", doc, "project:proj", lambda mm, e: mm.beneath(e))
|
|
for eid in ["component:alpha", "domain:core", "component:gamma", "deployment_surface:d1"]:
|
|
q_case("owning_domain", doc, eid, lambda mm, e: mm.owning_domain(e))
|
|
for eid in ["component:beta", "component:alpha", "component:gamma"]:
|
|
q_case("dependencies", doc, eid, lambda mm, e: mm.dependencies(e))
|
|
for eid in ["component:alpha", "interface:api", "state_store:s1"]:
|
|
q_case("consumers", doc, eid, lambda mm, e: mm.consumers(e))
|
|
q_case("interfaces_for", doc, "component:alpha", lambda mm, e: mm.interfaces_for(e))
|
|
for eid in ["component:alpha", "component:beta", "component:gamma"]:
|
|
q_case("state_owned", doc, eid, lambda mm, e: mm.state_owned(e))
|
|
q_case("verification_surfaces", doc, "component:alpha", lambda mm, e: mm.verification_surfaces(e))
|
|
# surfaces carry NO stale filter (oracle)
|
|
q_case("deployment_surfaces", doc, "component:alpha", lambda mm, e: mm.deployment_surfaces(e))
|
|
for eid in ["component:alpha", "state_store:s1", "component:old", "component:zz"]:
|
|
q_case("canonical_state_owner", doc, eid, lambda mm, e: mm.canonical_state_owner(e))
|
|
q_case("supersession", doc, "component:alpha", lambda mm, e: mm.supersession(e))
|
|
q_case("supersession", doc, "component:gamma", lambda mm, e: mm.supersession(e))
|
|
|
|
# ── impact_closure (full §18F shape) ─────────────────────────────────
|
|
for name, eid, depth in [
|
|
("full", "component:alpha", None),
|
|
("root", "project:proj", None),
|
|
("leaf_state_store", "state_store:s1", None),
|
|
("depth_bound_1", "component:alpha", 1),
|
|
("unknown_entity", "nope:x", None),
|
|
("quoted_eid", "it's", None),
|
|
]:
|
|
args = {"doc": copy.deepcopy(doc), "eid": eid}
|
|
kwargs = {}
|
|
if depth is not None:
|
|
args["max_depth"] = depth
|
|
kwargs = {"max_depth": depth}
|
|
r = receiver(copy.deepcopy(doc)).impact_closure(eid, **kwargs)
|
|
case("impact_closure", name, args, {"ok": r})
|
|
|
|
# ── workspace_record / workspace_advance ─────────────────────────────
|
|
wr = sm.workspace_record(contract_id="C-1", construction_base="sha1")
|
|
case("workspace_record", "defaults", {"contract_id": "C-1", "construction_base": "sha1"}, {"ok": wr})
|
|
wr2 = sm.workspace_record(
|
|
contract_id="C-2",
|
|
construction_base="sha2",
|
|
runtime_generation="rt",
|
|
candidate_generation="cd",
|
|
promotion_target="gitea/release",
|
|
rollback_generation="rb",
|
|
)
|
|
case(
|
|
"workspace_record",
|
|
"full",
|
|
{
|
|
"contract_id": "C-2",
|
|
"construction_base": "sha2",
|
|
"runtime_generation": "rt",
|
|
"candidate_generation": "cd",
|
|
"promotion_target": "gitea/release",
|
|
"rollback_generation": "rb",
|
|
},
|
|
{"ok": wr2},
|
|
)
|
|
|
|
def wa_case(name, stages, stage, evidence="", build=None):
|
|
record = build if build is not None else sm.workspace_record(contract_id="C-3", construction_base="sha3")
|
|
for s in stages:
|
|
record = sm.workspace_advance(record, s)
|
|
args = {"record": copy.deepcopy(record), "stage": stage, "evidence": evidence}
|
|
try:
|
|
out = sm.workspace_advance(copy.deepcopy(record), stage, evidence=evidence)
|
|
expect = {"ok": out}
|
|
except ValueError as exc:
|
|
expect = {"err": str(exc)}
|
|
case("workspace_advance", name, args, expect)
|
|
|
|
wa_case("first_stage", [], "base_verified")
|
|
wa_case("second_stage", ["base_verified"], "worktree_created")
|
|
wa_case("evidence_truncated", ["base_verified"], "worktree_created", evidence="e" * 250)
|
|
wa_case("evidence_empty_no_key", ["base_verified"], "construction")
|
|
wa_case("evidence_second_appends", ["base_verified", "worktree_created"], "construction", evidence="second")
|
|
wa_case("wrong_first", [], "construction")
|
|
wa_case("skip_stage", ["base_verified"], "verified")
|
|
wa_case("backwards", ["base_verified", "worktree_created"], "base_verified")
|
|
wa_case("unknown_stage", [], "bogus")
|
|
wa_case("unknown_mid", ["base_verified"], "bogus")
|
|
# missing stages_completed → KeyError (crash parity)
|
|
args = {"record": {"contract_id": "C"}, "stage": "base_verified", "evidence": ""}
|
|
try:
|
|
sm.workspace_advance(copy.deepcopy(args["record"]), "base_verified")
|
|
expect = {"ok": None}
|
|
except KeyError as exc:
|
|
expect = {"err": f"KeyError: {exc}"}
|
|
case("workspace_advance", "missing_stages_key", args, expect)
|
|
|
|
# ── add ──────────────────────────────────────────────────────────────
|
|
def add_case(name, d, kind, name_, parent=None, ext=None, evidence_ref="structural-model"):
|
|
args = {
|
|
"doc": copy.deepcopy(d),
|
|
"kind": kind,
|
|
"name": name_,
|
|
"parent": parent,
|
|
"ext": ext,
|
|
"evidence_ref": evidence_ref,
|
|
}
|
|
mm = receiver(copy.deepcopy(d))
|
|
try:
|
|
eid = mm.add(kind, name_, parent=parent, ext=ext, evidence_ref=evidence_ref)
|
|
expect = {"ok": {"doc": mm.doc, "eid": eid}}
|
|
except (ValueError, KeyError) as exc:
|
|
expect = {"err": f"{type(exc).__name__}: {exc}" if isinstance(exc, KeyError) else str(exc)}
|
|
case("add", name, args, expect)
|
|
|
|
base = {
|
|
"schema_version": 1,
|
|
"generated_at": FIXED_NOW,
|
|
"entities": [
|
|
ent("project:proj", "project"),
|
|
ent("domain:core", "domain"),
|
|
ent("component:there", "component", maturity="PRESENT"),
|
|
],
|
|
"relationships": [],
|
|
"observations": [],
|
|
}
|
|
add_case("fresh", base, "component", "alpha", parent="domain:core")
|
|
add_case("with_ext", base, "interface", "api", ext={"proto": "grpc", "notes": "n"})
|
|
add_case("unknown_kind", base, "widget", "w")
|
|
add_case("quoted_kind", base, "it's", "w")
|
|
add_case("custom_evidence", base, "test_suite", "t", evidence_ref="ev-x")
|
|
readd = copy.deepcopy(base)
|
|
readd["entities"].append(
|
|
ent("component:again", "component", maturity="PRESENT", identities={"name": "old"}, ext={"keep": 1})
|
|
)
|
|
add_case("readd_refreshes", readd, "component", "again", ext={"new": 2})
|
|
no_identities = copy.deepcopy(base)
|
|
no_identities["entities"].append({"id": "component:bare", "kind": "component", "maturity": "DECLARED"})
|
|
add_case("missing_identities_crash", no_identities, "component", "bare")
|
|
add_case("empty_ext_falsy_skipped", base, "environment", "prod", ext={})
|
|
|
|
# ── relate ──────────────────────────────────────────────────────────
|
|
def relate_case(
|
|
name,
|
|
d,
|
|
from_id,
|
|
rl,
|
|
to_id,
|
|
evidence_ref="structural-model",
|
|
authority="model-author",
|
|
status="active",
|
|
ext=None,
|
|
):
|
|
args = {
|
|
"doc": copy.deepcopy(d),
|
|
"from_id": from_id,
|
|
"rel": rl,
|
|
"to_id": to_id,
|
|
"evidence_ref": evidence_ref,
|
|
"authority": authority,
|
|
"status": status,
|
|
"ext": ext,
|
|
}
|
|
mm = receiver(copy.deepcopy(d))
|
|
try:
|
|
r = mm.relate(from_id, rl, to_id, evidence_ref=evidence_ref, authority=authority, status=status, ext=ext)
|
|
expect = {"ok": {"doc": mm.doc, "rel": r}}
|
|
except ValueError as exc:
|
|
expect = {"err": str(exc)}
|
|
case("relate", name, args, expect)
|
|
|
|
relate_case("defaults", base, "component:there", "depends_on", "project:proj")
|
|
relate_case(
|
|
"importer_authority", base, "component:there", "calls", "domain:core", authority="importer", status="CURRENT"
|
|
)
|
|
relate_case("with_ext", base, "component:there", "reads", "domain:core", ext={"line": 3})
|
|
relate_case("unknown_rel", base, "component:there", "hates", "domain:core")
|
|
relate_case("empty_evidence", base, "component:there", "writes", "domain:core", evidence_ref="")
|
|
relate_case("empty_ext_falsy_skipped", base, "component:there", "connects_to", "domain:core", ext={})
|
|
dupdoc = copy.deepcopy(base)
|
|
sm.StructuralModel.relate(receiver(dupdoc), "component:there", "depends_on", "project:proj")
|
|
relate_case(
|
|
"dup_keeps_stored", dupdoc, "component:there", "depends_on", "project:proj", authority="user", status="legacy"
|
|
)
|
|
|
|
# ── _init_project / save / reload (real filesystem, byte law) ───────
|
|
with tempfile.TemporaryDirectory() as td:
|
|
m2 = sm.StructuralModel(td + "/s1", "init-proj")
|
|
# the constructor's pre-_init_project doc IS new_doc() (pinned clock)
|
|
case(
|
|
"init_project",
|
|
"fresh_construction",
|
|
{"doc": ss.new_doc(), "project_id": "init-proj"},
|
|
{"ok": {"doc": m2.doc, "content": Path(m2.path).read_text()}},
|
|
)
|
|
|
|
m3 = sm.StructuralModel(td + "/s2", "save-proj")
|
|
m3.add("domain", "core", parent="project:save-proj")
|
|
m3.add("component", "alpha", parent="domain:core")
|
|
m3.relate("component:alpha", "owns", "state_store:s1")
|
|
m3.save()
|
|
case(
|
|
"save",
|
|
"byte_law",
|
|
{"doc": copy.deepcopy(m3.doc)},
|
|
{"ok": {"doc": m3.doc, "content": Path(m3.path).read_text()}},
|
|
)
|
|
# a doc with secrets — the store's redaction law applies on disk
|
|
m4 = sm.StructuralModel(td + "/s3", "redact-proj")
|
|
m4.doc["credentials"] = {"token": "s3cret", "note": "keep"}
|
|
m4.save()
|
|
case(
|
|
"save",
|
|
"secrets_redacted",
|
|
{"doc": copy.deepcopy(m4.doc)},
|
|
{"ok": {"doc": m4.doc, "content": Path(m4.path).read_text()}},
|
|
)
|
|
# save() stamps schema_version=1 BEFORE the store save — a stale
|
|
# 99 is coerced, never raised (the facade's own law)
|
|
m5 = receiver({"schema_version": 99, "entities": [], "relationships": []}, "bad", td + "/s4/structural.json")
|
|
m5.save()
|
|
case(
|
|
"save",
|
|
"schema_version_stamped",
|
|
{"doc": {"schema_version": 99, "entities": [], "relationships": []}},
|
|
{"ok": {"doc": m5.doc, "content": Path(m5.path).read_text()}},
|
|
)
|
|
|
|
# reload: existing / missing / corrupt
|
|
m6 = sm.StructuralModel(td + "/s2", "save-proj")
|
|
m6.reload()
|
|
case("reload", "existing_file", {}, {"ok": {"doc": m6.doc}})
|
|
m7 = receiver(
|
|
{"schema_version": 1, "entities": [], "relationships": []}, "miss", td + "/missing/structural.json"
|
|
)
|
|
m7.reload()
|
|
case("reload", "missing_file_keeps_doc", {}, {"ok": {"doc": m7.doc}})
|
|
corrupt = td + "/corrupt/structural.json"
|
|
Path(corrupt).parent.mkdir(parents=True, exist_ok=True)
|
|
Path(corrupt).write_text("{not json")
|
|
m8 = receiver({"schema_version": 1, "entities": [], "relationships": []}, "corrupt", corrupt)
|
|
m8.reload()
|
|
case("reload", "corrupt_file_keeps_doc", {}, {"ok": {"doc": m8.doc}})
|
|
|
|
out = {"module": "structural_model", "now": FIXED_NOW, "groups": groups}
|
|
text = json.dumps(out, indent=1, sort_keys=True) + "\n"
|
|
if "--verify" in sys.argv:
|
|
frozen = OUT.read_text()
|
|
if frozen == text:
|
|
print(
|
|
f"OK: frozen truth byte-identical against live oracle "
|
|
f"({sum(len(v) for v in groups.values())} cases) -> {OUT}"
|
|
)
|
|
return 0
|
|
print("MISMATCH: regenerated truth differs from frozen file")
|
|
for a, b in zip(frozen.splitlines(), text.splitlines()):
|
|
if a != b:
|
|
print(f" frozen: {a[:200]}")
|
|
print(f" fresh: {b[:200]}")
|
|
break
|
|
return 1
|
|
OUT.write_text(text)
|
|
total = sum(len(v) for v in groups.values())
|
|
print(f"frozen {total} cases across {len(groups)} groups -> {OUT}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|