feat(meaningful_events): port to FULL Rust — dichotomy wave

Language verdict: RUST per docs/language-dichotomy-ledger.md (Law 4 —
deterministic trigger evaluation; the wall CLOCK is host-side by nature
(now passed per op, never null across FFI)).

- me_should_fire/me_fire_due/me_mark_fired doc-envelope FFI (me_*
  symbols; free via capability_scoring cs_free_string — no free
  re-export); me_loader mirroring broker_loader.py.
- Facade delegation with RUNE_ME_CORE=python pin; kernel floor on any
  kernel error; INT now keeps the Python floor in mark_fired (the
  oracle stores the value as-is; the kernel wire is float).
- Python laws replicated exactly: decision ordering (unknown → stop →
  absent → rate → fires), state truthiness (0/''/null are absent),
  falsy last_fired → 0, STRICTLY-LESS rate gate (equal fires),
  Python int() truncation toward zero INSIDE the reason string,
  fire_due in table (insertion) order, mark_fired shallow-copy with
  unknown-trigger recording.
- 96-case frozen oracle truth (RUNE_ME_CORE=python pinned, deep-copied
  inputs, every trigger class × every branch, truncation + boundary +
  future-last cases) embedded in the crate and replayed VERBATIM by the
  branch battery; live parity 1296/1296 (96 frozen + 1200 fuzz, seed
  2479); pytest 56 passed (kernel auto) and 32 per pin BOTH ways;
  clippy -D warnings clean.
This commit is contained in:
Nick committed 2026-10-13 07:30:18 +01:00
1 parent ffa8608f95
commit a0d0215ee0
10 files changed
+2319 -1

No files matched your search

