Files
rune/scripts/gen_capability_ledger_truth.py
T
Nick 31fb68b58d feat(capability_ledger): port to FULL Rust — dichotomy wave
FULL parity port of src/rune/capability_ledger.py to the capability_ledger
Rust core (RUST verdict, issue #2479 tranche, contract
BUILDER-2026-10-11T15:50:51Z-bde08d-factory-wave):
- 17 FFI symbols (cl_new/cl_define/cl_get/cl_material_change/cl_move/
  cl_supersede/cl_retire/cl_active/cl_by_component/cl_context_packet/
  cl_save/cl_classify_version_bump/cl_bump_version/cl_attach_spec/
  cl_revise_spec/cl_spec_drift_status/cl_spec_context) via doc-envelope
  law; the ledger doc travels as a sibling envelope key so "value" stays
  oracle-verbatim; free via capability_scoring cs_free_string (no
  re-export); serde_json preserve_order for dict-position parity
- stale partial port replaced (old CapabilityEntry struct had no Python
  counterpart); classify_version_bump/bump_version kept name-compatible
- REAL divergences fixed pre-wiring (battery caught all three):
  MINOR/PATCH bumps must normalize untouched components through int()
  (leading zeros collapse everywhere — '01.02.03' MINOR = '1.3.0');
  bump arithmetic is arbitrary-precision like Python ints (u64/u128
  boundary); SEMVER_RE \\d matches Unicode Nd + int() accepts them
  (full Nd range table, verified against unicodedata)
