feat(cognitive_budgeter): close PARTIAL gaps — dichotomy wave
Audit 1/3 PARTIAL -> 3/3 FULL (RUST). Ported the two missing fns name-preserving: to_dict (CognitiveBudget struct with untyped Value fields — the dataclass ctor validates nothing, so falsy/wrong-typed kwargs ride through; from_kwargs pins the ctor-defaulting law; 9-field projection in dataclass field order via preserve_order) and test_budgeter (the embedded smoke test: 4 scenarios, oracle asserts, py_float_str law — str(10.0) == '10.0'). New FFI: cb_to_dict (unknown-key TypeError crash parity) + cb_test; cb_loader gains to_dict()/test() wrappers; calculate_budget now builds the struct and returns to_dict() (output identical, keys field-ordered like the Python dict). Truth: 47-case truth_gap.json via the shared harness + cb_freeze_seams (JSON-safe wrappers — calculate_budget returns a dataclass instance the harness can't serialize) — byte-identical --verify AND fresh re-freeze. PIN LAW: this oracle reads RUNE_CB_CORE, not the harness's stem-derived pin. Battery 4/4 (legacy 14-case branch battery + gap battery incl. the oracle stdout pin); clippy -D warnings + fmt clean; live parity 57/57 ops (incl. sentinel-proof the delegation path is live — the oracle masks loader exceptions; ctor crash parity; CLI byte-identical both pins); Python battery green both pins + elastic_cognition adjacency 21 passed. Audit: cognitive_budgeter 3/3 FULL; programme 110/192 (57%). Evidence: verification/cbud-*.
This commit is contained in:
1 parent
7f63d45d38
commit
c5c64684b3
26 files changed
+2034
-34
No files matched your search
@@ -2,14 +2,14 @@
|
||||
|
||||
_Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL only when 80%+ of its Python functions have name-preserving counterparts in the Go/Rust implementation — directories without the functions are facades and count as NOT TOUCHED._
|
||||
|
||||
**FULL completion: 61%** (118/192 runtime modules at FULL)
|
||||
**FULL completion: 62%** (119/192 runtime modules at FULL)
|
||||
|
||||
Function-level detail: 73% (1678/2288 runtime functions matched) — secondary; includes in-flight work
|
||||
Function-level detail: 73% (1680/2288 runtime functions matched) — secondary; includes in-flight work
|
||||
|
||||
| Status | Modules | Lines | Meaning |
|
||||
|---|---|---|---|
|
||||
| FULL | 118 | 48886 | 80%+ of functions ported |
|
||||
| PARTIAL | 35 | 15146 | 20–80% — real logic exists, gaps named below |
|
||||
| FULL | 119 | 49107 | 80%+ of functions ported |
|
||||
| PARTIAL | 34 | 14925 | 20–80% — real logic exists, gaps named below |
|
||||
| NOT TOUCHED | 39 | 11471 | no name-preserving implementation |
|
||||
| Total runtime | 192 | 75503 | |
|
||||
|
||||
@@ -81,7 +81,6 @@ Function-level detail: 73% (1678/2288 runtime functions matched) — secondary;
|
||||
| graph_projection | 134 | 3/6 | _model_to_dicts, write_all, _now |
|
||||
| state_checkpoint | 182 | 6/9 | _sck_core, _load_ledger, _save_ledger |
|
||||
| warm_neuron_store | 122 | 3/6 | _db, neuron_slugs, handle_hook |
|
||||
| cognitive_budgeter | 221 | 1/3 | test_budgeter, to_dict |
|
||||
| inference_trace | 204 | 4/6 | record_trace, read_traces |
|
||||
| response_adapter | 222 | 5/7 | test_adapter, to_dict |
|
||||
| structural_migration | 77 | 2/4 | apply_migration, consent_brief |
|
||||
@@ -189,6 +188,7 @@ Function-level detail: 73% (1678/2288 runtime functions matched) — secondary;
|
||||
| model_registry | 253 | 8/8 | GO |
|
||||
| event_bus | 244 | 7/7 | GO |
|
||||
| openai_compat_transport | 227 | 9/9 | GO |
|
||||
| cognitive_budgeter | 221 | 3/3 | RUST |
|
||||
| evidence_probe | 213 | 9/9 | GO |
|
||||
| project_registry | 205 | 10/12 | RUST |
|
||||
| forge_client | 203 | 18/18 | GO |
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
"""cb freeze seams — PYTHONPATH-registered (sitecustomize) patch for
|
||||
rune.cognitive_budgeter during truth freeze/verify. The oracle's
|
||||
calculate_budget returns a CognitiveBudget INSTANCE, which the shared
|
||||
harness cannot JSON-serialize; these wrappers compose the same live calls
|
||||
with .to_dict() so every row stays JSON-safe. The oracle fns run
|
||||
unmodified underneath.
|
||||
|
||||
PIN LAW: this oracle reads RUNE_CB_CORE — NOT the harness's stem-derived
|
||||
RUNE_COGNITIVE_BUDGETER_CORE — so the pin must come from the environment
|
||||
(the generator sets it before import; --verify commands carry it too).
|
||||
"""
|
||||
import rune.cognitive_budgeter as _cb
|
||||
|
||||
|
||||
def _cb_gap_calculate(salience, context=None):
|
||||
return _cb.calculate_budget(salience, context).to_dict()
|
||||
|
||||
|
||||
def _cb_gap_budget(**kwargs):
|
||||
return _cb.CognitiveBudget(**kwargs).to_dict()
|
||||
|
||||
|
||||
_cb.cb_gap_calculate = _cb_gap_calculate
|
||||
_cb.cb_gap_budget = _cb_gap_budget
|
||||
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env python3
|
||||
"""check_cognitive_budgeter_parity.py — LIVE parity for the
|
||||
cognitive_budgeter gap-close (to_dict + test_budgeter + the calculate
|
||||
seam end to end).
|
||||
|
||||
Oracle side: src/rune/cognitive_budgeter.py with RUNE_CB_CORE=python
|
||||
(the pure-Python parity authority). Port side: the BUILT Rust .so reached
|
||||
through rune.cb_loader with the delegation LIVE (RUNE_CB_CORE unset/auto).
|
||||
The oracle checks the env var on every call, so one process flips the pin
|
||||
between captures.
|
||||
|
||||
Sections (all structural == unless noted):
|
||||
1. truth replay parity — every truth_gap.json row, oracle vs Rust
|
||||
2. ctor matrix extras — falsy / partial / type-passthrough kwargs
|
||||
3. crash parity — unknown ctor kwarg: Python TypeError message vs the
|
||||
cb_loader RuntimeError envelope message (str(e) law: type prefix gone)
|
||||
4. test_budgeter stdout — oracle subprocess (pinned `... test`) vs
|
||||
cb_loader.test()
|
||||
5. CLI budget subcommand — BYTE-identical stdout under both pins
|
||||
|
||||
Exit 0 iff 100% of ops match; exit 2 if the .so is not built (parity would
|
||||
be vacuously oracle-vs-oracle).
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
from rune import cb_loader # noqa: E402
|
||||
|
||||
OPS = [0, 0] # passed, total
|
||||
|
||||
|
||||
def check(label: str, got, want) -> bool:
|
||||
OPS[1] += 1
|
||||
ok = got == want
|
||||
if ok:
|
||||
OPS[0] += 1
|
||||
else:
|
||||
print(f"MISMATCH {label}:\n got: {got!r}\n want: {want!r}")
|
||||
return ok
|
||||
|
||||
|
||||
def oracle(fn, *args, **kwargs):
|
||||
"""Call the module with the Python oracle pinned."""
|
||||
old = os.environ.get("RUNE_CB_CORE")
|
||||
os.environ["RUNE_CB_CORE"] = "python"
|
||||
try:
|
||||
return fn(*args, **kwargs)
|
||||
finally:
|
||||
if old is None:
|
||||
os.environ.pop("RUNE_CB_CORE", None)
|
||||
else:
|
||||
os.environ["RUNE_CB_CORE"] = old
|
||||
|
||||
|
||||
def rust(fn, *args, **kwargs):
|
||||
"""Call the module with the delegation live (Rust .so via cb_loader)."""
|
||||
old = os.environ.get("RUNE_CB_CORE")
|
||||
os.environ["RUNE_CB_CORE"] = "auto"
|
||||
try:
|
||||
return fn(*args, **kwargs)
|
||||
finally:
|
||||
if old is None:
|
||||
os.environ.pop("RUNE_CB_CORE", None)
|
||||
else:
|
||||
os.environ["RUNE_CB_CORE"] = old
|
||||
|
||||
|
||||
def main() -> int:
|
||||
import rune.cognitive_budgeter as cb
|
||||
|
||||
if not cb_loader.available():
|
||||
print("Rust core not built — run cargo build --release first "
|
||||
"(parity would be vacuous)")
|
||||
return 2
|
||||
|
||||
truth = json.loads(
|
||||
(REPO / "src" / "rust" / "cognitive_budgeter" / "truth_gap.json").read_text()
|
||||
)
|
||||
|
||||
# 0. delegation path PROVEN live (not silent Python fallback): a
|
||||
# sentinel-returning loader must be picked up under auto. The oracle
|
||||
# masks loader exceptions (`except Exception: pass`), so env flipping
|
||||
# alone cannot distinguish Rust from fallback.
|
||||
real_calc = cb_loader.calculate
|
||||
cb_loader.calculate = lambda salience, context=None: {"start_tier": "RUST-SENTINEL"}
|
||||
got = rust(cb.calculate_budget, {"score": 0.5, "minimum_tier": "T9", "flags": []}, {})
|
||||
cb_loader.calculate = real_calc
|
||||
check("delegation path live", got.start_tier, "RUST-SENTINEL")
|
||||
|
||||
# 1. truth replay parity (oracle pinned vs Rust delegation, per row)
|
||||
for idx, case in enumerate(truth):
|
||||
fn = case["fn"]
|
||||
if fn == "cb_gap_calculate":
|
||||
args = case["args"]
|
||||
want = oracle(cb.calculate_budget, *args).to_dict()
|
||||
got = rust(cb.calculate_budget, *args).to_dict()
|
||||
elif fn == "cb_gap_budget":
|
||||
want = oracle(lambda **kw: cb.CognitiveBudget(**kw).to_dict(), **case["kwargs"])
|
||||
got = cb_loader.to_dict(case["kwargs"])
|
||||
elif fn == "CognitiveBudget.to_dict":
|
||||
state = {k: v for k, v in case["recv"].items() if k != "cls"}
|
||||
want = oracle(lambda **kw: cb.CognitiveBudget(**kw).to_dict(), **state)
|
||||
got = cb_loader.to_dict(state)
|
||||
else:
|
||||
raise AssertionError(f"unknown fn {fn}")
|
||||
check(f"truth[{idx}] {fn}", got, want)
|
||||
|
||||
# 2. ctor matrix extras
|
||||
for kwargs in (
|
||||
{},
|
||||
{"max_latency": 20}, # int in the float field rides through
|
||||
{"start_tier": None},
|
||||
{"cloud_allowed": 0}, # falsy int, not a bool
|
||||
{f: False for f in (
|
||||
"cloud_allowed", "verification_required", "tool_allowed")},
|
||||
{"max_attempts": "three"}, # wrong type rides as string
|
||||
):
|
||||
want = oracle(lambda **kw: cb.CognitiveBudget(**kw).to_dict(), **kwargs)
|
||||
got = cb_loader.to_dict(kwargs)
|
||||
check(f"ctor matrix {kwargs}", got, want)
|
||||
|
||||
# 3. crash parity: unknown ctor kwarg
|
||||
try:
|
||||
cb.CognitiveBudget(bogus=1)
|
||||
raise AssertionError("oracle ctor accepted an unknown kwarg")
|
||||
except TypeError as e:
|
||||
py_msg = str(e)
|
||||
try:
|
||||
cb_loader.to_dict({"bogus": 1})
|
||||
raise AssertionError("Rust ctor accepted an unknown kwarg")
|
||||
except RuntimeError as e:
|
||||
rs_msg = str(e)
|
||||
check("unknown-kwarg crash parity", rs_msg, py_msg)
|
||||
|
||||
# 4. test_budgeter stdout
|
||||
cli = [sys.executable, str(REPO / "src" / "rune" / "cognitive_budgeter.py"), "test"]
|
||||
env = dict(os.environ, RUNE_CB_CORE="python")
|
||||
proc = subprocess.run(cli, capture_output=True, text=True, env=env, check=True)
|
||||
want_lines = proc.stdout.splitlines()
|
||||
got_lines = cb_loader.test()
|
||||
check("test_budgeter stdout", got_lines, want_lines)
|
||||
|
||||
# 5. CLI budget subcommand — BYTE-identical under both pins
|
||||
sal = '{"score": 0.75, "minimum_tier": "T2", "flags": ["TASK_FAILURE"]}'
|
||||
ctx = '{"channel": "work", "urgency": "high", "risk": "high", "verification_available": true}'
|
||||
cli = [sys.executable, str(REPO / "src" / "rune" / "cognitive_budgeter.py"),
|
||||
"budget", sal, ctx]
|
||||
pinned = subprocess.run(cli, capture_output=True, text=True,
|
||||
env=dict(os.environ, RUNE_CB_CORE="python"), check=True)
|
||||
live = subprocess.run(cli, capture_output=True, text=True,
|
||||
env=dict(os.environ, RUNE_CB_CORE="auto"), check=True)
|
||||
check("CLI budget byte-identical (pinned vs live)", live.stdout, pinned.stdout)
|
||||
|
||||
print(f"parity: {OPS[0]}/{OPS[1]} ops identical")
|
||||
return 0 if OPS[0] == OPS[1] else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_cognitive_budgeter_gap_truth.py — gap-close truth battery for
|
||||
cognitive_budgeter (dichotomy wave #2479: to_dict + test_budgeter).
|
||||
|
||||
Freezes via the shared scripts/freeze_truth.py harness, with JSON-safe seam
|
||||
wrappers from scripts/cb_freeze_seams (the oracle's calculate_budget
|
||||
returns a CognitiveBudget instance the harness can't serialize):
|
||||
|
||||
* cb_gap_calculate — the full calculate_budget decision tree, incl. the
|
||||
context=None default branch, every risk/verification, flag, channel,
|
||||
urgency and score-threshold combination (plus the mixed-type flags
|
||||
membership quirk and previous_failures-has-no-effect pin)
|
||||
* cb_gap_budget — the CognitiveBudget(**kwargs) ctor-defaulting law:
|
||||
missing -> default; falsy (0, 0.0, False, "") and wrong-typed values
|
||||
ride through unchanged (the dataclass validates nothing)
|
||||
* CognitiveBudget.to_dict — receiver cases pinning the 9-field projection
|
||||
on explicitly reconstructed instance state
|
||||
|
||||
test_budgeter's stdout cannot ride the harness (it prints and returns
|
||||
None): pinned by the Rust battery (src/gap_battery.rs ORACLE_TEST_STDOUT,
|
||||
captured 2026-10-14) and re-captured live on every run by
|
||||
scripts/check_cognitive_budgeter_parity.py.
|
||||
|
||||
Verify (byte-identical law):
|
||||
PYTHONPATH=src:scripts/cb_freeze_seams RUNE_CB_CORE=python \\
|
||||
python3 scripts/freeze_truth.py --verify \\
|
||||
src/rust/cognitive_budgeter/truth_gap.json \\
|
||||
--module rune.cognitive_budgeter
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
|
||||
# Pin BEFORE the oracle import: freeze_truth's stem-derived pin
|
||||
# (RUNE_COGNITIVE_BUDGETER_CORE) does not reach this oracle.
|
||||
os.environ["RUNE_CB_CORE"] = "python"
|
||||
|
||||
from freeze_truth import freeze, write # noqa: E402
|
||||
|
||||
DEFAULTS = {
|
||||
"start_tier": "T0",
|
||||
"max_tier": "T4",
|
||||
"max_attempts": 3,
|
||||
"max_tokens": 4096,
|
||||
"max_latency": 30.0,
|
||||
"cloud_allowed": True,
|
||||
"verification_required": False,
|
||||
"tool_allowed": True,
|
||||
"attention_budget": 800,
|
||||
}
|
||||
|
||||
|
||||
def recv(**over):
|
||||
r = {"cls": "CognitiveBudget"}
|
||||
r.update(DEFAULTS)
|
||||
r.update(over)
|
||||
return r
|
||||
|
||||
|
||||
def main():
|
||||
cases = []
|
||||
# -- cb_gap_budget: the ctor-defaulting law -----------------------------
|
||||
cases.append(("cb_gap_budget", (), {})) # all defaults
|
||||
cases.append(("cb_gap_budget", (), {"start_tier": "T3", "max_tokens": 8192}))
|
||||
cases.append(("cb_gap_budget", (), {"max_attempts": 0, "max_tokens": 0, "attention_budget": 0})) # falsy ints
|
||||
cases.append(("cb_gap_budget", (), {"max_latency": 0.0, "cloud_allowed": False, "verification_required": True}))
|
||||
cases.append(("cb_gap_budget", (), {"start_tier": "", "max_tier": ""})) # falsy strings ride through
|
||||
cases.append(("cb_gap_budget", (), {"start_tier": 5, "max_latency": 30, "cloud_allowed": None})) # type passthrough
|
||||
cases.append(("cb_gap_budget", (), {"max_attempts": 4.5, "attention_budget": -1}))
|
||||
cases.append(("cb_gap_budget", (), {"tool_allowed": "yes"})) # wrong-typed bool rides as string
|
||||
|
||||
# -- CognitiveBudget.to_dict: receiver projection ------------------------
|
||||
cases.append({"fn": "CognitiveBudget.to_dict", "recv": recv(), "args": [], "kwargs": {}})
|
||||
cases.append({
|
||||
"fn": "CognitiveBudget.to_dict",
|
||||
"recv": recv(start_tier="T4", max_tier="T3", max_attempts=5, max_tokens=2048,
|
||||
max_latency=10.0, cloud_allowed=False, verification_required=True,
|
||||
tool_allowed=False, attention_budget=1000),
|
||||
"args": [], "kwargs": {},
|
||||
})
|
||||
cases.append({
|
||||
"fn": "CognitiveBudget.to_dict",
|
||||
"recv": recv(max_latency=20, start_tier=None, attention_budget=400),
|
||||
"args": [], "kwargs": {},
|
||||
})
|
||||
|
||||
# -- cb_gap_calculate: the decision tree ---------------------------------
|
||||
# context omitted -> oracle's context=None branch
|
||||
cases.append(("cb_gap_calculate", ({"score": 0.5, "minimum_tier": "T1", "flags": []},), {}))
|
||||
cases.append(("cb_gap_calculate", ({},), {})) # empty salience -> every default
|
||||
# max_tier: risk x verification_available
|
||||
for ctx in (
|
||||
{"risk": "high"},
|
||||
{"risk": "high", "verification_available": True},
|
||||
{"risk": "normal"},
|
||||
{"risk": "weird"},
|
||||
):
|
||||
cases.append(("cb_gap_calculate", ({"score": 0.0, "minimum_tier": "T0", "flags": []}, ctx), {}))
|
||||
# max_attempts: flags (mixed-type membership quirk)
|
||||
for flags in (
|
||||
[],
|
||||
["UNKNOWN_FLAG"],
|
||||
["TASK_FAILURE"],
|
||||
["REPEATED_RETRY"],
|
||||
["TASK_FAILURE", "REPEATED_RETRY"],
|
||||
["TASK_FAILURE", 5, None], # non-str members: membership still True
|
||||
):
|
||||
cases.append(("cb_gap_calculate", ({"score": 0.0, "minimum_tier": "T0", "flags": flags}, {}), {}))
|
||||
# max_tokens/tool_allowed: channel
|
||||
for channel in ({"channel": "work"}, {"channel": "conversation"},
|
||||
{"channel": "api"}, {}):
|
||||
cases.append(("cb_gap_calculate", ({"score": 0.0, "minimum_tier": "T0", "flags": []}, channel), {}))
|
||||
cases.append(("cb_gap_calculate", ({"score": 0.0, "minimum_tier": "T0", "flags": []}, {"tool_allowed": False, "channel": "work"}), {}))
|
||||
# max_latency: urgency
|
||||
for ctx in ({"urgency": "critical"}, {"urgency": "high"}, {"urgency": "normal"}, {"urgency": "whenever"}, {}):
|
||||
cases.append(("cb_gap_calculate", ({"score": 0.0, "minimum_tier": "T0", "flags": []}, ctx), {}))
|
||||
# verification_required / attention_budget: score thresholds
|
||||
for score in (0.0, 0.59, 0.6, 0.79, 0.8, 1.0):
|
||||
cases.append(("cb_gap_calculate", ({"score": score, "minimum_tier": "T0", "flags": []}, {}), {}))
|
||||
# cloud_allowed: HIGH_RISK_IRREVERSIBLE x verification
|
||||
cases.append(("cb_gap_calculate", ({"score": 0.0, "minimum_tier": "T0", "flags": ["HIGH_RISK_IRREVERSIBLE"]}, {"verification_available": False, "cloud_allowed": False}), {}))
|
||||
cases.append(("cb_gap_calculate", ({"score": 0.0, "minimum_tier": "T0", "flags": ["HIGH_RISK_IRREVERSIBLE"]}, {"verification_available": True, "cloud_allowed": False}), {}))
|
||||
# previous_failures is read but never used by the tree
|
||||
cases.append(("cb_gap_calculate", ({"score": 0.0, "minimum_tier": "T0", "flags": []}, {"previous_failures": 3}), {}))
|
||||
# start_tier passthrough
|
||||
cases.append(("cb_gap_calculate", ({"score": 0.1, "minimum_tier": "T4", "flags": []}, {}), {}))
|
||||
# the four embedded smoke scenarios
|
||||
cases.append(("cb_gap_calculate", ({"score": 0.0, "minimum_tier": "T0", "flags": []}, {"channel": "conversation", "urgency": "normal"}), {}))
|
||||
cases.append(("cb_gap_calculate", ({"score": 0.7, "minimum_tier": "T2", "flags": ["EXPLICIT_DIRECTIVE"]}, {"channel": "work", "urgency": "normal", "verification_available": True}), {}))
|
||||
cases.append(("cb_gap_calculate", ({"score": 0.9, "minimum_tier": "T3", "flags": ["HIGH_RISK_IRREVERSIBLE"]}, {"channel": "work", "urgency": "critical", "verification_available": False}), {}))
|
||||
cases.append(("cb_gap_calculate", ({"score": 0.8, "minimum_tier": "T3", "flags": ["TASK_FAILURE", "REPEATED_RETRY"]}, {"channel": "work", "urgency": "normal", "previous_failures": 2}), {}))
|
||||
|
||||
truth = freeze("rune.cognitive_budgeter", cases, oracle_module="rune.cognitive_budgeter")
|
||||
dest = REPO / "src/rust/cognitive_budgeter/truth_gap.json"
|
||||
write(truth, dest)
|
||||
print(f"wrote {len(truth)} cases -> {dest}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+29
-5
@@ -4,6 +4,10 @@ batch 5, #2479 tranche). Mirrors broker_loader.py's contract:
|
||||
|
||||
FFI contract (doc-envelope law):
|
||||
* cb_calculate({salience, context}) -> budget dict
|
||||
* cb_to_dict({kwargs}) -> CognitiveBudget(**kwargs).to_dict() dict
|
||||
(unknown keys raise RuntimeError carrying the dataclass TypeError
|
||||
message — crash parity)
|
||||
* cb_test({}) -> the embedded smoke test's printed lines (test_budgeter)
|
||||
|
||||
Override the .so with CB_RUST_LIB (a FILE path).
|
||||
"""
|
||||
@@ -38,17 +42,23 @@ def _load() -> ctypes.CDLL:
|
||||
lib = ctypes.CDLL(str(_LIB_PATH))
|
||||
lib.cb_calculate.argtypes = [ctypes.c_void_p]
|
||||
lib.cb_calculate.restype = ctypes.c_void_p
|
||||
lib.cb_to_dict.argtypes = [ctypes.c_void_p]
|
||||
lib.cb_to_dict.restype = ctypes.c_void_p
|
||||
lib.cb_test.argtypes = [ctypes.c_void_p]
|
||||
lib.cb_test.restype = ctypes.c_void_p
|
||||
_lib = lib
|
||||
return lib
|
||||
|
||||
|
||||
def calculate(salience: dict, context: dict | None = None) -> dict:
|
||||
def _call(fn_name: str, payload: dict):
|
||||
"""Envelope call: serialize payload, invoke fn, free the returned buffer
|
||||
via capability_scoring's shared cs_free_string, unpack {ok, value|err}."""
|
||||
lib = _load()
|
||||
data = json.dumps({"salience": salience, "context": context or {}}).encode()
|
||||
data = json.dumps(payload).encode()
|
||||
buf = ctypes.create_string_buffer(data)
|
||||
ptr = lib.cb_calculate(ctypes.cast(buf, ctypes.c_void_p))
|
||||
ptr = getattr(lib, fn_name)(ctypes.cast(buf, ctypes.c_void_p))
|
||||
if not ptr:
|
||||
raise RuntimeError("cb_calculate returned null")
|
||||
raise RuntimeError(f"{fn_name} returned null")
|
||||
out = ctypes.string_at(ptr).decode()
|
||||
try:
|
||||
scoring = ctypes.CDLL(str(
|
||||
@@ -60,5 +70,19 @@ def calculate(salience: dict, context: dict | None = None) -> dict:
|
||||
pass
|
||||
doc = json.loads(out)
|
||||
if not doc.get("ok"):
|
||||
raise RuntimeError(doc.get("err", "cb_calculate failed"))
|
||||
raise RuntimeError(doc.get("err", f"{fn_name} failed"))
|
||||
return doc["value"]
|
||||
|
||||
|
||||
def calculate(salience: dict, context: dict | None = None) -> dict:
|
||||
return _call("cb_calculate", {"salience": salience, "context": context or {}})
|
||||
|
||||
|
||||
def to_dict(kwargs: dict | None = None) -> dict:
|
||||
"""CognitiveBudget(**kwargs).to_dict() via the Rust core."""
|
||||
return _call("cb_to_dict", {"kwargs": kwargs or {}})
|
||||
|
||||
|
||||
def test() -> list[str]:
|
||||
"""The embedded smoke test's printed lines (test_budgeter port)."""
|
||||
return _call("cb_test", {})
|
||||
Generated
+23
@@ -9,6 +9,28 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
@@ -74,6 +96,7 @@ version = "1.0.151"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
|
||||
@@ -7,4 +7,8 @@ edition = "2021"
|
||||
crate-type = ["rlib", "cdylib", "staticlib"]
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1"
|
||||
# preserve_order: Python-dict insertion-order semantics — to_dict projects
|
||||
# the 9 budget fields in dataclass field order and cb_calculate's envelope
|
||||
# must serialize in that same order (byte-parity with the oracle CLI).
|
||||
# float_roundtrip: exact binary64 round-trips for max_latency rows.
|
||||
serde_json = { version = "1", features = ["preserve_order", "float_roundtrip"] }
|
||||
@@ -313,9 +313,20 @@ const TRUTH: &str = r###"{
|
||||
#[test]
|
||||
fn calculate_matches_oracle() {
|
||||
let truth: serde_json::Value = serde_json::from_str(TRUTH).expect("truth parses");
|
||||
for (idx, case) in truth["calculate"].as_array().expect("calculate array").iter().enumerate() {
|
||||
let salience = case.get("salience").cloned().unwrap_or(serde_json::Value::Null);
|
||||
let context = case.get("context").cloned().unwrap_or(serde_json::Value::Null);
|
||||
for (idx, case) in truth["calculate"]
|
||||
.as_array()
|
||||
.expect("calculate array")
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
let salience = case
|
||||
.get("salience")
|
||||
.cloned()
|
||||
.unwrap_or(serde_json::Value::Null);
|
||||
let context = case
|
||||
.get("context")
|
||||
.cloned()
|
||||
.unwrap_or(serde_json::Value::Null);
|
||||
let got = calculate_budget(&salience, &context);
|
||||
assert_eq!(got, case["out"], "calculate case {}", idx);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
//! cognitive_budgeter gap battery — closes the dichotomy audit gap
|
||||
//! (to_dict, test_budgeter). Replays truth_gap.json, frozen from the Python
|
||||
//! oracle via the shared scripts/freeze_truth.py harness (generator:
|
||||
//! scripts/gen_cognitive_budgeter_gap_truth.py; seams:
|
||||
//! scripts/cb_freeze_seams — cb_gap_calculate/cb_gap_budget JSON-safe
|
||||
//! wrappers around the dataclass-returning oracle fns), and pins the
|
||||
//! embedded smoke test's stdout.
|
||||
|
||||
use crate::{calculate_budget, test_budgeter, CognitiveBudget};
|
||||
use serde_json::Value;
|
||||
|
||||
const TRUTH: &str = include_str!("../truth_gap.json");
|
||||
|
||||
// Captured from the oracle 2026-10-14:
|
||||
// RUNE_CB_CORE=python python3 src/rune/cognitive_budgeter.py test (rc=0)
|
||||
const ORACLE_TEST_STDOUT: &[&str] = &[
|
||||
"Testing Cognitive Budgeter...",
|
||||
"Greeting: start=T0, max=T4, tokens=2048",
|
||||
"Directive: start=T2, max=T4, tokens=8192",
|
||||
"Emergency: start=T3, max=T4, latency=10.0",
|
||||
"Failure: start=T3, max_attempts=5",
|
||||
"✅ All tests passed!",
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn gap_truth_replay() {
|
||||
let truth: Vec<Value> = serde_json::from_str(TRUTH).expect("truth parses");
|
||||
assert!(!truth.is_empty(), "empty gap truth");
|
||||
for (idx, case) in truth.iter().enumerate() {
|
||||
let fn_name = case["fn"].as_str().unwrap_or_default();
|
||||
match fn_name {
|
||||
// seam wrapper: calculate_budget(salience, context=None).to_dict()
|
||||
"cb_gap_calculate" => {
|
||||
let args = case["args"].as_array().cloned().unwrap_or_default();
|
||||
let salience = args.first().cloned().unwrap_or(Value::Null);
|
||||
let context = args.get(1).cloned().unwrap_or(Value::Null);
|
||||
let got = calculate_budget(&salience, &context);
|
||||
assert_eq!(got, case["expected"], "case {} cb_gap_calculate", idx);
|
||||
}
|
||||
// seam wrapper: CognitiveBudget(**kwargs).to_dict() — the
|
||||
// dataclass ctor-defaulting law (missing -> default, falsy and
|
||||
// wrong-typed values ride through unchanged).
|
||||
"cb_gap_budget" => {
|
||||
let got = CognitiveBudget::from_kwargs(&case["kwargs"]).to_dict();
|
||||
assert_eq!(got, case["expected"], "case {} cb_gap_budget", idx);
|
||||
}
|
||||
// the method itself, on an explicitly reconstructed receiver
|
||||
"CognitiveBudget.to_dict" => {
|
||||
let recv = &case["recv"];
|
||||
let got = CognitiveBudget::from_kwargs(recv).to_dict();
|
||||
assert_eq!(got, case["expected"]["result"], "case {} to_dict", idx);
|
||||
for f in crate::BUDGET_FIELDS {
|
||||
assert_eq!(recv[f], case["expected"][f], "case {} attr {}", idx, f);
|
||||
}
|
||||
}
|
||||
other => panic!("unknown fn {} at case {}", other, idx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_budgeter_matches_oracle_stdout() {
|
||||
assert_eq!(test_budgeter(), ORACLE_TEST_STDOUT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_dict_field_order_is_python_dict_order() {
|
||||
let d = CognitiveBudget::default().to_dict();
|
||||
let keys: Vec<&str> = d
|
||||
.as_object()
|
||||
.expect("object")
|
||||
.keys()
|
||||
.map(|k| k.as_str())
|
||||
.collect();
|
||||
assert_eq!(keys, crate::BUDGET_FIELDS.to_vec());
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
//! cognitive_budgeter — budget kernels (dichotomy debt queue batch 5).
|
||||
//! PARITY port of src/rune/cognitive_budgeter.py calculate_budget (GO
|
||||
//! verdict, deterministic decision tree; salience/context stay Python).
|
||||
//! PARITY port of src/rune/cognitive_budgeter.py (GO verdict, deterministic
|
||||
//! decision tree; salience/context stay Python): calculate_budget,
|
||||
//! CognitiveBudget.to_dict and the embedded smoke test test_budgeter.
|
||||
|
||||
#[path = "branch_battery.rs"]
|
||||
#[cfg(test)]
|
||||
mod branch_battery;
|
||||
|
||||
#[path = "gap_battery.rs"]
|
||||
#[cfg(test)]
|
||||
mod gap_battery;
|
||||
|
||||
use serde_json::{json, Value};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_char;
|
||||
@@ -40,18 +45,100 @@ fn bool_of(v: Option<&Value>, dflt: bool) -> bool {
|
||||
|
||||
fn flags_of(v: Option<&Value>) -> Vec<String> {
|
||||
match v {
|
||||
Some(Value::Array(a)) => a.iter().filter_map(|i| i.as_str().map(String::from)).collect(),
|
||||
Some(Value::Array(a)) => a
|
||||
.iter()
|
||||
.filter_map(|i| i.as_str().map(String::from))
|
||||
.collect(),
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
const BUDGET_FIELDS: [&str; 9] = [
|
||||
"start_tier",
|
||||
"max_tier",
|
||||
"max_attempts",
|
||||
"max_tokens",
|
||||
"max_latency",
|
||||
"cloud_allowed",
|
||||
"verification_required",
|
||||
"tool_allowed",
|
||||
"attention_budget",
|
||||
];
|
||||
|
||||
/// CognitiveBudget — Python dataclass mirror. Fields are Values, not typed
|
||||
/// Rust scalars, because the dataclass ctor validates nothing:
|
||||
/// `CognitiveBudget(start_tier=5).to_dict()` emits the int 5 verbatim
|
||||
/// (type-passthrough rows are frozen in truth_gap.json).
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CognitiveBudget {
|
||||
pub start_tier: Value,
|
||||
pub max_tier: Value,
|
||||
pub max_attempts: Value,
|
||||
pub max_tokens: Value,
|
||||
pub max_latency: Value,
|
||||
pub cloud_allowed: Value,
|
||||
pub verification_required: Value,
|
||||
pub tool_allowed: Value,
|
||||
pub attention_budget: Value,
|
||||
}
|
||||
|
||||
fn field_of(kwargs: &Value, key: &str, dflt: Value) -> Value {
|
||||
kwargs.get(key).cloned().unwrap_or(dflt)
|
||||
}
|
||||
|
||||
impl CognitiveBudget {
|
||||
/// `CognitiveBudget(**kwargs)` — dataclass ctor law: a MISSING key takes
|
||||
/// the field default; a PRESENT value rides through unchanged (no
|
||||
/// coercion, falsy does NOT trigger the default).
|
||||
pub fn from_kwargs(kwargs: &Value) -> Self {
|
||||
CognitiveBudget {
|
||||
start_tier: field_of(kwargs, "start_tier", json!("T0")),
|
||||
max_tier: field_of(kwargs, "max_tier", json!("T4")),
|
||||
max_attempts: field_of(kwargs, "max_attempts", json!(3)),
|
||||
max_tokens: field_of(kwargs, "max_tokens", json!(4096)),
|
||||
max_latency: field_of(kwargs, "max_latency", json!(30.0)),
|
||||
cloud_allowed: field_of(kwargs, "cloud_allowed", json!(true)),
|
||||
verification_required: field_of(kwargs, "verification_required", json!(false)),
|
||||
tool_allowed: field_of(kwargs, "tool_allowed", json!(true)),
|
||||
attention_budget: field_of(kwargs, "attention_budget", json!(800)),
|
||||
}
|
||||
}
|
||||
|
||||
/// `to_dict` — Python CognitiveBudget.to_dict: the 9-field projection in
|
||||
/// dataclass field order (preserve_order keeps the map insertion-ordered,
|
||||
/// matching the Python dict).
|
||||
pub fn to_dict(&self) -> Value {
|
||||
json!({
|
||||
"start_tier": self.start_tier.clone(),
|
||||
"max_tier": self.max_tier.clone(),
|
||||
"max_attempts": self.max_attempts.clone(),
|
||||
"max_tokens": self.max_tokens.clone(),
|
||||
"max_latency": self.max_latency.clone(),
|
||||
"cloud_allowed": self.cloud_allowed.clone(),
|
||||
"verification_required": self.verification_required.clone(),
|
||||
"tool_allowed": self.tool_allowed.clone(),
|
||||
"attention_budget": self.attention_budget.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CognitiveBudget {
|
||||
fn default() -> Self {
|
||||
CognitiveBudget::from_kwargs(&json!({}))
|
||||
}
|
||||
}
|
||||
|
||||
/// calculate_budget port. salience={score, minimum_tier, flags},
|
||||
/// context={risk, verification_available, previous_failures, channel,
|
||||
/// urgency, cloud_allowed, tool_allowed}.
|
||||
pub fn calculate_budget(salience: &Value, context: &Value) -> Value {
|
||||
let score = f64_of(salience.get("score"), 0.0);
|
||||
let start_tier = s(salience.get("minimum_tier"));
|
||||
let start_tier = if start_tier.is_empty() { "T0" } else { start_tier };
|
||||
let start_tier = if start_tier.is_empty() {
|
||||
"T0"
|
||||
} else {
|
||||
start_tier
|
||||
};
|
||||
let flags = flags_of(salience.get("flags"));
|
||||
|
||||
let risk = s(context.get("risk"));
|
||||
@@ -76,7 +163,11 @@ pub fn calculate_budget(salience: &Value, context: &Value) -> Value {
|
||||
};
|
||||
|
||||
let channel = s(context.get("channel"));
|
||||
let channel = if channel.is_empty() { "conversation" } else { channel };
|
||||
let channel = if channel.is_empty() {
|
||||
"conversation"
|
||||
} else {
|
||||
channel
|
||||
};
|
||||
let max_tokens = if channel == "work" {
|
||||
8192
|
||||
} else if channel == "conversation" {
|
||||
@@ -86,7 +177,11 @@ pub fn calculate_budget(salience: &Value, context: &Value) -> Value {
|
||||
};
|
||||
|
||||
let urgency = s(context.get("urgency"));
|
||||
let urgency = if urgency.is_empty() { "normal" } else { urgency };
|
||||
let urgency = if urgency.is_empty() {
|
||||
"normal"
|
||||
} else {
|
||||
urgency
|
||||
};
|
||||
let max_latency = if urgency == "critical" {
|
||||
10.0
|
||||
} else if urgency == "high" {
|
||||
@@ -115,24 +210,96 @@ pub fn calculate_budget(salience: &Value, context: &Value) -> Value {
|
||||
400
|
||||
};
|
||||
|
||||
json!({
|
||||
"start_tier": start_tier,
|
||||
"max_tier": max_tier,
|
||||
"max_attempts": max_attempts,
|
||||
"max_tokens": max_tokens,
|
||||
"max_latency": max_latency,
|
||||
"cloud_allowed": cloud_allowed,
|
||||
"verification_required": verification_required,
|
||||
"tool_allowed": tool_allowed,
|
||||
"attention_budget": attention_budget,
|
||||
})
|
||||
let budget = CognitiveBudget {
|
||||
start_tier: json!(start_tier),
|
||||
max_tier: json!(max_tier),
|
||||
max_attempts: json!(max_attempts),
|
||||
max_tokens: json!(max_tokens),
|
||||
max_latency: json!(max_latency),
|
||||
cloud_allowed: json!(cloud_allowed),
|
||||
verification_required: json!(verification_required),
|
||||
tool_allowed: json!(tool_allowed),
|
||||
attention_budget: json!(attention_budget),
|
||||
};
|
||||
budget.to_dict()
|
||||
}
|
||||
|
||||
/// str(float) law: Python renders integral floats with a trailing ".0"
|
||||
/// (`str(10.0)` == "10.0"); Rust's Display for f64 gives "10".
|
||||
fn py_float_str(v: f64) -> String {
|
||||
if v == v.trunc() {
|
||||
format!("{:.1}", v)
|
||||
} else {
|
||||
format!("{}", v)
|
||||
}
|
||||
}
|
||||
|
||||
/// test_budgeter — Python test_budgeter: the module's embedded smoke test
|
||||
/// (greeting / directive / emergency / failure-retry scenarios). Asserts the
|
||||
/// oracle's invariants (panics on mismatch) and returns the exact lines the
|
||||
/// oracle prints.
|
||||
pub fn test_budgeter() -> Vec<String> {
|
||||
let mut out = vec!["Testing Cognitive Budgeter...".to_string()];
|
||||
|
||||
let salience = json!({"score": 0.0, "minimum_tier": "T0", "flags": []});
|
||||
let context = json!({"channel": "conversation", "urgency": "normal"});
|
||||
let budget = calculate_budget(&salience, &context);
|
||||
out.push(format!(
|
||||
"Greeting: start={}, max={}, tokens={}",
|
||||
s(budget.get("start_tier")),
|
||||
s(budget.get("max_tier")),
|
||||
i64_of(budget.get("max_tokens"), 0),
|
||||
));
|
||||
assert_eq!(budget["start_tier"], json!("T0"));
|
||||
assert!(i64_of(budget.get("max_tokens"), 0) <= 2048);
|
||||
|
||||
let salience = json!({"score": 0.7, "minimum_tier": "T2", "flags": ["EXPLICIT_DIRECTIVE"]});
|
||||
let context = json!({"channel": "work", "urgency": "normal", "verification_available": true});
|
||||
let budget = calculate_budget(&salience, &context);
|
||||
out.push(format!(
|
||||
"Directive: start={}, max={}, tokens={}",
|
||||
s(budget.get("start_tier")),
|
||||
s(budget.get("max_tier")),
|
||||
i64_of(budget.get("max_tokens"), 0),
|
||||
));
|
||||
assert_eq!(budget["start_tier"], json!("T2"));
|
||||
assert_eq!(budget["verification_required"], json!(true));
|
||||
|
||||
let salience = json!({"score": 0.9, "minimum_tier": "T3", "flags": ["HIGH_RISK_IRREVERSIBLE"]});
|
||||
let context =
|
||||
json!({"channel": "work", "urgency": "critical", "verification_available": false});
|
||||
let budget = calculate_budget(&salience, &context);
|
||||
out.push(format!(
|
||||
"Emergency: start={}, max={}, latency={}",
|
||||
s(budget.get("start_tier")),
|
||||
s(budget.get("max_tier")),
|
||||
py_float_str(f64_of(budget.get("max_latency"), 0.0)),
|
||||
));
|
||||
assert_eq!(budget["start_tier"], json!("T3"));
|
||||
assert!(f64_of(budget.get("max_latency"), 0.0) <= 10.0);
|
||||
|
||||
let salience =
|
||||
json!({"score": 0.8, "minimum_tier": "T3", "flags": ["TASK_FAILURE", "REPEATED_RETRY"]});
|
||||
let context = json!({"channel": "work", "urgency": "normal", "previous_failures": 2});
|
||||
let budget = calculate_budget(&salience, &context);
|
||||
out.push(format!(
|
||||
"Failure: start={}, max_attempts={}",
|
||||
s(budget.get("start_tier")),
|
||||
i64_of(budget.get("max_attempts"), 0),
|
||||
));
|
||||
assert!(i64_of(budget.get("max_attempts"), 0) >= 4);
|
||||
|
||||
out.push("✅ All tests passed!".to_string());
|
||||
out
|
||||
}
|
||||
|
||||
fn read_arg(ptr: *const c_char) -> Result<Value, String> {
|
||||
if ptr.is_null() {
|
||||
return Err("null argument pointer".into());
|
||||
}
|
||||
let x = unsafe { CStr::from_ptr(ptr) }.to_str().map_err(|e| e.to_string())?;
|
||||
let x = unsafe { CStr::from_ptr(ptr) }
|
||||
.to_str()
|
||||
.map_err(|e| e.to_string())?;
|
||||
serde_json::from_str(x).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
@@ -150,12 +317,49 @@ fn envelope(r: Result<Value, String>) -> *mut c_char {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn cb_calculate(arg: *const c_char) -> *mut c_char {
|
||||
let r = read_arg(arg).map(|a| {
|
||||
let salience = a.get("salience").cloned().unwrap_or(Value::Object(Default::default()));
|
||||
let context = a.get("context").cloned().unwrap_or(Value::Object(Default::default()));
|
||||
let salience = a
|
||||
.get("salience")
|
||||
.cloned()
|
||||
.unwrap_or(Value::Object(Default::default()));
|
||||
let context = a
|
||||
.get("context")
|
||||
.cloned()
|
||||
.unwrap_or(Value::Object(Default::default()));
|
||||
calculate_budget(&salience, &context)
|
||||
});
|
||||
envelope(r)
|
||||
}
|
||||
|
||||
/// cb_to_dict({"kwargs": {...}}) -> `CognitiveBudget(**kwargs).to_dict()`.
|
||||
/// Unknown keys reproduce the dataclass TypeError (crash parity); the first
|
||||
/// offending key follows the caller's serialized order — Python reports
|
||||
/// insertion order, so single-unknown-key calls are the pinned contract.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn cb_to_dict(arg: *const c_char) -> *mut c_char {
|
||||
let r = read_arg(arg).and_then(|a| {
|
||||
let kwargs = a.get("kwargs").cloned().unwrap_or_else(|| json!({}));
|
||||
if let Some(obj) = kwargs.as_object() {
|
||||
for k in obj.keys() {
|
||||
if !BUDGET_FIELDS.contains(&k.as_str()) {
|
||||
return Err(format!(
|
||||
"CognitiveBudget.__init__() got an unexpected keyword argument '{}'",
|
||||
k
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(CognitiveBudget::from_kwargs(&kwargs).to_dict())
|
||||
});
|
||||
envelope(r)
|
||||
}
|
||||
|
||||
/// cb_test({}) -> the embedded smoke test's printed lines (test_budgeter).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn cb_test(_arg: *const c_char) -> *mut c_char {
|
||||
envelope(Ok(Value::Array(
|
||||
test_budgeter().into_iter().map(Value::String).collect(),
|
||||
)))
|
||||
}
|
||||
|
||||
// NOTE: no free-symbol export (capability_scoring's cs_free_string serves
|
||||
// the whole process — duplicate symbols break the cdylib link).
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,67 @@
|
||||
"""cognitive_budgeter battery — replays truth_gap.json (frozen via the
|
||||
shared freeze_truth harness, scripts/gen_cognitive_budgeter_gap_truth.py)
|
||||
through the LIVE module. Green under BOTH pins: delegation live
|
||||
(RUNE_CB_CORE unset with the .so built) and oracle pinned
|
||||
(RUNE_CB_CORE=python) — the calculate rows ride the delegation chain when
|
||||
live, proving the seam end to end.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
from rune import cognitive_budgeter as cb # noqa: E402
|
||||
|
||||
TRUTH = json.loads(
|
||||
(REPO / "src" / "rust" / "cognitive_budgeter" / "truth_gap.json").read_text()
|
||||
)
|
||||
|
||||
|
||||
def _ensure_shims():
|
||||
"""The freeze seams (cb_gap_*) exist only under the seams PYTHONPATH;
|
||||
re-register equivalents against the live module when absent."""
|
||||
if hasattr(cb, "cb_gap_calculate"):
|
||||
return
|
||||
def cb_gap_calculate(salience, context=None):
|
||||
return cb.calculate_budget(salience, context).to_dict()
|
||||
def cb_gap_budget(**kwargs):
|
||||
return cb.CognitiveBudget(**kwargs).to_dict()
|
||||
cb.cb_gap_calculate = cb_gap_calculate
|
||||
cb.cb_gap_budget = cb_gap_budget
|
||||
|
||||
|
||||
_ensure_shims()
|
||||
|
||||
|
||||
def _rebuild_receiver(recv):
|
||||
inst = cb.CognitiveBudget.__new__(cb.CognitiveBudget)
|
||||
for key, value in recv.items():
|
||||
if key != "cls":
|
||||
setattr(inst, key, json.loads(json.dumps(value)))
|
||||
return inst
|
||||
|
||||
|
||||
def test_gap_truth_replay():
|
||||
assert TRUTH, "empty gap truth"
|
||||
for idx, case in enumerate(TRUTH):
|
||||
fn = case["fn"]
|
||||
if fn == "cb_gap_calculate":
|
||||
args = case["args"]
|
||||
got = cb.cb_gap_calculate(*args, **case["kwargs"])
|
||||
assert got == case["expected"], f"case {idx} cb_gap_calculate"
|
||||
elif fn == "cb_gap_budget":
|
||||
got = cb.cb_gap_budget(**case["kwargs"])
|
||||
assert got == case["expected"], f"case {idx} cb_gap_budget"
|
||||
elif fn == "CognitiveBudget.to_dict":
|
||||
got = _rebuild_receiver(case["recv"]).to_dict()
|
||||
assert got == case["expected"]["result"], f"case {idx} to_dict"
|
||||
else:
|
||||
raise AssertionError(f"unknown fn {fn} at case {idx}")
|
||||
|
||||
|
||||
def test_embedded_smoke_test():
|
||||
"""The oracle's own test fn passes against the live module."""
|
||||
cb.test_budgeter()
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1,78 @@
|
||||
# cognitive_budgeter dichotomy gap-close — verification evidence (2026-10-14)
|
||||
|
||||
Module: src/rune/cognitive_budgeter.py (221L, 3 fns) →
|
||||
src/rust/cognitive_budgeter (dichotomy wave #2479; worktree rune-wave-dh;
|
||||
contract factory-gap-cbud). Evidence prefix cbud- (cb- belongs to
|
||||
capability_broker and the original batch-5 port).
|
||||
|
||||
## Before / after
|
||||
|
||||
- Audit coverage: **1/3 PARTIAL → 3/3 FULL (RUST)** — missing were
|
||||
`test_budgeter, to_dict`; both ported name-preserving
|
||||
(`pub fn to_dict` / `pub fn test_budgeter` in lib.rs, snake-form exact).
|
||||
- Programme: FULL 109/192 → 110/192 (57%); fn-level 1632→1634/2284.
|
||||
|
||||
## What was ported
|
||||
|
||||
- `CognitiveBudget` struct — untyped Value fields (the dataclass ctor
|
||||
validates nothing; type-passthrough rows are frozen);
|
||||
`from_kwargs` = `CognitiveBudget(**kwargs)` ctor-defaulting law (missing
|
||||
→ default; falsy / wrong-typed values ride through unchanged);
|
||||
`to_dict` = the 9-field projection in dataclass field order
|
||||
(preserve_order feature added — precedent: behaviour_ledger et al.).
|
||||
- `test_budgeter` — the embedded smoke test (4 scenarios, oracle asserts,
|
||||
`py_float_str` law: Python `str(10.0)` == "10.0" vs Rust "10").
|
||||
- FFI: `cb_to_dict` (unknown-key → `CognitiveBudget.__init__() got an
|
||||
unexpected keyword argument '<k>'` — crash parity with the dataclass
|
||||
TypeError, str(e) law) and `cb_test` (smoke-test lines).
|
||||
- `calculate_budget` refactored to build the struct and return
|
||||
`to_dict()` — output identical (keys now field-ordered, matching Python
|
||||
dict order; previously BTreeMap-sorted).
|
||||
|
||||
## Verification gates (all green in THIS checkout)
|
||||
|
||||
| Gate | Result | Evidence |
|
||||
|---|---|---|
|
||||
| freeze_truth --verify (byte-identical vs live oracle) | OK: 47 cases | cbud-freeze-verify.{log,exit} |
|
||||
| fresh regeneration byte-identity | REGEN BYTE-IDENTICAL (cmp) | cbud-freeze-regen.{log,exit} |
|
||||
| cargo build --release (+ exported symbols) | clean; cb_calculate/cb_to_dict/cb_test exported | cbud-cargo-build.{log,exit} |
|
||||
| cargo clippy --release --all-targets -D warnings | clean | cbud-clippy.{log,exit} |
|
||||
| cargo fmt --check | clean | cbud-fmt.{log,exit} |
|
||||
| cargo test --release (branch battery 14-case legacy + gap battery) | 4/4 | cbud-cargo-test.{log,exit} |
|
||||
| LIVE parity (scripts/check_cognitive_budgeter_parity.py) | 57/57 ops identical | cbud-parity.{log,exit} |
|
||||
| Python battery, delegation live | 2 passed | cbud-pytests-live.{log,exit} |
|
||||
| Python battery, RUNE_CB_CORE=python pin | 2 passed | cbud-pytests-python.{log,exit} |
|
||||
| Oracle CLI smoke (`cognitive_budgeter.py test`, pinned) | all tests passed | cbud-oracle-smoke.{log,exit} |
|
||||
| Adjacency (tests/test_elastic_cognition.py — constructs CognitiveBudget) | 21 passed | cbud-adjacency.{log,exit} |
|
||||
| dichotomy_audit.py --write | cognitive_budgeter 3/3 RUST row | cbud-audit-write.{log,exit} |
|
||||
| ruff check (5 new/changed Python files) | all checks passed | cbud-ruff.{log,exit} |
|
||||
|
||||
## Truth-freeze notes
|
||||
|
||||
- Shared harness (scripts/freeze_truth.py) + seams
|
||||
scripts/cb_freeze_seams/sitecustomize.py (JSON-safe wrappers
|
||||
cb_gap_calculate / cb_gap_budget — the oracle's calculate_budget returns
|
||||
a CognitiveBudget instance the harness can't serialize; the oracle fns
|
||||
run unmodified underneath). 47 cases.
|
||||
- **PIN LAW**: this oracle reads `RUNE_CB_CORE`, not the harness's
|
||||
stem-derived `RUNE_COGNITIVE_BUDGETER_CORE` — pinned explicitly in the
|
||||
generator (before import) and in the --verify command env.
|
||||
- cb_gap_calculate rows cover the full decision tree incl. the
|
||||
context=None branch, mixed-type flags membership (["TASK_FAILURE", 5,
|
||||
None] → attempts 4) and the previous_failures-is-read-but-unused pin.
|
||||
- cb_gap_budget rows pin the ctor-defaulting law incl. falsy (0 / 0.0 /
|
||||
False / "") and type-passthrough (int in str field, None bool).
|
||||
- test_budgeter's stdout can't ride the harness (prints, returns None):
|
||||
pinned by the Rust battery (ORACLE_TEST_STDOUT, captured 2026-10-14,
|
||||
rc=0) and re-captured live by the parity script every run.
|
||||
- Parity section 0 PROVES the delegation path is live (sentinel loader
|
||||
picked up under auto) — necessary because the oracle masks loader
|
||||
exceptions with `except Exception: pass`.
|
||||
|
||||
## Representation divergences (documented, not bugs)
|
||||
|
||||
- cb_to_dict unknown-key error reports the first offending key in the
|
||||
caller's serialized order; Python reports insertion order — identical
|
||||
for single-unknown-key calls (the pinned contract).
|
||||
- None (JSON null) field values ride through in both languages
|
||||
(dataclass law) — frozen rows carry them as null.
|
||||
Reference in new issue
Block a user