+124
View File
@@ -0,0 +1,124 @@
#!/usr/bin/env python3
"""Live parity: meaningful_events Rust kernel vs Python oracle (dichotomy
wave). Layers:
1. FROZEN — the 96 frozen-oracle cases replayed through the facade in
BOTH core modes (RUNE_ME_CORE=python floor vs auto kernel); exact
compare, inputs deep-copied.
2. FUZZ — 400 seeded random trigger/state/now triples (seed 2479)
driven through both modes; exact compare (should_fire, fire_due,
mark_fired round-trips).
"""
import copy
import json
import os
import random
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO / "src"))
sys.path.insert(0, str(REPO / "src" / "rune"))
import meaningful_events as me # noqa: E402
from rune import me_loader # noqa: E402
if not me_loader.available():
print("FATAL: Rust meaningful_events core not built")
sys.exit(2)
TRUTH = json.loads(
(REPO / "src" / "rust" / "meaningful_events" / "frozen_truth.json")
.read_text())
fails = []
counts = {"frozen": 0, "fuzz": 0}
def fail(layer, name, detail):
fails.append((layer, name, detail))
print(f"FAIL[{layer}] {name}: {detail}")
def drive(mode, fn, *args):
os.environ["RUNE_ME_CORE"] = mode
try:
return fn(*copy.deepcopy(args))
finally:
os.environ["RUNE_ME_CORE"] = "auto"
def frozen():
for c in TRUTH["should_fire_cases"]:
counts["frozen"] += 1
args = (c["args"]["trigger"], c["args"]["state"], c["args"]["now"])
py = drive("python", me.should_fire, *args)
rs = drive("auto", me.should_fire, *args)
if py != rs or py != c["expect"]:
fail("frozen", f"should_fire/{c['name']}", f"{py!r} != {rs!r}")
for c in TRUTH["fire_due_cases"]:
counts["frozen"] += 1
args = (c["args"]["state"], c["args"]["now"])
py = drive("python", me.fire_due, *args)
rs = drive("auto", me.fire_due, *args)
if py != rs or py != c["expect"]:
fail("frozen", f"fire_due/{c['name']}", f"{py!r} != {rs!r}")
for c in TRUTH["mark_fired_cases"]:
counts["frozen"] += 1
args = (c["args"]["state"], c["args"]["trigger"], c["args"]["now"])
py = drive("python", me.mark_fired, *args)
rs = drive("auto", me.mark_fired, *args)
if py != rs or py != c["expect"]:
fail("frozen", f"mark_fired/{c['name']}", f"{py!r} != {rs!r}")
TRIGGER_NAMES = list(me.TRIGGERS) + ["mystery", ""]
COND_KEYS = sorted({t[0] for t in me.TRIGGERS.values()})
STOP_KEYS = sorted({t[2] for t in me.TRIGGERS.values()})
VALUES = [True, False, 0, 1, "", "yes", None, 0.5]
def rand_state(rng):
state = {}
for k in COND_KEYS + STOP_KEYS:
if rng.random() < 0.3:
state[k] = rng.choice(VALUES)
for name in list(me.TRIGGERS):
if rng.random() < 0.3:
state[f"last_fired_{name}"] = rng.choice(
[0, 0.0, rng.uniform(-500, 1500), 999.0])
return state
def fuzz():
rng = random.Random(2479)
for i in range(400):
trigger = rng.choice(TRIGGER_NAMES)
state = rand_state(rng)
now = round(rng.uniform(-100, 5000), 4)
py = drive("python", me.should_fire, trigger, state, now)
rs = drive("auto", me.should_fire, trigger, state, now)
counts["fuzz"] += 1
if py != rs:
fail("fuzz", f"fire-{i}",
f"{trigger!r}@{now}: {py!r} != {rs!r}")
continue
py = drive("python", me.fire_due, state, now)
rs = drive("auto", me.fire_due, state, now)
counts["fuzz"] += 1
if py != rs:
fail("fuzz", f"due-{i}", f"{py!r} != {rs!r}")
continue
mark_trigger = rng.choice(TRIGGER_NAMES)
py = drive("python", me.mark_fired, state, mark_trigger, now)
rs = drive("auto", me.mark_fired, state, mark_trigger, now)
counts["fuzz"] += 1
if py != rs:
fail("fuzz", f"mark-{i}", f"{py!r} != {rs!r}")
frozen()
fuzz()
total = counts["frozen"] + counts["fuzz"]
print(f"parity: {total - len(fails)}/{total} "
f"(frozen {counts['frozen']}, fuzz {counts['fuzz']})")
sys.exit(1 if fails else 0)
+133
View File
@@ -0,0 +1,133 @@
#!/usr/bin/env python3
"""Freeze the meaningful_events oracle truth (dichotomy wave, #2479).
Generator law: RUNE_ME_CORE=python pinned, every input deep-copied,
every public function exercised across ALL branches:
* should_fire — unknown trigger; each of the 8 stop conditions; each
of the 8 absent conditions; rate limiting incl. Python int()
truncation (fractional remaining), future last_fired, last_fired 0;
falsy condition/stop values (0, "", None) vs truthy
* fire_due — table-order listing, subsets, none due, stop-blocked
* mark_fired — key set on copy (original untouched), unknown trigger
still records, explicit now
"""
import copy
import json
import os
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO / "src"))
sys.path.insert(0, str(REPO / "src" / "rune"))
os.environ["RUNE_ME_CORE"] = "python"
import meaningful_events as me # noqa: E402
cases_should_fire = []
cases_fire_due = []
cases_mark_fired = []
def case_fire(name, trigger, state, now):
trigger = copy.deepcopy(trigger)
state = copy.deepcopy(state)
now = copy.deepcopy(now)
cases_should_fire.append({
"name": name,
"args": {"trigger": trigger, "state": state, "now": now},
"expect": me.should_fire(copy.deepcopy(trigger),
copy.deepcopy(state),
copy.deepcopy(now)),
})
def case_due(name, state, now):
state = copy.deepcopy(state)
now = copy.deepcopy(now)
cases_fire_due.append({
"name": name,
"args": {"state": state, "now": now},
"expect": me.fire_due(copy.deepcopy(state), copy.deepcopy(now)),
})
def case_mark(name, state, trigger, now):
state = copy.deepcopy(state)
trigger = copy.deepcopy(trigger)
now = copy.deepcopy(now)
st = copy.deepcopy(state)
expect = me.mark_fired(st, copy.deepcopy(trigger), copy.deepcopy(now))
cases_mark_fired.append({
"name": name,
"args": {"state": state, "trigger": trigger, "now": now},
"expect": expect,
"input_unchanged": st == state,
})
# ── should_fire: every branch ───────────────────────────────────────────────
case_fire("unknown-trigger", "nope", {}, 1000.0)
for trig in me.TRIGGERS:
cond, limit, stop = me.TRIGGERS[trig]
case_fire(f"fires-{trig}", trig, {cond: True}, 1000.0)
case_fire(f"fires-with-old-last-{trig}", trig,
{cond: True, f"last_fired_{trig}": 0.0}, 1000.0)
case_fire(f"stop-met-{trig}", trig, {cond: True, stop: True}, 1000.0)
case_fire(f"stop-truthy-string-{trig}", trig, {cond: True, stop: "yes"}, 1000.0)
case_fire(f"stop-falsy-zero-not-met-{trig}", trig,
{cond: True, stop: 0}, 1000.0)
case_fire(f"condition-absent-{trig}", trig, {}, 1000.0)
case_fire(f"condition-falsy-empty-str-{trig}", trig, {cond: ""}, 1000.0)
case_fire(f"condition-falsy-null-{trig}", trig, {cond: None}, 1000.0)
case_fire(f"rate-limited-{trig}", trig,
{cond: True, f"last_fired_{trig}": 1000.0 - (limit - 100.0)}, 1000.0)
case_fire(f"rate-limited-exact-boundary-{trig}", trig,
{cond: True, f"last_fired_{trig}": 1000.0 - limit}, 1000.0)
case_fire("rate-truncation-fractional", "task_blockers",
{"task_blocked": True, "last_fired_task_blockers": 400.5}, 1000.0)
case_fire("rate-truncation-tiny-remaining", "owner_corrections",
{"correction_received": True, "last_fired_owner_corrections": 700.0001},
1000.0)
case_fire("future-last-rate-limited", "task_blockers",
{"task_blocked": True, "last_fired_task_blockers": 5000.0}, 1000.0)
case_fire("negative-now-last-fractional", "curiosity_candidates",
{"novel_material_present": True, "last_fired_curiosity_candidates": -100.25},
1000.0)
case_fire("float-now-and-last", "repeated_failure",
{"same_failure_3x": True, "last_fired_repeated_failure": 123.456},
789.012)
# ── fire_due: ordering + subsets ────────────────────────────────────────────
ALL_DUE = {cond: True for cond, _, _ in me.TRIGGERS.values()}
case_due("all-eight-due-table-order", ALL_DUE, 1000.0)
case_due("none-due", {}, 1000.0)
case_due("subset-due", {"task_blocked": True, "correction_received": 1}, 1000.0)
case_due("stop-blocks-some", {**ALL_DUE, "task_unblocked": True}, 1000.0)
case_due("rate-limits-block-some",
{cond: True for cond, _, _ in me.TRIGGERS.values()}
| {"last_fired_owner_corrections": 999.0}, 1000.0)
case_due("falsy-conditions-only",
{cond: 0 for cond, _, _ in me.TRIGGERS.values()}, 1000.0)
# ── mark_fired: copy semantics ──────────────────────────────────────────────
case_mark("mark-explicit-now", {"task_blocked": True}, "task_blockers", 1000.0)
case_mark("mark-unknown-trigger-still-records", {}, "mystery", 55.5)
case_mark("mark-overwrites-existing", {"last_fired_task_blockers": 1.0},
"task_blockers", 2000.0)
case_mark("mark-empty-state", {}, "owner_corrections", 0.25)
truth = {
"module": "meaningful_events",
"core_pin": "RUNE_ME_CORE=python",
"should_fire_cases": cases_should_fire,
"fire_due_cases": cases_fire_due,
"mark_fired_cases": cases_mark_fired,
}
out = REPO / "src" / "rust" / "meaningful_events" / "frozen_truth.json"
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(truth, indent=1, sort_keys=True) + "\n",
encoding="utf-8")
n = len(cases_should_fire) + len(cases_fire_due) + len(cases_mark_fired)
print(f"frozen {n} cases -> {out}")
+98
View File
@@ -0,0 +1,98 @@
#!/usr/bin/env python3
"""ctypes loader for the Rust meaningful_events core (dichotomy wave,
#2479 tranche). Mirrors broker_loader.py's contract:
FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
* me_should_fire({trigger, state, now}) -> {fires, reason}
* me_fire_due({state, now}) -> [trigger, ...]
* me_mark_fired({state, trigger, now}) -> state dict
The wall CLOCK is host-side by nature (now resolved by the facade —
null never crosses); the kernel owns the trigger table and the laws.
Free: capability_scoring's cs_free_string (shared process). Override the
.so with ME_RUST_LIB (a FILE path).
"""
from __future__ import annotations
import ctypes
import json
import os
from pathlib import Path
_REPO = Path(__file__).resolve().parents[2]
_CRATE_DIR = _REPO / "src" / "rust" / "meaningful_events"
_LIB_PATH = Path(
os.environ.get("ME_RUST_LIB", _CRATE_DIR / "target" / "release" / "libmeaningful_events.so")
)
_ERR_TYPES = {
"ValueError": ValueError,
"KeyError": KeyError,
"AttributeError": AttributeError,
"TypeError": TypeError,
}
_lib = None
class EventsCoreUnavailable(FileNotFoundError):
"""The Rust meaningful-events core is not built. Callers fall back
to Python."""
def available() -> bool:
return _LIB_PATH.is_file()
def _load() -> ctypes.CDLL:
global _lib
if _lib is not None:
return _lib
if not _LIB_PATH.is_file():
raise EventsCoreUnavailable(str(_LIB_PATH))
lib = ctypes.CDLL(str(_LIB_PATH))
for sym in ("me_should_fire", "me_fire_due", "me_mark_fired"):
getattr(lib, sym).argtypes = [ctypes.c_void_p]
getattr(lib, sym).restype = ctypes.c_void_p
try:
lib.cs_free_string
except AttributeError:
scoring = ctypes.CDLL(str(
_REPO / "src" / "rust" / "capability_scoring" / "target" / "release" / "libcapability_scoring.so"
))
scoring.cs_free_string.argtypes = [ctypes.c_void_p]
lib._free = scoring.cs_free_string
_lib = lib
return lib
def _call(sym: str, payload: dict):
lib = _load()
data = json.dumps(payload).encode()
buf = ctypes.create_string_buffer(data)
ptr = getattr(lib, sym)(ctypes.cast(buf, ctypes.c_void_p))
if not ptr:
raise RuntimeError(f"{sym} returned null")
out = ctypes.string_at(ptr).decode()
free = getattr(lib, "_free", None)
if free is not None:
free(ptr)
doc = json.loads(out)
if not doc.get("ok"):
etype = doc.get("type", "RuntimeError")
raise _ERR_TYPES.get(etype, RuntimeError)(doc.get("err", f"{sym} failed"))
return doc["value"]
def should_fire(trigger: str, state: dict, now: float) -> dict:
return _call("me_should_fire", {"trigger": trigger, "state": state,
"now": now})
def fire_due(state: dict, now: float) -> list:
return _call("me_fire_due", {"state": state, "now": now})
def mark_fired(state: dict, trigger: str, now: float) -> dict:
return _call("me_mark_fired", {"state": state, "trigger": trigger,
"now": now})
+42 -1
View File
@@ -15,6 +15,7 @@ approaching, not because a timer fired.
from __future__ import annotations
import os
import time
# Trigger classes with (condition_key, default_rate_limit_s, stop_key)
@@ -30,10 +31,36 @@ TRIGGERS = {
}
def _kernel():
"""The Rust meaningful-events core (me_loader) when RUNE_ME_CORE
allows it, else None — the trigger table, the should_fire law, the
fire_due ordering and the mark_fired copy law delegate there first
and fall back to the Python body below (same semantics). The wall
CLOCK is host-side by nature: `now` is resolved here (never null
across FFI), and an INT now keeps the Python floor in mark_fired
(the oracle stores the value as-is; the kernel wire is float)."""
if os.environ.get("RUNE_ME_CORE", "auto") == "python":
return None
try:
from rune import me_loader
if me_loader.available():
return me_loader
except Exception: # noqa: BLE001 — kernel failure → Python floor
return None
return None
def should_fire(trigger: str, state: dict, now: float | None = None) -> dict:
"""Evaluate one trigger against current state. Returns
{fires, reason}. Rate-limited, stop-conditioned."""
now = now if now is not None else time.time()
k = _kernel()
if k is not None:
try:
return k.should_fire(trigger, state, now)
except Exception: # noqa: BLE001 — kernel floor
pass
if trigger not in TRIGGERS:
return {"fires": False, "reason": f"unknown trigger: {trigger}"}
cond_key, rate_limit, stop_key = TRIGGERS[trigger]
@@ -50,12 +77,26 @@ def should_fire(trigger: str, state: dict, now: float | None = None) -> dict:
def fire_due(state: dict, now: float | None = None) -> list:
"""All triggers due right now (the endogenous cognition tick)."""
resolved = now if now is not None else time.time()
k = _kernel()
if k is not None:
try:
return k.fire_due(state, resolved)
except Exception: # noqa: BLE001 — kernel floor
pass
return [t for t in TRIGGERS
if should_fire(t, state, now)["fires"]]
if should_fire(t, state, resolved)["fires"]]
def mark_fired(state: dict, trigger: str, now: float | None = None) -> dict:
"""Record a firing (for rate limiting). Returns the updated state."""
k = _kernel()
if k is not None and (now is None or isinstance(now, float)):
try:
return k.mark_fired(state, trigger,
now if now is not None else time.time())
except Exception: # noqa: BLE001 — kernel floor
pass
state = dict(state)
state[f"last_fired_{trigger}"] = now if now is not None else time.time()
return state
+14
View File
@@ -0,0 +1,14 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "evidence_provenance"
version = "0.1.0"
[[package]]
name = "meaningful_events"
version = "0.1.0"
dependencies = [
"evidence_provenance",
]
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "meaningful_events"
version = "0.1.0"
edition = "2021"
description = "FULL parity port of src/rune/meaningful_events.py (dichotomy wave, #2479 tranche)"
[lib]
crate-type = ["rlib", "cdylib", "staticlib"]
[dependencies]
evidence_provenance = { path = "../evidence_provenance" }
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,153 @@
//! branch_battery — replay the FROZEN oracle truth VERBATIM
//! (frozen_truth.json, generated by scripts/gen_meaningful_events_truth.py
//! against the LIVE Python oracle with RUNE_ME_CORE=python pinned and
//! deep-copied inputs).
use crate::{fire_due, mark_fired, should_fire};
use evidence_provenance::pyjson::{self, Json};
const TRUTH: &str = include_str!("../frozen_truth.json");
fn truth() -> Json {
pyjson::parse(TRUTH).expect("frozen truth parses")
}
fn cases(group: &str) -> Vec<Json> {
match truth().get(group) {
Some(Json::Arr(items)) => items.clone(),
_ => panic!("truth group {group} missing"),
}
}
fn at<'a>(v: &'a Json, path: &[&str]) -> Option<&'a Json> {
let mut cur = v;
for k in path {
cur = match cur {
Json::Obj(pairs) => pairs
.iter()
.find(|(k2, _)| k2 == k)
.map(|(_, x)| x)?,
_ => return None,
};
}
Some(cur)
}
fn jstr(v: Option<&Json>) -> String {
match v {
Some(Json::Str(s)) => s.clone(),
_ => String::new(),
}
}
fn now_of(v: Option<&Json>) -> Option<f64> {
match v {
Some(Json::Num(n)) | Some(Json::Float(n)) => Some(*n),
_ => None,
}
}
/// Python-value equality on Json (order-insensitive objects).
fn peq(a: &Json, b: &Json) -> bool {
match (a, b) {
(Json::Null, Json::Null) => true,
(Json::Bool(x), Json::Bool(y)) => x == y,
(Json::Num(x), Json::Num(y)) | (Json::Float(x), Json::Float(y)) => x == y,
(Json::Float(x), Json::Num(y)) | (Json::Num(x), Json::Float(y)) => x == y,
(Json::Str(x), Json::Str(y)) => x == y,
(Json::Arr(x), Json::Arr(y)) => {
x.len() == y.len() && x.iter().zip(y).all(|(a, b)| peq(a, b))
}
(Json::Obj(x), Json::Obj(y)) => {
x.len() == y.len()
&& x.iter().all(|(k, v)| {
y.iter()
.find(|(k2, _)| k2 == k)
.map(|(_, v2)| peq(v, v2))
.unwrap_or(false)
})
}
_ => false,
}
}
fn assert_case(name: &str, want: &Json, got: &Json) {
assert!(
peq(want, got),
"case {name} diverged:\n want={}\n got={}",
pyjson::dumps(want),
pyjson::dumps(got),
);
}
#[test]
fn replay_should_fire() {
for c in cases("should_fire_cases") {
let got = should_fire(
&jstr(at(&c, &["args", "trigger"])),
at(&c, &["args", "state"]).expect("state"),
now_of(at(&c, &["args", "now"])),
);
assert_case(
&jstr(at(&c, &["name"])),
at(&c, &["expect"]).expect("expect"),
&got,
);
}
}
#[test]
fn replay_fire_due() {
for c in cases("fire_due_cases") {
let got = fire_due(
at(&c, &["args", "state"]).expect("state"),
now_of(at(&c, &["args", "now"])),
);
assert_case(
&jstr(at(&c, &["name"])),
at(&c, &["expect"]).expect("expect"),
&got,
);
}
}
#[test]
fn replay_mark_fired() {
for c in cases("mark_fired_cases") {
let got = mark_fired(
at(&c, &["args", "state"]).expect("state"),
&jstr(at(&c, &["args", "trigger"])),
now_of(at(&c, &["args", "now"])),
);
assert_case(
&jstr(at(&c, &["name"])),
at(&c, &["expect"]).expect("expect"),
&got,
);
// the copy law: the kernel takes &Json and cannot mutate, but
// the truth ALSO records the oracle's input-unchanged invariant
assert!(
matches!(at(&c, &["input_unchanged"]), Some(Json::Bool(true))),
"case {} oracle input was mutated",
jstr(at(&c, &["name"])),
);
}
}
#[test]
fn all_eight_triggers_in_table_order() {
let names: Vec<&str> = crate::TRIGGERS.iter().map(|t| t.name).collect();
assert_eq!(
names,
vec![
"unresolved_commitments",
"approaching_deadlines",
"repeated_failure",
"contradictions",
"curiosity_candidates",
"prediction_mismatches",
"task_blockers",
"owner_corrections",
]
);
}
+247
View File
@@ -0,0 +1,247 @@
//! meaningful_events — endogenous meaningful-event triggers (#48).
//!
//! FULL parity port of src/rune/meaningful_events.py (dichotomy wave,
//! #2479 tranche). The wall CLOCK is host-side by nature — `now` is
//! passed per op (null never reaches the kernel; the facade resolves
//! time.time() first). The kernel owns the eight-trigger table, the
//! should_fire law, the fire_due ordering and the mark_fired copy law.
//!
//! FFI (doc-envelope law): every call takes one JSON doc and returns
//! {"ok": true, "value": ...} | {"ok": false, "err": ..., "type": ...}.
//! * me_should_fire({trigger, state, now}) -> {fires, reason}
//! * me_fire_due({state, now}) -> [trigger, ...]
//! * me_mark_fired({state, trigger, now}) -> state dict
//!
//! Python laws replicated exactly:
//! * should_fire ordering: unknown → stop → absent → rate → fires
//! * truthiness on state values (0, "", null are ABSENT/NOT-MET)
//! * last = state["last_fired_{trigger}"] or 0 (falsy → 0)
//! * rate gate is STRICTLY `now - last < rate_limit` (equal fires)
//! * remaining = int(rate_limit - (now - last)) — Python int()
//! truncation TOWARD ZERO (matches `as i64` for this domain)
//! * fire_due lists TABLE ORDER (insertion order, not sorted)
//! * mark_fired: dict(state) shallow copy + key set (unknown
//! triggers still record)
//!
//! NOTE: no me_free_string export — callers free through capability_
//! scoring's cs_free_string (shared process allocator).
use evidence_provenance::pyjson::{self, Json};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
fn obj(pairs: Vec<(&str, Json)>) -> Json {
Json::Obj(pairs.into_iter().map(|(k, v)| (k.to_string(), v)).collect())
}
fn s(v: &str) -> Json {
Json::Str(v.to_string())
}
/// Phase B groundwork for the language-advantage pass: the trigger
/// table is typed records, not tuples — the compiler names every field.
pub struct Trigger {
pub name: &'static str,
pub condition_key: &'static str,
pub rate_limit_s: f64,
pub stop_key: &'static str,
}
/// The eight trigger classes, TABLE ORDER (the fire_due listing law).
pub const TRIGGERS: [Trigger; 8] = [
Trigger { name: "unresolved_commitments", condition_key: "has_open_commitments", rate_limit_s: 3600.0, stop_key: "commitments_resolved" },
Trigger { name: "approaching_deadlines", condition_key: "deadline_within_24h", rate_limit_s: 1800.0, stop_key: "deadline_passed_or_met" },
Trigger { name: "repeated_failure", condition_key: "same_failure_3x", rate_limit_s: 900.0, stop_key: "failure_pattern_broken" },
Trigger { name: "contradictions", condition_key: "contradiction_detected", rate_limit_s: 1800.0, stop_key: "contradiction_resolved" },
Trigger { name: "curiosity_candidates", condition_key: "novel_material_present", rate_limit_s: 7200.0, stop_key: "material_absorbed" },
Trigger { name: "prediction_mismatches", condition_key: "prediction_wrong", rate_limit_s: 1800.0, stop_key: "model_updated" },
Trigger { name: "task_blockers", condition_key: "task_blocked", rate_limit_s: 600.0, stop_key: "task_unblocked" },
Trigger { name: "owner_corrections", condition_key: "correction_received", rate_limit_s: 300.0, stop_key: "correction_absorbed" },
];
fn find(trigger: &str) -> Option<&'static Trigger> {
TRIGGERS.iter().find(|t| t.name == trigger)
}
/// Python truthiness on a JSON value (state.get() laws).
fn truthy(v: Option<&Json>) -> bool {
match v {
None | Some(Json::Null) => false,
Some(Json::Bool(b)) => *b,
Some(Json::Num(n)) => *n != 0.0,
Some(Json::Float(n)) => *n != 0.0,
Some(Json::Str(x)) => !x.is_empty(),
Some(Json::Arr(a)) => !a.is_empty(),
Some(Json::Obj(o)) => !o.is_empty(),
}
}
/// as_f64 over state/now values (Num and Float alike).
fn f64_of(v: Option<&Json>) -> Option<f64> {
match v {
Some(Json::Num(n)) | Some(Json::Float(n)) => Some(*n),
_ => None,
}
}
/// should_fire law. `now` is always Some (facade-resolved clock).
pub fn should_fire(trigger: &str, state: &Json, now: Option<f64>) -> Json {
let now = now.unwrap_or(0.0);
let Some(t) = find(trigger) else {
return obj(vec![
("fires", Json::Bool(false)),
("reason", s(&format!("unknown trigger: {trigger}"))),
]);
};
if truthy(state.get(t.stop_key)) {
return obj(vec![
("fires", Json::Bool(false)),
("reason", s(&format!("stop condition met: {}", t.stop_key))),
]);
}
if !truthy(state.get(t.condition_key)) {
return obj(vec![
("fires", Json::Bool(false)),
("reason", s(&format!("condition absent: {}", t.condition_key))),
]);
}
// last = state.get(f"last_fired_{trigger}", 0) — falsy → 0
let last = f64_of(state.get(&format!("last_fired_{trigger}"))).unwrap_or(0.0);
if now - last < t.rate_limit_s {
let remaining = (t.rate_limit_s - (now - last)) as i64;
return obj(vec![
("fires", Json::Bool(false)),
("reason", s(&format!("rate-limited ({remaining}s left)"))),
]);
}
obj(vec![
("fires", Json::Bool(true)),
("reason", s(&format!("condition holds: {}", t.condition_key))),
])
}
/// fire_due law: TABLE ORDER listing of due triggers.
pub fn fire_due(state: &Json, now: Option<f64>) -> Json {
let due: Vec<Json> = TRIGGERS
.iter()
.filter(|t| {
should_fire(t.name, state, now)
.get("fires")
.map(|f| f == &Json::Bool(true))
.unwrap_or(false)
})
.map(|t| s(t.name))
.collect();
Json::Arr(due)
}
/// mark_fired law: dict(state) shallow copy + `last_fired_{trigger}`.
pub fn mark_fired(state: &Json, trigger: &str, now: Option<f64>) -> Json {
let mut pairs: Vec<(String, Json)> = match state {
Json::Obj(pairs) => pairs.clone(),
_ => Vec::new(),
};
let stamp = Json::Float(now.unwrap_or(0.0));
let key = format!("last_fired_{trigger}");
if let Some(slot) = pairs.iter_mut().find(|(k, _)| *k == key) {
slot.1 = stamp;
} else {
pairs.push((key, stamp));
}
Json::Obj(pairs)
}
// ── FFI (doc-envelope) ───────────────────────────────────────────────────
fn envelope(result: Result<Json, (String, String)>) -> *mut c_char {
let body = match result {
Ok(value) => obj(vec![("ok", Json::Bool(true)), ("value", value)]),
Err((err, ty)) => obj(vec![
("ok", Json::Bool(false)),
("err", Json::Str(err)),
("type", Json::Str(ty)),
]),
};
let out = pyjson::dumps(&body);
match CString::new(out) {
Ok(c) => c.into_raw(),
Err(_) => CString::new(
r#"{"ok":false,"err":"nul in output","type":"RuntimeError"}"#,
)
.unwrap()
.into_raw(),
}
}
fn parse_doc(raw: &str) -> Result<Json, (String, String)> {
pyjson::parse(raw).map_err(|e| (e, "ValueError".to_string()))
}
fn read_arg(ptr: *const c_char) -> Result<String, (String, String)> {
if ptr.is_null() {
return Ok(String::new());
}
unsafe { CStr::from_ptr(ptr) }
.to_str()
.map(|s| s.to_string())
.map_err(|e| (e.to_string(), "ValueError".to_string()))
}
fn now_of(doc: &Json) -> Result<Option<f64>, (String, String)> {
match doc.get("now") {
None | Some(Json::Null) => Ok(None),
Some(v) => f64_of(Some(v))
.map(Some)
.ok_or_else(|| ("now must be a number or null".into(), "TypeError".into())),
}
}
fn state_of(doc: &Json) -> Result<&Json, (String, String)> {
match doc.get("state") {
Some(st @ Json::Obj(_)) => Ok(st),
_ => Err(("state must be an object".into(), "TypeError".into())),
}
}
macro_rules! ffi {
($name:ident, $body:expr) => {
#[no_mangle]
pub extern "C" fn $name(arg: *const c_char) -> *mut c_char {
let out: Result<Json, (String, String)> =
read_arg(arg).and_then(|raw| $body(&raw));
envelope(out)
}
};
}
ffi!(me_should_fire, |raw: &str| -> Result<Json, (String, String)> {
let doc = parse_doc(raw)?;
let trigger = match doc.get("trigger") {
Some(Json::Str(t)) => t.clone(),
_ => return Err(("trigger must be a string".into(), "TypeError".into())),
};
let state = state_of(&doc)?.clone();
let now = now_of(&doc)?;
Ok(should_fire(&trigger, &state, now))
});
ffi!(me_fire_due, |raw: &str| -> Result<Json, (String, String)> {
let doc = parse_doc(raw)?;
let state = state_of(&doc)?.clone();
let now = now_of(&doc)?;
Ok(fire_due(&state, now))
});
ffi!(me_mark_fired, |raw: &str| -> Result<Json, (String, String)> {
let doc = parse_doc(raw)?;
let state = state_of(&doc)?.clone();
let trigger = match doc.get("trigger") {
Some(Json::Str(t)) => t.clone(),
_ => return Err(("trigger must be a string".into(), "TypeError".into())),
};
let now = now_of(&doc)?;
Ok(mark_fired(&state, &trigger, now))
});
#[cfg(test)]
mod branch_battery;
+93
View File
@@ -0,0 +1,93 @@
"""Tests: meaningful_events.py Rust core port (dichotomy wave, #2479).
Frozen-truth battery: the 96 frozen-oracle cases replayed VERBATIM
through the facade in BOTH core modes (RUNE_ME_CORE=python floor vs the
Rust kernel), plus loader pin discipline, the fire→mark→quiet loop and
input-copy semantics.
"""
from __future__ import annotations
import copy
import json
import os
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
sys.path.insert(0, str(ROOT / "src" / "rune"))
import meaningful_events as me # noqa: E402
from rune import me_loader # noqa: E402
TRUTH = json.loads(
(ROOT / "src" / "rust" / "meaningful_events" / "frozen_truth.json")
.read_text())
@pytest.fixture()
def core_built():
if not me_loader.available():
pytest.skip("rust meaningful_events core not built")
return True
def drive(mode, fn, *args):
os.environ["RUNE_ME_CORE"] = mode
try:
return fn(*copy.deepcopy(args))
finally:
os.environ["RUNE_ME_CORE"] = "auto"
def test_loader_probe_and_pin(core_built, monkeypatch):
monkeypatch.delenv("RUNE_ME_CORE", raising=False)
assert me_loader.available()
assert me._kernel() is not None
monkeypatch.setenv("RUNE_ME_CORE", "python")
assert me._kernel() is None
def test_frozen_should_fire_both_ways(core_built):
for c in TRUTH["should_fire_cases"]:
args = (c["args"]["trigger"], c["args"]["state"], c["args"]["now"])
py = drive("python", me.should_fire, *args)
rs = drive("auto", me.should_fire, *args)
assert py == rs == c["expect"], c["name"]
def test_frozen_fire_due_both_ways(core_built):
for c in TRUTH["fire_due_cases"]:
args = (c["args"]["state"], c["args"]["now"])
py = drive("python", me.fire_due, *args)
rs = drive("auto", me.fire_due, *args)
assert py == rs == c["expect"], c["name"]
def test_frozen_mark_fired_both_ways(core_built):
for c in TRUTH["mark_fired_cases"]:
args = (c["args"]["state"], c["args"]["trigger"], c["args"]["now"])
py = drive("python", me.mark_fired, *args)
rs = drive("auto", me.mark_fired, *args)
assert py == rs == c["expect"], c["name"]
assert c["input_unchanged"] is True, c["name"]
def test_fire_mark_due_loop_both_ways(core_built):
for mode in ("python", "auto"):
state = {"task_blocked": True}
due = drive(mode, me.fire_due, state, 1000.0)
assert "task_blockers" in due
state = drive(mode, me.mark_fired, state, "task_blockers", 1000.0)
assert drive(mode, me.fire_due, state, 1000.0) == []
def test_rate_limit_string_law(core_built):
# int() truncation surfaces IN the reason string — verbatim law
r = drive("auto", me.should_fire, "task_blockers",
{"task_blocked": True, "last_fired_task_blockers": 400.5},
1000.0)
assert r == {"fires": False, "reason": "rate-limited (0s left)"}