- py_repr() mirrors Python repr() in error envelopes (str(KeyError) is
  repr'd); note/reason truncation by codepoints (py_slice)
- ctypes loader cl_loader.py mirrors broker_loader.py (LEDGER_RUST_LIB
  override); facade delegation on all 18 public fns with RUNE_LEDGER_CORE
  =python pin and Python-body fallback (kernel errors fall back so the
  proper exception types re-raise)
- battery: 16 sequences / 155 ops frozen from the live oracle
  (frozen_cl_truth.json, replayed VERBATIM in-crate) + save() byte-parity
  pinned (indent=1 sort_keys identical); 500-op live fuzz seed 2479:
  500/500 + final-file-bytes equal
- FIX-FIRST (pre-existing latent regression surfaced by this batch):
  spine unseeded path bypassed the monkeypatchable ingest_umbrellas seam
  once the rune_spine kernel was built — the generated-view refusal fired
  inside the kernel where isolation tests cannot intercept it
  (test_spine_seeds_entry_point red on master with kernel built). build()
  now routes the manifest ingest through the public seam first and hands
  the kernel a carry-attempted doc; the kernel skips its manifest branch
  when existing was provided as an object (true cold path keeps the
  load-bearing refusal). 198 ledger/spine Python tests green.
2026-10-11 23:03:35 +01:00

441 lines
20 KiB
Python

#!/usr/bin/env python3
"""Freeze capability_ledger oracle truth for the Rust port battery
(dichotomy factory batch, #2479 tranche).
Exercises EVERY public function across all branches: CapabilityLedger
(new/load, save, define defaults + full + duplicate, get prefixed/plain/
unknown, material_change revisions + generation + source_locations + note
truncation, move, supersede (plain + prefixed by, note truncation), retire,
active, by_component, context_packet known/unknown) plus the module-level
spec machinery (classify_version_bump full 4-bool matrix, bump_version
valid/invalid/leading-zero/overflow, attach_spec defaults + errors,
revise_spec classification + proposed-kind governance + unjustified bump +
KeyError + covers_material_revision, spec_drift_status all outcomes,
spec_context with/without spec).
The wall clock is FROZEN: every op records the exact _now() value it was
given, and the kernel receives that same string via the FFI payload — the
kernel itself never reads a clock.
Output: /tmp/cl_truth.json — replayed VERBATIM by
src/rust/capability_ledger/src/branch_battery.rs.
"""
from __future__ import annotations
import json
import os
import sys
import tempfile
from datetime import datetime, timedelta, timezone
from pathlib import Path
os.environ["RUNE_LEDGER_CORE"] = "python" # freeze the ORACLE, never the kernel
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from rune import capability_ledger as cl # noqa: E402
# ── deterministic frozen clock ───────────────────────────────────────────
_T0 = datetime(2026, 1, 1, tzinfo=timezone.utc)
_tick = [0]
def _next_now() -> str:
_tick[0] += 1
return (_T0 + timedelta(seconds=_tick[0] - 1)).strftime("%Y-%m-%dT%H:%M:%SZ")
def _pin_clock(now: str) -> None:
"""All _now() calls WITHIN one op return the same value (the kernel
receives exactly one now per op; live wall-clock is same-second)."""
cl._now = lambda n=now: n # noqa: E731
class Seq:
"""One replayable op sequence against one ledger document."""
def __init__(self, name: str, sequences: list):
self.name = name
self.ops: list[dict] = []
self.expected: list[dict] = []
self.saved_files: dict[str, str] = {}
self.ledger: cl.CapabilityLedger | None = None
self.tmpdir = tempfile.mkdtemp(prefix=f"cltruth-{name}-")
sequences.append(self)
# -- op plumbing -------------------------------------------------------
def _call(self, opname: str, **args) -> None:
op = {"op": opname, **args}
now = _next_now()
_pin_clock(now)
if opname not in ("spec_context", "classify_version_bump",
"bump_version"):
op["now"] = now
self.ops.append(op)
try:
out = getattr(self, f"_{opname}")(op)
# oracle returns LIVE references (rec dicts) — snapshot NOW or
# later ops mutate the frozen expectation
out = json.loads(json.dumps(out))
self.expected.append({"ok": True, "value": out})
except Exception as e: # ValueError/SpecError/KeyError — message parity
self.expected.append({"ok": False, "err": str(e)})
def _new(self, op) -> dict:
file_text = op.get("file_text")
if file_text is not None:
(Path(self.tmpdir) / "capability_ledger.json").write_text(file_text)
self.ledger = cl.CapabilityLedger(self.tmpdir)
return self.ledger.doc
def _define(self, op) -> dict:
kwargs = {k: v for k, v in op.items()
if k not in ("op", "now")}
return self.ledger.define(**kwargs)
def _get(self, op) -> dict | None:
return self.ledger.get(op["capability_id"])
def _material_change(self, op) -> dict:
kwargs = {"note": op["note"]}
if "generation" in op:
kwargs["generation"] = op["generation"]
if "source_locations" in op:
kwargs["source_locations"] = op["source_locations"]
return self.ledger.material_change(op["capability_id"], **kwargs)
def _move(self, op) -> dict:
return self.ledger.move(op["capability_id"], op["new_locations"])
def _supersede(self, op) -> dict:
return self.ledger.supersede(op["capability_id"], op["by"],
note=op.get("note", ""))
def _retire(self, op) -> dict:
return self.ledger.retire(op["capability_id"],
note=op.get("note", ""))
def _active(self, op) -> list:
return self.ledger.active()
def _by_component(self, op) -> list:
return self.ledger.by_component(op["component"])
def _context_packet(self, op) -> dict:
return self.ledger.context_packet(op["capability_id"])
def _save(self, op) -> None:
self.ledger.save()
self.saved_files["capability_ledger.json"] = (
Path(self.tmpdir) / "capability_ledger.json").read_text()
return None
def _classify_version_bump(self, op) -> str:
return cl.classify_version_bump(
contract_changed=op["contract_changed"],
behaviour_added=op["behaviour_added"],
consumer_break=op["consumer_break"],
implementation_only=op["implementation_only"])
def _bump_version(self, op) -> str:
return cl.bump_version(op["version"], op["kind"])
def _attach_spec(self, op) -> dict:
kwargs = {k: v for k, v in op.items()
if k not in ("op", "now", "capability_id")}
return cl.attach_spec(self.ledger, op["capability_id"], **kwargs)
def _revise_spec(self, op) -> dict:
kwargs = {k: v for k, v in op.items()
if k not in ("op", "now", "capability_id")}
return cl.revise_spec(self.ledger, op["capability_id"], **kwargs)
def _spec_drift_status(self, op) -> dict:
return cl.spec_drift_status(self.ledger, op["capability_id"])
def _spec_context(self, op) -> dict:
rec = self.ledger.get(op["capability_id"])
return cl.spec_context(rec)
sequences: list[Seq] = []
LONG_NOTE = "x" * 260
LONG_REASON = "r" * 260
def frozen_json(doc: dict) -> str:
"""A saved ledger file EXACTLY as save() writes it (indent=1, sorted)."""
return json.dumps(doc, indent=1, sort_keys=True)
# ── S1: fresh ledger — defaults, queries, save ───────────────────────────
s = Seq("fresh-defaults-queries", sequences)
s._call("new")
s._call("define", capability_id="auth-token", name="Auth Token",
purpose="issue + verify session tokens", owning_project="rune",
component="component:auth-core")
s._call("define", capability_id="cap:prefixed-given", name="Prefixed",
purpose="id given with prefix", owning_project="rune",
component="component:auth-core")
s._call("get", capability_id="auth-token")
s._call("get", capability_id="capability:auth-token")
s._call("get", capability_id="nope")
s._call("active")
s._call("by_component", component="component:auth-core")
s._call("by_component", component="component:nowhere")
s._call("context_packet", capability_id="auth-token")
s._call("context_packet", capability_id="ghost")
s._call("save")
# ── S2: define with every field provided ─────────────────────────────────
s = Seq("define-full-fields", sequences)
s._call("new")
s._call("define", capability_id="full-rec", name="Full", purpose="all fields",
owning_project="rune", component="component:c1",
source_locations=["src/a.py", "src/b.py"],
contract="contract text", state_touched="store/scaffold",
dependencies=["dep-x"], consumers=["consumer-y"],
invariants=["inv-1"], behaviour_specs=["bs-1"],
constraints="no raw tokens", generation="gen-7")
s._call("define", capability_id="empty-lists", name="Empty", purpose="empties",
owning_project="rune", component="component:c1",
source_locations=[], dependencies=[], consumers=[],
invariants=[], behaviour_specs=[])
s._call("get", capability_id="full-rec")
s._call("get", capability_id="empty-lists")
s._call("context_packet", capability_id="full-rec")
# ── S3: duplicate define raises ──────────────────────────────────────────
s = Seq("duplicate-define", sequences)
s._call("new")
s._call("define", capability_id="dup", name="D", purpose="first",
owning_project="rune", component="component:c")
s._call("define", capability_id="dup", name="D2", purpose="second",
owning_project="rune", component="component:c")
# ── S4: unknown-capability errors for every mutator/query ───────────────
s = Seq("unknown-cap-errors", sequences)
s._call("new")
s._call("material_change", capability_id="ghost", note="n")
s._call("move", capability_id="ghost", new_locations=["a.py"])
s._call("supersede", capability_id="ghost", by="other")
s._call("retire", capability_id="ghost")
s._call("attach_spec", capability_id="ghost", requirements=["r"],
expected_behaviours=["b"], non_goals=["n"], acceptance_criteria=["a"])
s._call("revise_spec", capability_id="ghost", reason="x")
s._call("spec_drift_status", capability_id="ghost")
# ── S5: supersede / retire / move lifecycle ──────────────────────────────
s = Seq("supersede-retire-move", sequences)
s._call("new")
s._call("define", capability_id="old-cap", name="Old", purpose="p",
owning_project="rune", component="component:c",
source_locations=["src/old.py"])
s._call("define", capability_id="doomed", name="Doomed", purpose="p",
owning_project="rune", component="component:c")
s._call("supersede", capability_id="old-cap", by="new-cap", note="replaced")
s._call("supersede", capability_id="doomed", by="capability:explicit-prefixed",
note=LONG_NOTE)
s._call("retire", capability_id="doomed", note="dead")
s._call("retire", capability_id="doomed", note=LONG_NOTE) # already retired: still appends history
s._call("active")
s._call("move", capability_id="old-cap", new_locations=["src/moved/old.py", "src/moved/old2.py"])
s._call("context_packet", capability_id="old-cap") # packet still served for superseded
s._call("by_component", component="component:c")
# ── S6: material_change revision counter + branches ─────────────────────
s = Seq("material-change-revisions", sequences)
s._call("new")
s._call("define", capability_id="rev-cap", name="Rev", purpose="p",
owning_project="rune", component="component:c",
source_locations=["src/a.py"], constraints="c0")
s._call("material_change", capability_id="rev-cap", note="first change")
s._call("material_change", capability_id="rev-cap", note=LONG_NOTE,
generation="gen-9")
s._call("material_change", capability_id="rev-cap", note="third",
generation="", source_locations=["src/b.py"])
s._call("material_change", capability_id="rev-cap", note="fourth",
source_locations=[])
s._call("get", capability_id="rev-cap")
s._call("context_packet", capability_id="rev-cap")
# ── S7: classify_version_bump — full 4-bool matrix ───────────────────────
s = Seq("classify-bump-matrix", sequences)
s._call("new")
for cc in (False, True):
for ba in (False, True):
for cb in (False, True):
for io in (False, True):
s._call("classify_version_bump", contract_changed=cc,
behaviour_added=ba, consumer_break=cb,
implementation_only=io)
# ── S8: bump_version — valid / invalid / zeros / overflow ────────────────
s = Seq("bump-version-cases", sequences)
s._call("new")
for version, kind in [
("0.1.0", "MAJOR"), ("0.1.0", "MINOR"), ("0.1.0", "PATCH"),
("1.2.3", "MAJOR"), ("1.2.3", "MINOR"), ("1.2.3", "PATCH"),
("0.0.9", "PATCH"), ("999.999.999", "MAJOR"), ("01.02.03", "MINOR"),
("007.0.0", "PATCH"),
("18446744073709551615.0.0", "MAJOR"), # u64 boundary
("99999999999999999999999999.0.0", "PATCH"), # beyond u128
("1.2", "PATCH"), ("1.2.3.4", "PATCH"), ("abc", "MAJOR"),
("", "PATCH"), ("1..3", "MINOR"), ("-1.2.3", "MAJOR"),
("1.2.x", "PATCH"), (" 1.2.3", "PATCH"), ("1.2.3 ", "PATCH"),
("1.2.3", "BOGUS"), ("1.2.3", ""), ("1.2.3", "major"),
]:
s._call("bump_version", version=version, kind=kind)
# ── S9: attach_spec — defaults + explicit + errors ───────────────────────
s = Seq("attach-spec-branches", sequences)
s._call("new")
s._call("define", capability_id="specd", name="S", purpose="spec'd",
owning_project="rune", component="component:c")
s._call("spec_drift_status", capability_id="specd") # no spec attached
s._call("attach_spec", capability_id="specd", requirements=["r1", "r2"],
expected_behaviours=["b1"], non_goals=["ng1"],
acceptance_criteria=["ac1"])
s._call("spec_drift_status", capability_id="specd") # baseline covers rev 0
s._call("define", capability_id="specd2", name="S2", purpose="x",
owning_project="rune", component="component:c")
s._call("attach_spec", capability_id="specd2", requirements=[],
expected_behaviours=[], non_goals=[], acceptance_criteria=[],
baseline_version="1.0.0", baseline_reason="stable, evidenced",
governance_version_started_at="2025-12-31T23:59:59Z")
s._call("get", capability_id="specd2")
# errors
s._call("define", capability_id="badbase", name="B", purpose="x",
owning_project="rune", component="component:c")
s._call("attach_spec", capability_id="badbase", requirements=[],
expected_behaviours=[], non_goals=[], acceptance_criteria=[],
baseline_version="1.0")
s._call("attach_spec", capability_id="badbase", requirements=[],
expected_behaviours=[], non_goals=[], acceptance_criteria=[],
baseline_version="not-semver")
s._call("attach_spec", capability_id="ghost", requirements=[],
expected_behaviours=[], non_goals=[], acceptance_criteria=[])
# ── S10: revise_spec — classification, governance, drift closure ─────────
s = Seq("revise-spec-governance", sequences)
s._call("new")
s._call("define", capability_id="gov", name="G", purpose="governed",
owning_project="rune", component="component:c")
s._call("attach_spec", capability_id="gov", requirements=["r"],
expected_behaviours=["b"], non_goals=["n"], acceptance_criteria=["a"])
s._call("material_change", capability_id="gov", note="uncovered rev 1")
s._call("spec_drift_status", capability_id="gov") # DRIFT: rev 1 > covered 0
s._call("revise_spec", capability_id="gov", implementation_only=True,
reason="internal tidy-up")
s._call("spec_drift_status", capability_id="gov") # drift closed
s._call("material_change", capability_id="gov", note="rev 2")
s._call("material_change", capability_id="gov", note="rev 3")
s._call("spec_drift_status", capability_id="gov") # 2 uncovered
s._call("revise_spec", capability_id="gov", behaviour_added=True,
reason=LONG_REASON, changed_sections=["requirements"],
affected_consumers=["consumer-x"],
validation_consequences=["retest suite B"])
s._call("spec_drift_status", capability_id="gov")
s._call("revise_spec", capability_id="gov", consumer_break=True,
reason="breaking contract change")
s._call("spec_drift_status", capability_id="gov")
s._call("get", capability_id="gov")
# ── S11: revise_spec — proposed-kind governance branches ─────────────────
s = Seq("revise-proposed-kind", sequences)
s._call("new")
s._call("define", capability_id="pk", name="PK", purpose="p",
owning_project="rune", component="component:c")
s._call("attach_spec", capability_id="pk", requirements=["r"],
expected_behaviours=["b"], non_goals=["n"], acceptance_criteria=["a"])
s._call("revise_spec", capability_id="pk", implementation_only=True,
reason="ok patch, proposed matches", proposed_kind="PATCH")
s._call("revise_spec", capability_id="pk", implementation_only=True,
reason="patch proposed but minor earned", proposed_kind="MINOR")
s._call("revise_spec", capability_id="pk", behaviour_added=True,
reason="minor earned, downgraded to patch on purpose",
proposed_kind="PATCH")
s._call("revise_spec", capability_id="pk", implementation_only=True,
reason="major proposed for metadata churn — unjustified",
proposed_kind="MAJOR")
s._call("revise_spec", capability_id="pk", implementation_only=True,
reason="minor proposed for metadata churn — unjustified",
proposed_kind="MINOR")
s._call("revise_spec", capability_id="pk", implementation_only=True,
reason="garbage proposed kind", proposed_kind="HUGE")
s._call("revise_spec", capability_id="pk", implementation_only=True,
reason="empty proposed kind falls back to classify",
proposed_kind="")
s._call("revise_spec", capability_id="pk", reason="no flags at all — churn")
s._call("get", capability_id="pk")
# ── S12: revise_spec on a capability with no spec ────────────────────────
s = Seq("revise-no-spec", sequences)
s._call("new")
s._call("define", capability_id="bare", name="B", purpose="p",
owning_project="rune", component="component:c")
s._call("revise_spec", capability_id="bare", implementation_only=True,
reason="x")
# ── S13: save/reload roundtrip (sorted file bytes pinned) ────────────────
s = Seq("save-reload-roundtrip", sequences)
s._call("new")
s._call("define", capability_id="zzz-last", name="Z", purpose="p",
owning_project="rune", component="component:c")
s._call("define", capability_id="aaa-first", name="A", purpose="p",
owning_project="rune", component="component:c")
s._call("save")
saved = s.saved_files["capability_ledger.json"]
s2 = Seq("reload-from-saved-file", sequences)
s2._call("new", file_text=saved)
s2._call("active") # sorted-key order from the file
s2._call("get", capability_id="aaa-first")
s2._call("save")
# ── S14: unicode + repr edge cases ───────────────────────────────────────
s = Seq("unicode-repr-edges", sequences)
s._call("new")
s._call("define", capability_id="café-x", name="Café", purpose="rôle",
owning_project="rune", component="component:coffeé")
s._call("context_packet", capability_id="café-x")
s._call("material_change", capability_id="café-x", note="naïve change → ünïcode ✓")
s._call("material_change", capability_id="ghost-cap", note="x")
s._call("bump_version", version="café.1.0", kind="PATCH")
s._call("context_packet", capability_id="quo'te")
s._call("context_packet", capability_id='dq"te')
# ── S15: spec_context with and without spec ──────────────────────────────
s = Seq("spec-context", sequences)
s._call("new")
s._call("define", capability_id="ctx-bare", name="CB", purpose="purpose-y",
owning_project="rune", component="component:c",
contract="contract-y", invariants=["i1"], behaviour_specs=["bs1"])
s._call("spec_context", capability_id="ctx-bare") # no spec — empty spec half
s._call("attach_spec", capability_id="ctx-bare", requirements=["req1"],
expected_behaviours=["eb1"], non_goals=["ngo1"],
acceptance_criteria=["ac1"])
s._call("spec_context", capability_id="ctx-bare")
s._call("material_change", capability_id="ctx-bare", note="m")
s._call("spec_context", capability_id="ctx-bare") # last_bump = BASELINE entry
truth = {
"frozen": "capability_ledger full public surface + spec machinery",
"oracle": "src/rune/capability_ledger.py",
"note": "Every op records the exact _now() it was given; the kernel "
"receives that string via the FFI payload and never reads a "
"clock. saved_files pin save() byte output (Python facade "
"serializes with indent=1 sort_keys=True).",
"sequences": [
{"name": s.name, "ops": s.ops, "expected": s.expected,
"saved_files": s.saved_files,
"final_doc": s.ledger.doc}
for s in sequences
],
}
out = Path("/tmp/cl_truth.json")
out.write_text(json.dumps(truth, indent=1, ensure_ascii=False) + "\n")
n_ops = sum(len(s.ops) for s in sequences)
print(f"froze {len(sequences)} sequences / {n_ops} ops -> {out}")