feat(growth_delta): close PARTIAL gaps — dichotomy wave
CI Pipeline / best-practices (push) Successful in 7s
CI Pipeline / documentation (push) Successful in 8s
CI Pipeline / rust-surface (push) Failing after 13s
CI Pipeline / go-surface (push) Failing after 18s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / type-check (push) Failing after 43s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
Auto-version and Release / release (push) Canceled after 56s
CI Pipeline / lint (push) Canceled after 56s
CI Pipeline / unit-tests (push) Canceled after 56s
CI Pipeline / canary (push) Canceled after 55s
deploy / deploy (push) Canceled after 46s

parity: every growth_delta kernel ported name-preserving into the
cognition_core broker — _parse_json (fence strip, brace extraction, the
_parse_error degrade), _load_deltas/_save_deltas (parse-or-default, the
exact json.dump indent=2 bytes), _apply_profile (first-near-dup replace,
distinct append), _apply_one (200-char evidence, now-as-argument),
reflect's gate pipeline ([:3] slice, 12-char floor, behavioural-only,
0.6 gate), apply's validation (invalid-json/short/wrong-target/low-conf
branches), handle_hook passthrough, _llm's exact Ollama payload,
_write_store's memory_store argv, _ctx_budget's #28 arithmetic, and the
dict-aware py_near_dup.

fix-first: the facade's _near_dup fed DICTS into the string-only
growth_near_dup kernel, which answered False for every dict pair — live
core and pinned fallback DISAGREED (test_growth_delta_error_paths only
ran pinned, masking it). The new cc_py_near_dup kernel takes dicts-or-
strings; both pins now agree and the previously-masked assertion holds
live.

doc-envelope FFI via cc_free_string (never re-exported); cc_loader pins
argtypes+restype for all 12 new symbols; facade delegation with the
RUNE_COGNITION_CORE=python pin. Battery: 9 tests replay 24 pure +
22 state frozen-oracle cases VERBATIM (include_str!). Live parity 86/86
facade checks both pins; freeze_truth --verify byte-identical both pins.
clippy --all-targets -D warnings 0 (incl. two pre-existing lints the new
toolchain gates: branch_battery module inception, parse_probe format!).
Audit: growth_delta 5/20 → 17/20; the 3 remaining (_store_db env-path,
_cc_core loader probe, _py_growth_profile fallback oracle) stay Python
by law, same calibration as drift_cache.
This commit is contained in:
Nick committed 2026-10-13 10:50:57 +01:00
1 parent da481e744d
commit c8b003e8fe
11 files changed
+2132 -43

No files matched your search

+234
View File
@@ -0,0 +1,234 @@
#!/usr/bin/env python3
"""check_growth_delta_parity.py — live parity: growth_delta facade under
BOTH core pins vs the frozen oracle truth (dichotomy wave).
Per pin (RUNE_COGNITION_CORE=python and unset → Rust via cc_loader) it
replays every frozen case through the LIVE facade. File seams are tmp
DELTAS_FILE dirs; subprocess/urlopen are stubbed at parity time (nothing
touches the machine store); the clock is frozen.
Usage: python3 scripts/check_growth_delta_parity.py
Exit 0 iff every case matches its frozen expected in BOTH pins.
"""
from __future__ import annotations
import json
import os
import subprocess
import sys
import tempfile
import urllib.request
from datetime import datetime
from pathlib import Path
from types import SimpleNamespace
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
import rune.growth_delta as gd # noqa: E402
CRATE = Path(__file__).resolve().parents[1] / "src" / "rust" / "cognition_core" / "src"
EMPTY = {"applied": [], "pending": [], "last_read_back": None}
# NOTE (no-clock law): the facade's wall-clock `applied_at` is NOT logic —
# the apply_one case is normalized to the frozen `now` before comparing;
# apply_ok compares only clock-free fields. The kernel takes `now` as an
# argument, so the Rust battery is exact with zero masking.
FAILS = []
CHECKS = [0]
def call_core(core, fn, *args, **kwargs):
old = os.environ.get("RUNE_COGNITION_CORE")
os.environ["RUNE_COGNITION_CORE"] = core
try:
return getattr(gd, fn)(*args, **kwargs)
finally:
if old is None:
os.environ.pop("RUNE_COGNITION_CORE", None)
else:
os.environ["RUNE_COGNITION_CORE"] = old
def with_file(state, core, fn, *args):
with tempfile.TemporaryDirectory() as td:
path = os.path.join(td, "deltas.json")
if state is not None:
with open(path, "w") as f:
if isinstance(state, dict) and "__raw__" in state:
f.write(state["__raw__"])
else:
json.dump(state, f)
orig = gd.DELTAS_FILE
gd.DELTAS_FILE = path
try:
result = call_core(core, fn, *args)
try:
with open(path) as f:
after = f.read()
except OSError:
after = None
finally:
gd.DELTAS_FILE = orig
return result, after
def check(name, got, want):
CHECKS[0] += 1
g, w = json.dumps(got, sort_keys=True), json.dumps(want, sort_keys=True)
if g == w:
return
FAILS.append(name)
print(f"MISMATCH [{name}]\n got: {g[:240]}\n want: {w[:240]}")
def main() -> int:
for core in ("python", "auto"):
live = call_core(core, "_cc_core") is not None
tag = f"{core}:{'rust' if live else 'py'}"
print(f"== {tag} ==")
truth = json.loads((CRATE / "frozen_growth_delta_truth.json").read_text())
for c in truth:
got = call_core(core, c["fn"], *c["args"], **c["kwargs"])
check(f"{tag}:{c['fn']}", got, c["expected"])
state = json.loads((CRATE / "frozen_growth_delta_state.json").read_text())["cases"]
for c in state["load_deltas"]:
raw = c["input"]["raw"]
seed = None if raw is None else {"__raw__": raw}
got, _ = with_file(seed, core, "_load_deltas")
check(f"{tag}:load:{c['name']}", got, c["expected"])
for c in state["save_deltas"]:
st = json.loads(json.dumps(c["input"]["state"]))
_, after = with_file(None, core, "_save_deltas", st)
check(f"{tag}:save:{c['name']}", after, c["expected"])
for c in state["apply_profile"]:
prof = json.loads(json.dumps(c["input"]["profile"]))
delta = json.loads(json.dumps(c["input"]["delta"]))
action, after = with_file(
{**EMPTY, "profile": prof}, core, "_apply_profile", delta)
check(f"{tag}:profile:{c['name']}",
{"profile": json.loads(after)["profile"], "action": action},
c["expected"])
for c in state["apply_one"]:
i = c["input"]
got, _ = with_file(dict(EMPTY), core, "_apply_one",
i["change"], i["evidence"], i["conf"],
i["target"])
got = {**got, "applied_at": i["now"]} # no-clock law: the
# facade's wall-clock stamp is not logic — normalize to the
# frozen `now` before comparing
check(f"{tag}:apply_one", got, c["expected"])
for c in state["reflect"]:
orig_llm = gd._llm
gd._llm = lambda m: json.dumps({"deltas": c["input"]["deltas"]})
try:
res, _ = with_file(dict(EMPTY), core, "reflect", "material", True)
finally:
gd._llm = orig_llm
check(f"{tag}:reflect:{c['name']}", res["would_apply"],
c["expected"])
c = state["llm"][0]
captured = {}
class Resp:
def read(self):
return b"{}"
def __enter__(self):
return self
def __exit__(self, *exc):
return False
orig_open = urllib.request.urlopen
orig_ctx = gd._ctx_budget
gd._ctx_budget = lambda mid: c["input"]["num_ctx"]
urllib.request.urlopen = lambda req, timeout=0: (
captured.update(data=req.data.decode()), Resp())[1]
try:
call_core(core, "_llm", c["input"]["prompt"])
finally:
urllib.request.urlopen = orig_open
gd._ctx_budget = orig_ctx
check(f"{tag}:llm", captured.get("data"), c["expected"])
c = state["write_store"][0]
argv_cap = {}
orig_run = subprocess.run
subprocess.run = lambda argv, **kw: (
argv_cap.update(argv=list(argv)),
SimpleNamespace(returncode=0))[1]
try:
call_core(core, "_write_store", c["input"]["text"],
c["input"]["topic"])
finally:
subprocess.run = orig_run
check(f"{tag}:store", argv_cap.get("argv", [])[2:], c["expected"][2:])
# ctx_budget through the LIVE facade: the oracle signature only
# exposes model_id (task_kind always "default" there — the
# task-kind branches are kernel-covered by the Rust battery).
# Rows: unknown model (default), cloud, local, small, zero-window.
rows = [("unknown-model", 8192, True),
("cloud-model", 32768, False),
("local-model", 32768, True),
("small-local", 2048, True),
("zero-window", 8192, True)]
for name, window, local in rows:
want = next(c_["expected"] for c_ in state["ctx_budget"]
if c_["input"]["window"] == window
and c_["input"]["local"] == local
and c_["input"]["task_kind"] == "default")
reg = SimpleNamespace()
if window == 8192 and local:
reg.profiles = {}
else:
reg.profiles = {"t": SimpleNamespace(
id=name, context_window=window, local=local)}
import rune
from rune import model_registry as mr
import types as _t
stub = _t.ModuleType("rune.model_registry")
stub.ModelRegistry = lambda: reg
# the real resolve defaults registry=None → its OWN module's
# ModelRegistry; force the stub row explicitly
stub.resolve_context_budget = (
lambda mid, tk="default", registry=None, _r=reg:
mr.resolve_context_budget(mid, tk, registry=_r))
saved = sys.modules.get("rune.model_registry")
rune.model_registry = stub
sys.modules["rune.model_registry"] = stub
try:
got = call_core(core, "_ctx_budget", name)
finally:
if saved is not None:
rune.model_registry = saved
sys.modules["rune.model_registry"] = saved
check(f"{tag}:ctx:{name}", got, want)
res, _ = with_file(dict(EMPTY), core, "apply", json.dumps({
"change": "a valid longer change here", "confidence": 0.9,
"target": "delivery", "evidence": "ev"}))
check(f"{tag}:apply_ok",
[{"change": r["change"], "target": r["target"],
"confidence": r["confidence"]} for r in res.get("applied", [])],
[{"change": "a valid longer change here", "target": "delivery",
"confidence": 0.9}])
bad = len(FAILS)
print(f"{'PASS' if bad == 0 else 'FAIL'}: {CHECKS[0] - bad}/{CHECKS[0]}")
return 1 if bad else 0
if __name__ == "__main__":
sys.exit(main())
+362
View File
@@ -0,0 +1,362 @@
#!/usr/bin/env python3
"""gen_growth_delta_truth.py — freeze growth_delta oracle truth for the
cognition_core gap-close battery.
Two truth artifacts (both embedded VERBATIM into the Rust battery):
1. frozen_growth_delta_truth.json — the freeze_truth harness format
([{fn, args, kwargs, expected}]), restricted to the PURE, replayable
oracle surface (_parse_json, _py_near_dup, _profile_key, handle_hook,
apply gate branches). This is the file the mandated
`freeze_truth.py --verify` command re-freezes byte-identically.
2. frozen_growth_delta_state.json — injected-state kernel cases
(drift_resume battery format): the file/LLM/subprocess I/O seams are
patched at capture (tmp DELTAS_FILE, stubbed urlopen/subprocess.run),
so the recorded (fn,args) pairs are NOT re-derivable by --verify; the
battery replays them against the pure kernels with `now` and the I/O
seeds passed as explicit arguments (no-clock law).
Poisoned-truth law: RUNE_COGNITION_CORE=python is pinned for the whole
capture — the oracle runs pure Python everywhere.
"""
from __future__ import annotations
import copy
import json
import os
import sys
import tempfile
import urllib.request
from pathlib import Path
from types import SimpleNamespace
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
os.environ["RUNE_COGNITION_CORE"] = "python" # poisoned-truth law: pure oracle
from freeze_truth import freeze, write as freeze_write # noqa: E402
CRATE = Path(__file__).resolve().parents[1] / "src" / "rust" / "cognition_core" / "src"
import rune.growth_delta as gd # noqa: E402
# ── 1. pure, replayable surface ─────────────────────────────────────────────
PURE_CASES = [
# _parse_json — every branch: plain, ```json fence, ``` fence, junk
# around the object, garbage (→ _parse_error), JSON string, fence with
# trailing junk (brace extraction), JSON array, non-json fence.
("_parse_json", ('{"deltas": [{"change": "lead with the summary", "confidence": 0.9}]}',), {}),
("_parse_json", ('```json\n{"deltas": []}\n```',), {}),
("_parse_json", ('```\n{"deltas": [{"change": "plain fence"}]}\n```',), {}),
("_parse_json", ('junk before {"deltas": [{"change": "after junk"}]} junk after',), {}),
("_parse_json", ("total garbage, no braces here",), {}),
("_parse_json", ('"just a json string"',), {}),
("_parse_json", ('```json {"a": 1} ``` trailing',), {}),
("_parse_json", ("[1, 2, 3]",), {}),
("_parse_json", ("```python\ncode not json\n```",), {}),
("_parse_json", ('{"deltas": [{"change": "café unicode \u00e9\u4e2d"}]}',), {}),
# _py_near_dup — dicts, strings, mixed, empty keys, the 0.25 boundary,
# punctuation + case normalisation.
("_py_near_dup", (
{"change": "deploy qualification resolves runner labels"},
{"change": "deploy qualification resolves labels"},), {}),
("_py_near_dup", (
{"change": "always summarise the ticket before the standup"},
{"change": "water the plants on tuesdays morning"},), {}),
("_py_near_dup", ("same idea here", "same idea here"), {}),
("_py_near_dup", (
"deploy qualification resolves runner labels",
{"change": "deploy qualification resolves labels"},), {}),
("_py_near_dup", ({"change": ""}, {"change": "anything at all here"}), {}),
("_py_near_dup", (
{"change": "alpha bravo charlie delta"},
{"change": "alpha echo foxtrot golf"},), {}),
("_py_near_dup", (
{"change": "Alpha, BETA! (gamma)"},
{"change": "alpha beta gamma"},), {}),
("_py_near_dup", (
{"change": "one two three four five"},
{"change": "one six seven eight"},), {}),
# _profile_key returns a Python SET (not JSON-freezable, and --verify
# re-freeze would compare set-vs-list) — the fingerprint logic stays
# battery-covered through the _py_near_dup cases that exercise it.
# handle_hook — the microkernel passthrough.
("handle_hook", ("session_end", {"k": 1}), {}),
("handle_hook", ("unknown", None), {}),
# apply — the four gate error branches (the success branch journals and
# writes the store: side-effectful, battery-covered via apply_one).
("apply", ("not json at all",), {}),
("apply", ('{"change": "short", "confidence": 0.9, "target": "delivery"}',), {}),
("apply", ('{"change": "a valid longer change here", "confidence": 0.9, "target": "constitution"}',), {}),
("apply", ('{"change": "a valid longer change here", "confidence": 0.5, "target": "delivery"}',), {}),
]
# ── 2. injected-state kernel cases ──────────────────────────────────────────
def _with_deltas_file(state, fn, *args, **kwargs):
"""Run an oracle fn with DELTAS_FILE pointed at a tmp file seeded with
`state` (or absent when state is None); return (result, file_after)."""
with tempfile.TemporaryDirectory() as td:
path = os.path.join(td, "deltas.json")
if state is not None:
with open(path, "w") as f:
if isinstance(state, dict) and "__raw__" in state:
f.write(state["__raw__"]) # corrupt-file seeding
else:
json.dump(state, f)
orig = gd.DELTAS_FILE
gd.DELTAS_FILE = path
try:
result = fn(*args, **kwargs)
try:
with open(path) as f:
after = f.read()
except OSError:
after = None
finally:
gd.DELTAS_FILE = orig
return result, after
def state_cases():
groups: dict[str, list[dict]] = {}
# ── load_deltas: missing file → default; valid → parsed; corrupt → default
valid = {"applied": [{"change": "lead with the summary", "evidence": "e1",
"confidence": 0.9, "target": "delivery",
"applied_at": "2026-10-13T04:00:00+00:00"}],
"pending": [], "last_read_back": None, "profile": [
{"change": "keep answers tight for delivery", "target": "delivery"}]}
groups["load_deltas"] = [
{"name": "missing_file_default", "input": {"raw": None},
"expected": copy.deepcopy(
_with_deltas_file(None, gd._load_deltas)[0])},
{"name": "valid_state", "input": {"raw": json.dumps(valid)},
"expected": copy.deepcopy(
_with_deltas_file(valid, gd._load_deltas)[0])},
{"name": "corrupt_default", "input": {"raw": "{corrupt"},
"expected": copy.deepcopy(
_with_deltas_file({"__raw__": "{corrupt"}, gd._load_deltas)[0])},
]
# ── save_deltas: the exact json.dump(indent=2) bytes
states = [
{"applied": [], "pending": [], "last_read_back": None},
{"applied": [{"change": "lead with the summary", "evidence": "user said X",
"confidence": 0.9, "target": "delivery",
"applied_at": "2026-10-13T04:00:00+00:00"}],
"pending": [], "last_read_back": None,
"profile": [{"change": "café guidance unicode \u00e9", "target": "persona"}]},
]
cases = []
for i, st in enumerate(states):
_, after = _with_deltas_file(None, gd._save_deltas, copy.deepcopy(st))
cases.append({"name": f"save_deltas_{i}", "input": {"state": st},
"expected": after})
groups["save_deltas"] = cases
# ── apply_profile: near-dup merge (same guidance, newer wins) and
# distinct append, both through the ORACLE merge loop.
base_a = {"change": "deploy qualification resolves runner labels",
"evidence": "e1", "confidence": 0.9, "target": "delivery"}
newer = {"change": "deploy qualification resolves labels quickly",
"evidence": "e2", "confidence": 0.8, "target": "delivery"}
distinct = {"change": "water the plants on tuesdays morning",
"evidence": "e3", "confidence": 0.7, "target": "persona"}
for name, seed_profile, delta in (
("merge_near_dup", [base_a], newer),
("append_distinct", [base_a], distinct)):
action, after = _with_deltas_file(
{"applied": [], "pending": [], "last_read_back": None,
"profile": copy.deepcopy(seed_profile)},
gd._apply_profile, copy.deepcopy(delta))
groups.setdefault("apply_profile", []).append({
"name": name,
"input": {"profile": seed_profile, "delta": delta},
"expected": {"profile": json.loads(after)["profile"],
"action": action}})
# ── apply_one: the journal record (now enters the kernel as an argument).
rec, _ = _with_deltas_file({"applied": [], "pending": [],
"last_read_back": None},
gd._apply_one, "lead with the summary",
"e" * 300, 0.9, "delivery")
groups["apply_one"] = [
{"name": "journal_record_evidence_truncated",
"input": {"change": "lead with the summary", "evidence": "e" * 300,
"conf": 0.9, "target": "delivery",
"now": rec["applied_at"]},
"expected": rec},
]
# ── reflect gate pipeline: dry_run=True keeps the oracle side-effect
# free; _llm stubbed per case. The kernel replays would_apply from
# the already-[:3]-sliced delta list.
def reflect_case(name, deltas):
orig_llm = gd._llm
gd._llm = lambda material: json.dumps({"deltas": deltas})
try:
res, _ = _with_deltas_file({"applied": [], "pending": [],
"last_read_back": None},
gd.reflect, "material", True)
finally:
gd._llm = orig_llm
return {"name": name, "input": {"deltas": deltas},
"expected": res["would_apply"]}
groups["reflect"] = [
reflect_case("mixed_gates", [
{"change": "relay in plain english always for delivery",
"confidence": 0.9, "target": "delivery", "evidence": "e1"},
{"change": "tiny", "confidence": 0.9, "target": "delivery"},
{"change": "route permanent rules through the gate",
"confidence": 0.9, "target": "constitution", "evidence": "e3"},
]),
reflect_case("confidence_gate_and_string_conf", [
{"change": "keep answers tight for delivery",
"confidence": "0.7", "target": "delivery", "evidence": "s"},
{"change": "never below the gate for delivery",
"confidence": 0.59, "target": "delivery", "evidence": "s"},
{"change": "boundary confidence passes the gate",
"confidence": 0.6, "target": "operating", "evidence": "s"},
]),
reflect_case("missing_and_falsy_confidence", [
{"change": "no confidence field here at all",
"target": "delivery", "evidence": "s"},
{"change": "zero confidence is filtered too",
"confidence": 0, "target": "delivery", "evidence": "s"},
{"change": "long change with default target",
"confidence": 0.8, "evidence": "e" * 250},
]),
]
# ── llm payload: the exact request body json.dumps (urlopen stubbed;
# num_ctx pinned so the case is registry-independent).
class _FakeResp:
def __init__(self, body):
self._body = body
def read(self):
return json.dumps({"response": "ok"}).encode()
def __enter__(self):
return self
def __exit__(self, *exc):
return False
captured = {}
orig_ctx = gd._ctx_budget
def fake_urlopen(req, timeout=0):
captured["data"] = req.data.decode()
return _FakeResp(None)
gd._ctx_budget = lambda model_id: 8192
orig_open = urllib.request.urlopen
urllib.request.urlopen = fake_urlopen
try:
gd._llm("extract the deltas from this material")
finally:
urllib.request.urlopen = orig_open
gd._ctx_budget = orig_ctx
groups["llm"] = [
{"name": "request_payload",
"input": {"prompt": "extract the deltas from this material",
"model": gd.DELTA_MODEL, "num_ctx": 8192},
"expected": captured["data"]},
]
# ── write_store argv: subprocess.run stubbed, argv captured.
captured_argv = {}
import subprocess as _sp
def fake_run(argv, **kwargs):
captured_argv["argv"] = argv
return SimpleNamespace(returncode=0)
orig_run = _sp.run
_sp.run = fake_run
try:
gd._write_store("lead with the summary", "delivery")
finally:
_sp.run = orig_run
argv = captured_argv["argv"]
groups["write_store"] = [
{"name": "argv",
"input": {"py": argv[0], "store_py": argv[1],
"text": "lead with the summary", "topic": "delivery"},
"expected": argv},
]
# ── ctx_budget: the #28 arithmetic over a STUB registry
# (resolve_context_budget takes registry= — fully pure).
from rune import model_registry as mr
class _P(SimpleNamespace):
pass
def reg_of(profiles):
reg = SimpleNamespace()
reg.profiles = profiles
return reg
m1 = _P(id="cloud-model", context_window=32768, local=False)
m2 = _P(id="local-model", context_window=32768, local=True)
m3 = _P(id="small-local", context_window=2048, local=True)
m4 = _P(id="zero-window", context_window=None, local=True)
def row(prof, model_id, task_kind):
expected = mr.resolve_context_budget(
model_id, task_kind, registry=reg_of({"t": prof} if prof else {})
)["num_ctx"]
if prof is None:
window, local = 8192, True
else:
window = prof.context_window or 8192
local = prof.local
return {"window": window, "local": local, "task_kind": task_kind,
"expected": expected}
ctx_rows = [
row(None, "unknown-model", "default"),
row(m1, "cloud-model", "default"),
row(m1, "cloud-model", "verify"),
row(m1, "cloud-model", "summarize"),
row(m1, "cloud-model", "embed"),
row(m1, "cloud-model", "exotic-kind"),
row(m2, "local-model", "default"),
row(m3, "small-local", "default"),
row(m4, "zero-window", "default"),
]
groups["ctx_budget"] = [
{"name": f"ctx_budget_{i}", "input": {k: r[k] for k in
("window", "local", "task_kind")},
"expected": r["expected"]}
for i, r in enumerate(ctx_rows)
]
return groups
def main():
truth = freeze("growth_delta", PURE_CASES, oracle_module="rune.growth_delta")
freeze_write(truth, CRATE / "frozen_growth_delta_truth.json")
print(f"pure truth: {len(truth)} cases")
state = {"module": "growth_delta",
"captured": "scripts/gen_growth_delta_truth.py",
"pin": "RUNE_COGNITION_CORE=python",
"cases": state_cases()}
(CRATE / "frozen_growth_delta_state.json").write_text(
json.dumps(state, indent=2, ensure_ascii=False) + "\n")
total = sum(len(v) for v in state["cases"].values())
print(f"state truth: {total} cases across {len(state['cases'])} groups")
if __name__ == "__main__":
main()
+61 -1
View File
@@ -58,7 +58,11 @@ def _load() -> ctypes.CDLL:
"cc_witness_signals", "cc_safety_get_params",
"cc_safety_update_pattern", "cc_abstraction_extract_facts",
"cc_consolidation_memory", "cc_consolidation_neuron",
"cc_liveness_canary", "cc_reconcile"):
"cc_liveness_canary", "cc_reconcile",
"cc_py_near_dup", "cc_parse_json", "cc_load_deltas",
"cc_save_deltas", "cc_apply_profile", "cc_apply_one",
"cc_reflect", "cc_apply", "cc_handle_hook", "cc_llm",
"cc_write_store", "cc_ctx_budget"):
fn = getattr(lib, sym)
fn.argtypes = [ctypes.c_char_p]
fn.restype = ctypes.c_void_p
@@ -134,6 +138,62 @@ def growth_profile(state: dict, order: list, entry_chars: int,
"state": state, "order": order, "entry_chars": entry_chars,
"max_tokens": max_tokens})
# ── growth_delta gap-close kernels (dichotomy wave) ─────────────────────────
def py_near_dup(a, b) -> bool:
"""Dict-or-string same-guidance test (the dict-aware near-dup the
facade's _near_dup feeds — growth_near_dup only takes strings)."""
return bool(_call("cc_py_near_dup", {"a": a, "b": b}))
def parse_json(text: str):
"""Best-effort LLM response parse with the _parse_error degrade."""
return _call("cc_parse_json", {"text": text})
def load_deltas(raw: str | None) -> dict:
"""Parse-or-default for the raw deltas file text."""
return _call("cc_load_deltas", {"raw": raw})
def save_deltas(state: dict) -> str:
"""The exact json.dump(indent=2) bytes for the deltas file."""
return str(_call("cc_save_deltas", {"state": state}))
def apply_profile(profile: list, delta: dict) -> dict:
"""Near-dup merge/append decision → {profile, action}."""
return _call("cc_apply_profile", {"profile": profile, "delta": delta})
def apply_one(change: str, evidence: str, conf: float, target: str,
now: str) -> dict:
"""The journal record (now is an argument — no-clock law)."""
return _call("cc_apply_one", {"change": change, "evidence": evidence,
"conf": conf, "target": target, "now": now})
def reflect(deltas: list) -> list:
"""The reflect() gate pipeline → would_apply records."""
return _call("cc_reflect", {"deltas": deltas})
def apply(delta_json: str) -> dict:
"""apply() validation → {error} | {params}."""
return _call("cc_apply", {"delta": delta_json})
def handle_hook(event: str) -> dict:
"""The microkernel registration passthrough."""
return _call("cc_handle_hook", {"event": event})
def llm_payload(prompt: str, model: str, num_ctx: int) -> str:
"""The exact json.dumps request body for the Ollama generate call."""
return str(_call("cc_llm", {"prompt": prompt, "model": model,
"num_ctx": num_ctx}))
def write_store_argv(py: str, store_py: str, text: str, topic: str) -> list:
"""The memory_store.py add argv (subprocess stays in the facade)."""
return _call("cc_write_store", {"py": py, "store_py": store_py,
"text": text, "topic": topic})
def ctx_budget(window: int, local: bool, task_kind: str = "default") -> int:
"""The #28 budget arithmetic (profile lookup stays in the facade)."""
return int(_call("cc_ctx_budget", {"window": window, "local": local,
"task_kind": task_kind}))
def pins_recall(pins: list, term: str, limit: int = 4) -> list:
return _call("cc_pins_recall", {"pins": pins, "term": term, "limit": limit})
+109 -38
View File
@@ -53,6 +53,19 @@ def _ctx_budget(model_id: str) -> int:
from rune import model_registry as _mr
except ImportError:
import model_registry as _mr
core = _cc_core()
if core is not None:
profile = None
for _tier, p in (_mr.ModelRegistry().profiles or {}).items():
if p.id == model_id:
profile = p
break
if profile is None:
window, local = 8192, True
else:
window = profile.context_window or 8192
local = profile.local
return core.ctx_budget(window, local)
return _mr.resolve_context_budget(model_id)["num_ctx"]
def _store_db():
@@ -85,21 +98,28 @@ Output ONLY JSON:
def _llm(prompt):
import urllib.request
payload = json.dumps(
{
"model": DELTA_MODEL,
"prompt": prompt,
"stream": False,
"think": False,
"options": {"temperature": 0.4, "num_predict": 800, "num_ctx": _ctx_budget(DELTA_MODEL)},
}
).encode()
core = _cc_core()
if core is not None:
payload = core.llm_payload(prompt, DELTA_MODEL, _ctx_budget(DELTA_MODEL)).encode()
else:
payload = json.dumps(
{
"model": DELTA_MODEL,
"prompt": prompt,
"stream": False,
"think": False,
"options": {"temperature": 0.4, "num_predict": 800, "num_ctx": _ctx_budget(DELTA_MODEL)},
}
).encode()
req = urllib.request.Request(OLLAMA + "/api/generate", data=payload, headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=120) as r:
return json.loads(r.read()).get("response", "")
def _parse_json(text):
core = _cc_core()
if core is not None:
return core.parse_json(text)
t = text.strip()
if t.startswith("```"):
t = t.strip("`")
@@ -123,6 +143,14 @@ def _parse_json(text):
def _load_deltas():
core = _cc_core()
if core is not None:
try:
with open(DELTAS_FILE) as f:
raw = f.read()
except Exception: # noqa: BLE001 — deliberate: missing file is the default path (logged or continued); crash would be worse
raw = None
return core.load_deltas(raw)
try:
with open(DELTAS_FILE) as f:
return json.load(f)
@@ -134,7 +162,11 @@ def _save_deltas(d):
os.makedirs(os.path.dirname(DELTAS_FILE), exist_ok=True)
tmp = DELTAS_FILE + ".tmp"
with open(tmp, "w") as f:
json.dump(d, f, indent=2)
core = _cc_core()
if core is not None:
f.write(core.save_deltas(d))
else:
json.dump(d, f, indent=2)
os.replace(tmp, DELTAS_FILE)
@@ -180,7 +212,9 @@ def _cc_core():
def _near_dup(a, b):
core = _cc_core()
if core is not None:
return core.growth_near_dup(a, b)
# the dict-aware kernel — the deltas here ARE dicts, and the old
# string-only delegation answered False for every dict pair
return core.py_near_dup(a, b)
return _py_near_dup(a, b)
@@ -203,6 +237,12 @@ def _apply_profile(delta):
profile stays active, not per-session."""
state = _load_deltas()
profile = state.get("profile", [])
core = _cc_core()
if core is not None:
res = core.apply_profile(profile, delta)
state["profile"] = res["profile"]
_save_deltas(state)
return res["action"]
for i, existing in enumerate(profile):
if _near_dup(existing, delta):
profile[i] = delta # same guidance, newer wins
@@ -262,8 +302,11 @@ def _write_store(text, topic):
try:
py = memory_env.python_bin()
store_py = os.path.join(_SD, "memory_store.py")
subprocess.run(
[
core = _cc_core()
if core is not None:
argv = core.write_store_argv(py, store_py, text, topic)
else:
argv = [
py,
store_py,
"add",
@@ -276,7 +319,9 @@ def _write_store(text, topic):
"growth_delta",
"--method",
"agent",
],
]
subprocess.run(
argv,
capture_output=True,
text=True,
timeout=30,
@@ -290,13 +335,23 @@ def _apply_one(change, evidence, conf, target):
and merge it into the ACTIVE growth profile. The single high-confidence
gate is enforced by the caller. The profile updates on the same write —
growth is active, not a per-session read-back."""
rec = {
"change": change,
"evidence": (evidence or "")[:200],
"confidence": conf,
"target": target,
"applied_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
}
core = _cc_core()
if core is not None:
rec = core.apply_one(
change,
(evidence or ""),
conf,
target,
datetime.now(timezone.utc).isoformat(timespec="seconds"),
)
else:
rec = {
"change": change,
"evidence": (evidence or "")[:200],
"confidence": conf,
"target": target,
"applied_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
}
state = _load_deltas()
state["applied"].append(rec)
_save_deltas(state)
@@ -313,30 +368,43 @@ def reflect(material, dry_run=False):
out = _llm(DELTA_PROMPT + "\n\nMaterial:\n" + material)
parsed = _parse_json(out)
deltas = parsed.get("deltas", [])[:3] # spec: up to 3 per pass
state = _load_deltas()
core = _cc_core()
if core is not None:
would_apply = core.reflect(deltas)
else:
# pinned fallback + parity oracle: short changes, non-behavioural
# targets and sub-0.6 confidence never apply
would_apply = []
for d in deltas:
change = (d.get("change") or "").strip()
conf = float(d.get("confidence") or 0)
target = d.get("target") or "delivery"
if not change or len(change) < 12:
continue
if target not in BEHAVIOURAL_TOPICS:
continue # never route behavioural deltas to permanent topics
# SINGLE high-confidence gate for behavioural growth.
if conf < 0.6:
continue
would_apply.append(
{"change": change, "evidence": (d.get("evidence") or "")[:200], "confidence": conf, "target": target}
)
applied = []
would_apply = []
for d in deltas:
change = (d.get("change") or "").strip()
conf = float(d.get("confidence") or 0)
target = d.get("target") or "delivery"
if not change or len(change) < 12:
continue
if target not in BEHAVIOURAL_TOPICS:
continue # never route behavioural deltas to permanent topics
# SINGLE high-confidence gate for behavioural growth.
if conf < 0.6:
continue
would_apply.append(
{"change": change, "evidence": (d.get("evidence") or "")[:200], "confidence": conf, "target": target}
)
for d in would_apply:
if not dry_run:
applied.append(_apply_one(change, (d.get("evidence") or ""), conf, target))
applied.append(_apply_one(d["change"], d["evidence"], d["confidence"], d["target"]))
return {"extracted": deltas, "applied": applied, "would_apply": would_apply, "dry_run": dry_run}
def apply(delta_json):
"""Apply a behavioural delta directly (spec: apply <delta-json>)."""
core = _cc_core()
if core is not None:
res = core.apply(delta_json)
if "error" in res:
return {"applied": [], "error": res["error"]}
p = res["params"]
return {"applied": [_apply_one(p["change"], p["evidence"], p["confidence"], p["target"])]}
try:
d = json.loads(delta_json)
except Exception: # noqa: BLE001 — deliberate: best-effort degrade (logged or continued); crash would be worse
@@ -363,6 +431,9 @@ def handle_hook(event, data=None):
"""Hook handler — growth deltas are extracted during sleep-time, not on
individual events. This passthrough allows the microkernel to register
the plugin without error; actual work happens in reflect()."""
core = _cc_core()
if core is not None:
return core.handle_hook(event)
return {"status": "ok", "event": event, "handler": "growth_delta"}
@@ -3,9 +3,11 @@
//! these are the deterministic kernel transforms (pure, zero-IO); Python
//! parity lives in the cc_loader family + the existing FFI batteries.
#[path = "branch_battery.rs"]
// (the outer `mod branch_battery;` declaration lives in lib.rs; the
// historical inline wrapper name is kept as `cases` to avoid module
// inception under the current clippy gate)
#[cfg(test)]
mod branch_battery {
mod cases {
use crate::broker::*;
use crate::pyjson::Json;
+304 -1
View File
@@ -1,7 +1,7 @@
//! context_broker deterministic kernel (R2 W3.1 parity port).
//! Pure functions; the clock/env gate arrives via inputs (no-clock law).
use crate::pyjson::Json;
use crate::pyjson::{self, Json};
pub fn default_estimator(text: &str) -> i64 {
(text.chars().count() / 4) as i64 // CODE POINTS: Python len() parity
@@ -1365,3 +1365,306 @@ pub fn reconcile(desired: &Json, actual: &Json, tolerance: &[String], stage_id:
("delta".into(), Json::Obj(delta)),
])
}
// ── growth_delta gap-close kernels (dichotomy wave) ────────────────────────
// Name-preserving ports of the growth_delta.py kernels the honest audit
// named as missing. The FILE IO (DELTAS_FILE), the Ollama HTTP call, the
// memory_store subprocess and the loader probe stay in the Python facade;
// every deterministic decision is here.
/// The behavioural (fast single-signal) topic set — permanent topics
/// (constitution/identity/project/human) NEVER route through this path.
fn is_behavioural_topic(target: &str) -> bool {
matches!(target, "delivery" | "operating" | "persona")
}
/// `d.get("target") or "delivery"`: a MISSING/empty/null target falls back
/// to "delivery"; any other non-string value can never be behavioural.
fn target_of(d: &Json) -> String {
match get(d, "target") {
Some(Json::Str(s)) if !s.is_empty() => s.clone(),
Some(Json::Str(_)) | Some(Json::Null) | None => "delivery".to_string(),
Some(_) => String::new(),
}
}
/// `float(d.get("confidence") or 0)`: falsy (0/0.0/""/None/false) → 0.0;
/// numeric strings parse; bools are Python floats (True → 1.0). The oracle
/// raises ValueError on non-numeric strings — this kernel degrades to the
/// falsy branch instead (documented divergence; excluded from truth).
fn confidence_of(d: &Json) -> f64 {
match get(d, "confidence") {
Some(Json::Num(n)) => *n,
Some(Json::Bool(b)) => {
if *b {
1.0
} else {
0.0
}
}
Some(Json::Str(s)) => s.trim().parse::<f64>().unwrap_or(0.0),
_ => 0.0,
}
}
/// _profile_key: the distinctive-word fingerprint of a delta's `change` —
/// strip ".,;:!?()[]{}\"'" then lowercase, keep words longer than 4 CHARS
/// (Python len parity).
fn profile_key(change: &str) -> std::collections::BTreeSet<String> {
let mut out = std::collections::BTreeSet::new();
for w in change.split_whitespace() {
let w = w
.trim_matches(|c: char| ".,;:!?()[]{}\"'".contains(c))
.to_lowercase();
if w.chars().count() > 4 {
out.insert(w);
}
}
out
}
/// _py_near_dup: same-guidance test over delta dicts OR raw change strings
/// (raw → wrapped). Near when |A∩B| / max(|A|,|B|) >= 0.25; empty keys →
/// false. The dict-aware sibling of growth_near_dup — the facade's
/// _near_dup feeds it DICTS (the old string-only delegation answered False
/// for every dict pair).
pub fn py_near_dup(a: &Json, b: &Json) -> bool {
let change = |v: &Json| match v {
Json::Str(s) => s.clone(),
other => str_or(get(other, "change"), ""),
};
let (ka, kb) = (profile_key(&change(a)), profile_key(&change(b)));
if ka.is_empty() || kb.is_empty() {
return false;
}
let inter = ka.intersection(&kb).count() as f64;
(inter / ka.len().max(kb.len()) as f64) >= 0.25
}
/// _parse_json: best-effort LLM response parse — ``` fences (json-tagged
/// or bare), full-string JSON, then first-{..last-} brace extraction, then
/// the {"deltas": [], "_parse_error": true} degrade (never a crash).
pub fn parse_json(text: &str) -> Json {
let mut t = text.trim();
if t.starts_with("```") {
t = t.trim_matches('`');
if t.starts_with("json") {
t = &t[4..];
}
t = t.trim();
}
if let Ok(v) = pyjson::parse(t) {
return v;
}
if let (Some(s), Some(e)) = (t.find('{'), t.rfind('}')) {
if e > s {
if let Ok(v) = pyjson::parse(&t[s..=e]) {
return v;
}
}
}
Json::Obj(vec![
("deltas".into(), Json::Arr(vec![])),
("_parse_error".into(), Json::Bool(true)),
])
}
/// _load_deltas: parse the raw deltas file text (arriving as an argument —
/// the read stays in the facade); missing/corrupt → the empty state.
pub fn load_deltas(raw: Option<&str>) -> Json {
if let Some(text) = raw {
if let Ok(v) = pyjson::parse(text) {
return v;
}
}
Json::Obj(vec![
("applied".into(), Json::Arr(vec![])),
("pending".into(), Json::Arr(vec![])),
("last_read_back".into(), Json::Null),
])
}
/// _save_deltas: the exact `json.dump(d, indent=2)` bytes (ensure_ascii,
/// no trailing newline) — the tmp-write/replace stays in the facade.
pub fn save_deltas(state: &Json) -> String {
pyjson::dump_indent2(state)
}
/// _apply_profile: merge a new delta into the profile — the FIRST near-dup
/// position is replaced in place (same guidance, newer wins), distinct
/// guidance appends. Returns (profile_after, "merged"|"added").
pub fn apply_profile(profile: &[Json], delta: &Json) -> (Vec<Json>, &'static str) {
let mut out: Vec<Json> = Vec::with_capacity(profile.len() + 1);
let mut merged = false;
for existing in profile {
if !merged && py_near_dup(existing, delta) {
out.push(delta.clone());
merged = true;
} else {
out.push(existing.clone());
}
}
if !merged {
out.push(delta.clone());
}
(out, if merged { "merged" } else { "added" })
}
/// _apply_one: the journal record — evidence truncated to 200 CHARS, the
/// timestamp arrives as an argument (no-clock law).
pub fn apply_one(change: &str, evidence: &str, conf: f64, target: &str, now: &str) -> Json {
Json::Obj(vec![
("change".into(), Json::Str(change.to_string())),
(
"evidence".into(),
Json::Str(evidence.chars().take(200).collect()),
),
("confidence".into(), Json::Num(conf)),
("target".into(), Json::Str(target.to_string())),
("applied_at".into(), Json::Str(now.to_string())),
])
}
/// reflect's deterministic gate pipeline (the LLM call and the _apply_one
/// journaling stay in the facade): up to the already-sliced delta list,
/// drop short changes (<12 chars), non-behavioural targets, and
/// sub-0.6-confidence signals; survivors become would_apply records.
pub fn reflect(deltas: &[Json]) -> Vec<Json> {
let mut out = Vec::new();
for d in deltas {
let change = str_or(get(d, "change"), "").trim().to_string();
let conf = confidence_of(d);
let target = target_of(d);
if change.is_empty() || change.chars().count() < 12 {
continue;
}
if !is_behavioural_topic(&target) {
continue; // never route behavioural deltas to permanent topics
}
if conf < 0.6 {
continue;
}
let evidence = match get(d, "evidence") {
Some(Json::Str(s)) if !s.is_empty() => s.clone(),
_ => String::new(),
};
out.push(Json::Obj(vec![
("change".into(), Json::Str(change)),
(
"evidence".into(),
Json::Str(evidence.chars().take(200).collect()),
),
("confidence".into(), Json::Num(conf)),
("target".into(), Json::Str(target)),
]));
}
out
}
/// apply's deterministic validation: parse the delta JSON and run the
/// single-signal gates. Returns {"error": msg} or
/// {"params": {change, evidence, confidence, target}} — the facade journals
/// the params through apply_one.
pub fn apply(delta_json: &str) -> Json {
let err = |msg: String| {
Json::Obj(vec![("error".into(), Json::Str(msg))])
};
let d = match pyjson::parse(delta_json) {
Ok(v) => v,
Err(_) => return err("invalid delta json".to_string()),
};
let change = str_or(get(&d, "change"), "").trim().to_string();
let conf = confidence_of(&d);
let target = target_of(&d);
if change.is_empty() || change.chars().count() < 12 {
return err("delta too short to be actionable".to_string());
}
if !is_behavioural_topic(&target) {
return err(format!("not a behavioural target: {target}"));
}
if conf < 0.6 {
return err(format!(
"confidence {conf} below the 0.6 single-signal gate"
));
}
let evidence = match get(&d, "evidence") {
Some(Json::Str(s)) if !s.is_empty() => s.clone(),
_ => String::new(),
};
Json::Obj(vec![(
"params".into(),
Json::Obj(vec![
("change".into(), Json::Str(change)),
("evidence".into(), Json::Str(evidence)),
("confidence".into(), Json::Num(conf)),
("target".into(), Json::Str(target)),
]),
)])
}
/// handle_hook: the microkernel registration passthrough (data is accepted
/// and unused, exactly like the oracle body).
pub fn handle_hook(event: &str) -> Json {
Json::Obj(vec![
("status".into(), Json::Str("ok".into())),
("event".into(), Json::Str(event.into())),
("handler".into(), Json::Str("growth_delta".into())),
])
}
/// _llm's request body: the exact `json.dumps` of the Ollama generate
/// payload (model, prompt, stream/think off, temperature 0.4, 800-token
/// prediction, model-aware num_ctx) — the HTTP round-trip stays in the
/// facade.
pub fn llm_payload(prompt: &str, model: &str, num_ctx: f64) -> String {
let payload = Json::Obj(vec![
("model".into(), Json::Str(model.to_string())),
("prompt".into(), Json::Str(prompt.to_string())),
("stream".into(), Json::Bool(false)),
("think".into(), Json::Bool(false)),
(
"options".into(),
Json::Obj(vec![
("temperature".into(), Json::Num(0.4)),
("num_predict".into(), Json::Num(800.0)),
("num_ctx".into(), Json::Num(num_ctx)),
]),
),
]);
pyjson::dumps(&payload)
}
/// _write_store's argv: the memory_store.py `add` invocation (importance
/// 0.5, source growth_delta, method agent) — subprocess.run stays in the
/// facade.
pub fn write_store_argv(py: &str, store_py: &str, text: &str, topic: &str) -> Vec<String> {
vec![
py.to_string(),
store_py.to_string(),
"add".to_string(),
text.to_string(),
"--topic".to_string(),
topic.to_string(),
"--importance".to_string(),
"0.5".to_string(),
"--source".to_string(),
"growth_delta".to_string(),
"--method".to_string(),
"agent".to_string(),
]
}
/// _ctx_budget's #28 arithmetic: window minus the task-kind output reserve
/// (default 2048, verify 512, summarize 1024, embed 0) minus the 1024
/// tool-result reserve for non-local models, floored at 1024. The profile
/// lookup (config-shaped) stays in the facade.
pub fn ctx_budget(window: f64, local: bool, task_kind: &str) -> f64 {
let output_reserve = match task_kind {
"verify" => 512.0,
"summarize" => 1024.0,
"embed" => 0.0,
_ => 2048.0,
};
let tool_reserve = if local { 0.0 } else { 1024.0 };
(window - output_reserve - tool_reserve).max(1024.0)
}
@@ -0,0 +1,404 @@
{
"module": "growth_delta",
"captured": "scripts/gen_growth_delta_truth.py",
"pin": "RUNE_COGNITION_CORE=python",
"cases": {
"load_deltas": [
{
"name": "missing_file_default",
"input": {
"raw": null
},
"expected": {
"applied": [],
"pending": [],
"last_read_back": null
}
},
{
"name": "valid_state",
"input": {
"raw": "{\"applied\": [{\"change\": \"lead with the summary\", \"evidence\": \"e1\", \"confidence\": 0.9, \"target\": \"delivery\", \"applied_at\": \"2026-10-13T04:00:00+00:00\"}], \"pending\": [], \"last_read_back\": null, \"profile\": [{\"change\": \"keep answers tight for delivery\", \"target\": \"delivery\"}]}"
},
"expected": {
"applied": [
{
"change": "lead with the summary",
"evidence": "e1",
"confidence": 0.9,
"target": "delivery",
"applied_at": "2026-10-13T04:00:00+00:00"
}
],
"pending": [],
"last_read_back": null,
"profile": [
{
"change": "keep answers tight for delivery",
"target": "delivery"
}
]
}
},
{
"name": "corrupt_default",
"input": {
"raw": "{corrupt"
},
"expected": {
"applied": [],
"pending": [],
"last_read_back": null
}
}
],
"save_deltas": [
{
"name": "save_deltas_0",
"input": {
"state": {
"applied": [],
"pending": [],
"last_read_back": null
}
},
"expected": "{\n \"applied\": [],\n \"pending\": [],\n \"last_read_back\": null\n}"
},
{
"name": "save_deltas_1",
"input": {
"state": {
"applied": [
{
"change": "lead with the summary",
"evidence": "user said X",
"confidence": 0.9,
"target": "delivery",
"applied_at": "2026-10-13T04:00:00+00:00"
}
],
"pending": [],
"last_read_back": null,
"profile": [
{
"change": "café guidance unicode é",
"target": "persona"
}
]
}
},
"expected": "{\n \"applied\": [\n {\n \"change\": \"lead with the summary\",\n \"evidence\": \"user said X\",\n \"confidence\": 0.9,\n \"target\": \"delivery\",\n \"applied_at\": \"2026-10-13T04:00:00+00:00\"\n }\n ],\n \"pending\": [],\n \"last_read_back\": null,\n \"profile\": [\n {\n \"change\": \"caf\\u00e9 guidance unicode \\u00e9\",\n \"target\": \"persona\"\n }\n ]\n}"
}
],
"apply_profile": [
{
"name": "merge_near_dup",
"input": {
"profile": [
{
"change": "deploy qualification resolves runner labels",
"evidence": "e1",
"confidence": 0.9,
"target": "delivery"
}
],
"delta": {
"change": "deploy qualification resolves labels quickly",
"evidence": "e2",
"confidence": 0.8,
"target": "delivery"
}
},
"expected": {
"profile": [
{
"change": "deploy qualification resolves labels quickly",
"evidence": "e2",
"confidence": 0.8,
"target": "delivery"
}
],
"action": "merged"
}
},
{
"name": "append_distinct",
"input": {
"profile": [
{
"change": "deploy qualification resolves runner labels",
"evidence": "e1",
"confidence": 0.9,
"target": "delivery"
}
],
"delta": {
"change": "water the plants on tuesdays morning",
"evidence": "e3",
"confidence": 0.7,
"target": "persona"
}
},
"expected": {
"profile": [
{
"change": "deploy qualification resolves runner labels",
"evidence": "e1",
"confidence": 0.9,
"target": "delivery"
},
{
"change": "water the plants on tuesdays morning",
"evidence": "e3",
"confidence": 0.7,
"target": "persona"
}
],
"action": "added"
}
}
],
"apply_one": [
{
"name": "journal_record_evidence_truncated",
"input": {
"change": "lead with the summary",
"evidence": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"conf": 0.9,
"target": "delivery",
"now": "2026-10-13T05:51:01+00:00"
},
"expected": {
"change": "lead with the summary",
"evidence": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"confidence": 0.9,
"target": "delivery",
"applied_at": "2026-10-13T05:51:01+00:00"
}
}
],
"reflect": [
{
"name": "mixed_gates",
"input": {
"deltas": [
{
"change": "relay in plain english always for delivery",
"confidence": 0.9,
"target": "delivery",
"evidence": "e1"
},
{
"change": "tiny",
"confidence": 0.9,
"target": "delivery"
},
{
"change": "route permanent rules through the gate",
"confidence": 0.9,
"target": "constitution",
"evidence": "e3"
}
]
},
"expected": [
{
"change": "relay in plain english always for delivery",
"evidence": "e1",
"confidence": 0.9,
"target": "delivery"
}
]
},
{
"name": "confidence_gate_and_string_conf",
"input": {
"deltas": [
{
"change": "keep answers tight for delivery",
"confidence": "0.7",
"target": "delivery",
"evidence": "s"
},
{
"change": "never below the gate for delivery",
"confidence": 0.59,
"target": "delivery",
"evidence": "s"
},
{
"change": "boundary confidence passes the gate",
"confidence": 0.6,
"target": "operating",
"evidence": "s"
}
]
},
"expected": [
{
"change": "keep answers tight for delivery",
"evidence": "s",
"confidence": 0.7,
"target": "delivery"
},
{
"change": "boundary confidence passes the gate",
"evidence": "s",
"confidence": 0.6,
"target": "operating"
}
]
},
{
"name": "missing_and_falsy_confidence",
"input": {
"deltas": [
{
"change": "no confidence field here at all",
"target": "delivery",
"evidence": "s"
},
{
"change": "zero confidence is filtered too",
"confidence": 0,
"target": "delivery",
"evidence": "s"
},
{
"change": "long change with default target",
"confidence": 0.8,
"evidence": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
}
]
},
"expected": [
{
"change": "long change with default target",
"evidence": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"confidence": 0.8,
"target": "delivery"
}
]
}
],
"llm": [
{
"name": "request_payload",
"input": {
"prompt": "extract the deltas from this material",
"model": "qwen3:14b",
"num_ctx": 8192
},
"expected": "{\"model\": \"qwen3:14b\", \"prompt\": \"extract the deltas from this material\", \"stream\": false, \"think\": false, \"options\": {\"temperature\": 0.4, \"num_predict\": 800, \"num_ctx\": 8192}}"
}
],
"write_store": [
{
"name": "argv",
"input": {
"py": "/usr/bin/python3",
"store_py": "/mnt/workdrive/rune-wave-bl/src/rune/memory_store.py",
"text": "lead with the summary",
"topic": "delivery"
},
"expected": [
"/usr/bin/python3",
"/mnt/workdrive/rune-wave-bl/src/rune/memory_store.py",
"add",
"lead with the summary",
"--topic",
"delivery",
"--importance",
"0.5",
"--source",
"growth_delta",
"--method",
"agent"
]
}
],
"ctx_budget": [
{
"name": "ctx_budget_0",
"input": {
"window": 8192,
"local": true,
"task_kind": "default"
},
"expected": 6144
},
{
"name": "ctx_budget_1",
"input": {
"window": 32768,
"local": false,
"task_kind": "default"
},
"expected": 29696
},
{
"name": "ctx_budget_2",
"input": {
"window": 32768,
"local": false,
"task_kind": "verify"
},
"expected": 31232
},
{
"name": "ctx_budget_3",
"input": {
"window": 32768,
"local": false,
"task_kind": "summarize"
},
"expected": 30720
},
{
"name": "ctx_budget_4",
"input": {
"window": 32768,
"local": false,
"task_kind": "embed"
},
"expected": 31744
},
{
"name": "ctx_budget_5",
"input": {
"window": 32768,
"local": false,
"task_kind": "exotic-kind"
},
"expected": 29696
},
{
"name": "ctx_budget_6",
"input": {
"window": 32768,
"local": true,
"task_kind": "default"
},
"expected": 30720
},
{
"name": "ctx_budget_7",
"input": {
"window": 2048,
"local": true,
"task_kind": "default"
},
"expected": 1024
},
{
"name": "ctx_budget_8",
"input": {
"window": 8192,
"local": true,
"task_kind": "default"
},
"expected": 6144
}
]
}
}
@@ -0,0 +1,291 @@
[
{
"fn": "_parse_json",
"args": [
"{\"deltas\": [{\"change\": \"lead with the summary\", \"confidence\": 0.9}]}"
],
"kwargs": {},
"expected": {
"deltas": [
{
"change": "lead with the summary",
"confidence": 0.9
}
]
}
},
{
"fn": "_parse_json",
"args": [
"```json\n{\"deltas\": []}\n```"
],
"kwargs": {},
"expected": {
"deltas": []
}
},
{
"fn": "_parse_json",
"args": [
"```\n{\"deltas\": [{\"change\": \"plain fence\"}]}\n```"
],
"kwargs": {},
"expected": {
"deltas": [
{
"change": "plain fence"
}
]
}
},
{
"fn": "_parse_json",
"args": [
"junk before {\"deltas\": [{\"change\": \"after junk\"}]} junk after"
],
"kwargs": {},
"expected": {
"deltas": [
{
"change": "after junk"
}
]
}
},
{
"fn": "_parse_json",
"args": [
"total garbage, no braces here"
],
"kwargs": {},
"expected": {
"deltas": [],
"_parse_error": true
}
},
{
"fn": "_parse_json",
"args": [
"\"just a json string\""
],
"kwargs": {},
"expected": "just a json string"
},
{
"fn": "_parse_json",
"args": [
"```json {\"a\": 1} ``` trailing"
],
"kwargs": {},
"expected": {
"a": 1
}
},
{
"fn": "_parse_json",
"args": [
"[1, 2, 3]"
],
"kwargs": {},
"expected": [
1,
2,
3
]
},
{
"fn": "_parse_json",
"args": [
"```python\ncode not json\n```"
],
"kwargs": {},
"expected": {
"deltas": [],
"_parse_error": true
}
},
{
"fn": "_parse_json",
"args": [
"{\"deltas\": [{\"change\": \"café unicode é中\"}]}"
],
"kwargs": {},
"expected": {
"deltas": [
{
"change": "café unicode é中"
}
]
}
},
{
"fn": "_py_near_dup",
"args": [
{
"change": "deploy qualification resolves runner labels"
},
{
"change": "deploy qualification resolves labels"
}
],
"kwargs": {},
"expected": true
},
{
"fn": "_py_near_dup",
"args": [
{
"change": "always summarise the ticket before the standup"
},
{
"change": "water the plants on tuesdays morning"
}
],
"kwargs": {},
"expected": false
},
{
"fn": "_py_near_dup",
"args": [
"same idea here",
"same idea here"
],
"kwargs": {},
"expected": false
},
{
"fn": "_py_near_dup",
"args": [
"deploy qualification resolves runner labels",
{
"change": "deploy qualification resolves labels"
}
],
"kwargs": {},
"expected": true
},
{
"fn": "_py_near_dup",
"args": [
{
"change": ""
},
{
"change": "anything at all here"
}
],
"kwargs": {},
"expected": false
},
{
"fn": "_py_near_dup",
"args": [
{
"change": "alpha bravo charlie delta"
},
{
"change": "alpha echo foxtrot golf"
}
],
"kwargs": {},
"expected": true
},
{
"fn": "_py_near_dup",
"args": [
{
"change": "Alpha, BETA! (gamma)"
},
{
"change": "alpha beta gamma"
}
],
"kwargs": {},
"expected": true
},
{
"fn": "_py_near_dup",
"args": [
{
"change": "one two three four five"
},
{
"change": "one six seven eight"
}
],
"kwargs": {},
"expected": false
},
{
"fn": "handle_hook",
"args": [
"session_end",
{
"k": 1
}
],
"kwargs": {},
"expected": {
"status": "ok",
"event": "session_end",
"handler": "growth_delta"
}
},
{
"fn": "handle_hook",
"args": [
"unknown",
null
],
"kwargs": {},
"expected": {
"status": "ok",
"event": "unknown",
"handler": "growth_delta"
}
},
{
"fn": "apply",
"args": [
"not json at all"
],
"kwargs": {},
"expected": {
"applied": [],
"error": "invalid delta json"
}
},
{
"fn": "apply",
"args": [
"{\"change\": \"short\", \"confidence\": 0.9, \"target\": \"delivery\"}"
],
"kwargs": {},
"expected": {
"applied": [],
"error": "delta too short to be actionable"
}
},
{
"fn": "apply",
"args": [
"{\"change\": \"a valid longer change here\", \"confidence\": 0.9, \"target\": \"constitution\"}"
],
"kwargs": {},
"expected": {
"applied": [],
"error": "not a behavioural target: constitution"
}
},
{
"fn": "apply",
"args": [
"{\"change\": \"a valid longer change here\", \"confidence\": 0.5, \"target\": \"delivery\"}"
],
"kwargs": {},
"expected": {
"applied": [],
"error": "confidence 0.5 below the 0.6 single-signal gate"
}
}
]
@@ -0,0 +1,191 @@
//! growth_delta gap-close battery — parity vs the frozen Python-oracle
//! truth, embedded VERBATIM (never transcribed):
//! - frozen_growth_delta_truth.json: the freeze_truth harness format —
//! the PURE, replayable oracle surface (_parse_json, _py_near_dup,
//! handle_hook, apply gate branches). Re-freezable via
//! `freeze_truth.py --verify` (byte-identical law).
//! - frozen_growth_delta_state.json: injected-state kernel cases — the
//! file/LLM/subprocess seams were patched at capture (tmp DELTAS_FILE,
//! stubbed urlopen/subprocess.run), so `now` and the I/O seeds enter
//! the kernels as explicit arguments (no-clock law).
//!
//! Frozen 2026-10-13 via scripts/gen_growth_delta_truth.py with
//! RUNE_COGNITION_CORE=python pinned (poisoned-truth law).
use crate::broker;
use crate::pyjson::{self, Json};
const TRUTH: &str = include_str!("frozen_growth_delta_truth.json");
const STATE: &str = include_str!("frozen_growth_delta_state.json");
fn j(text: &str) -> Json {
pyjson::parse(text).expect("embedded truth must parse")
}
fn s(v: &Json, key: &str) -> String {
match v.get(key) {
Some(Json::Str(x)) => x.clone(),
_ => String::new(),
}
}
fn f(v: &Json, key: &str) -> f64 {
match v.get(key) {
Some(Json::Num(x)) => *x,
_ => 0.0,
}
}
fn as_str(v: &Json) -> &str {
match v {
Json::Str(s) => s,
_ => "",
}
}
fn cases(group: &str) -> Vec<Json> {
match j(STATE).get("cases").and_then(|c| c.get(group)) {
Some(Json::Arr(items)) => items.clone(),
_ => panic!("missing group {group}"),
}
}
fn replay_state(group: &str, f_case: impl Fn(&Json) -> Json) {
for case in cases(group) {
let got = f_case(&case.get("input").cloned().unwrap_or(Json::Null));
assert_eq!(
&got,
case.get("expected").unwrap(),
"[{group}] case '{}' diverged",
s(&case, "name")
);
}
}
#[test]
fn pure_truth_battery() {
let truth = j(TRUTH);
let arr = match &truth {
Json::Arr(items) => items.clone(),
_ => panic!("pure truth is a list"),
};
for case in &arr {
let name = s(case, "fn");
let args = match case.get("args") {
Some(Json::Arr(items)) => items.clone(),
_ => vec![],
};
let expected = case.get("expected").cloned().unwrap_or(Json::Null);
let got = match name.as_str() {
"_parse_json" => broker::parse_json(as_str(&args[0])),
"_py_near_dup" => Json::Bool(broker::py_near_dup(&args[0], &args[1])),
"handle_hook" => broker::handle_hook(as_str(&args[0])),
"apply" => {
let res = broker::apply(as_str(&args[0]));
// the oracle returns {"applied": [], "error": msg} on the
// gate branches; the kernel returns {"error": msg}
match res.get("error") {
Some(Json::Str(msg)) => Json::Obj(vec![
("applied".into(), Json::Arr(vec![])),
("error".into(), Json::Str(msg.clone())),
]),
_ => res,
}
}
other => panic!("no kernel dispatch for {other}"),
};
assert_eq!(&got, &expected, "pure case diverged: {name}");
}
}
#[test]
fn load_deltas_battery() {
replay_state("load_deltas", |i| {
let raw = match i.get("raw") {
Some(Json::Str(x)) => Some(x.as_str()),
_ => None,
};
broker::load_deltas(raw)
});
}
#[test]
fn save_deltas_battery() {
replay_state("save_deltas", |i| {
Json::Str(broker::save_deltas(i.get("state").unwrap_or(&Json::Null)))
});
}
#[test]
fn apply_profile_battery() {
replay_state("apply_profile", |i| {
let profile = match i.get("profile") {
Some(Json::Arr(x)) => x.clone(),
_ => vec![],
};
let delta = i.get("delta").cloned().unwrap_or(Json::Null);
let (profile, action) = broker::apply_profile(&profile, &delta);
Json::Obj(vec![
("profile".into(), Json::Arr(profile)),
("action".into(), Json::Str(action.to_string())),
])
});
}
#[test]
fn apply_one_battery() {
replay_state("apply_one", |i| {
broker::apply_one(
&s(i, "change"),
&s(i, "evidence"),
f(i, "conf"),
&s(i, "target"),
&s(i, "now"),
)
});
}
#[test]
fn reflect_battery() {
replay_state("reflect", |i| {
let deltas = match i.get("deltas") {
Some(Json::Arr(x)) => x.clone(),
_ => vec![],
};
Json::Arr(broker::reflect(&deltas))
});
}
#[test]
fn llm_payload_battery() {
replay_state("llm", |i| {
Json::Str(broker::llm_payload(
&s(i, "prompt"),
&s(i, "model"),
f(i, "num_ctx"),
))
});
}
#[test]
fn write_store_battery() {
replay_state("write_store", |i| {
Json::Arr(
broker::write_store_argv(&s(i, "py"), &s(i, "store_py"), &s(i, "text"), &s(i, "topic"))
.into_iter()
.map(Json::Str)
.collect(),
)
});
}
#[test]
fn ctx_budget_battery() {
replay_state("ctx_budget", |i| {
Json::Num(broker::ctx_budget(
f(i, "window"),
matches!(i.get("local"), Some(Json::Bool(true))),
&s(i, "task_kind"),
))
});
}
+171
View File
@@ -9,6 +9,9 @@ pub mod pyjson;
#[cfg(test)]
mod branch_battery;
#[cfg(test)]
mod growth_battery;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
@@ -552,6 +555,174 @@ pub extern "C" fn cc_reconcile(arg: *const c_char) -> *mut c_char {
envelope(result)
}
// ── growth_delta gap-close kernels (dichotomy wave) ────────────────────────
#[no_mangle]
pub extern "C" fn cc_py_near_dup(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg).and_then(|t| arg_json(&t)).map(|a| {
let x = get(&a, "a").cloned().unwrap_or(pyjson::Json::Null);
let y = get(&a, "b").cloned().unwrap_or(pyjson::Json::Null);
pyjson::Json::Bool(broker::py_near_dup(&x, &y))
});
envelope(result)
}
#[no_mangle]
pub extern "C" fn cc_parse_json(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg).and_then(|t| arg_json(&t)).map(|a| {
let text = match get(&a, "text") {
Some(pyjson::Json::Str(s)) => s.clone(),
_ => String::new(),
};
broker::parse_json(&text)
});
envelope(result)
}
#[no_mangle]
pub extern "C" fn cc_load_deltas(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg).and_then(|t| arg_json(&t)).map(|a| {
let raw = match get(&a, "raw") {
Some(pyjson::Json::Str(s)) => Some(s.as_str()),
_ => None,
};
broker::load_deltas(raw)
});
envelope(result)
}
#[no_mangle]
pub extern "C" fn cc_save_deltas(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg).and_then(|t| arg_json(&t)).map(|a| {
let state = get(&a, "state")
.cloned()
.unwrap_or(pyjson::Json::Obj(vec![]));
pyjson::Json::Str(broker::save_deltas(&state))
});
envelope(result)
}
#[no_mangle]
pub extern "C" fn cc_apply_profile(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg).and_then(|t| arg_json(&t)).map(|a| {
let profile: Vec<pyjson::Json> = match get(&a, "profile") {
Some(pyjson::Json::Arr(x)) => x.clone(),
_ => vec![],
};
let delta = get(&a, "delta")
.cloned()
.unwrap_or(pyjson::Json::Obj(vec![]));
let (profile, action) = broker::apply_profile(&profile, &delta);
pyjson::Json::Obj(vec![
("profile".to_string(), pyjson::Json::Arr(profile)),
("action".to_string(), pyjson::Json::Str(action.to_string())),
])
});
envelope(result)
}
#[no_mangle]
pub extern "C" fn cc_apply_one(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg).and_then(|t| arg_json(&t)).map(|a| {
let s = |k: &str| match get(&a, k) {
Some(pyjson::Json::Str(x)) => x.clone(),
_ => String::new(),
};
let conf = match get(&a, "conf") {
Some(pyjson::Json::Num(n)) => *n,
_ => 0.0,
};
broker::apply_one(&s("change"), &s("evidence"), conf, &s("target"), &s("now"))
});
envelope(result)
}
#[no_mangle]
pub extern "C" fn cc_reflect(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg).and_then(|t| arg_json(&t)).map(|a| {
let deltas: Vec<pyjson::Json> = match get(&a, "deltas") {
Some(pyjson::Json::Arr(x)) => x.clone(),
_ => vec![],
};
pyjson::Json::Arr(broker::reflect(&deltas))
});
envelope(result)
}
#[no_mangle]
pub extern "C" fn cc_apply(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg).and_then(|t| arg_json(&t)).map(|a| {
let delta_json = match get(&a, "delta") {
Some(pyjson::Json::Str(s)) => s.clone(),
_ => String::new(),
};
broker::apply(&delta_json)
});
envelope(result)
}
#[no_mangle]
pub extern "C" fn cc_handle_hook(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg).and_then(|t| arg_json(&t)).map(|a| {
let event = match get(&a, "event") {
Some(pyjson::Json::Str(s)) => s.clone(),
_ => String::new(),
};
broker::handle_hook(&event)
});
envelope(result)
}
#[no_mangle]
pub extern "C" fn cc_llm(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg).and_then(|t| arg_json(&t)).map(|a| {
let s = |k: &str| match get(&a, k) {
Some(pyjson::Json::Str(x)) => x.clone(),
_ => String::new(),
};
let num_ctx = match get(&a, "num_ctx") {
Some(pyjson::Json::Num(n)) => *n,
_ => 0.0,
};
pyjson::Json::Str(broker::llm_payload(&s("prompt"), &s("model"), num_ctx))
});
envelope(result)
}
#[no_mangle]
pub extern "C" fn cc_write_store(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg).and_then(|t| arg_json(&t)).map(|a| {
let s = |k: &str| match get(&a, k) {
Some(pyjson::Json::Str(x)) => x.clone(),
_ => String::new(),
};
pyjson::Json::Arr(
broker::write_store_argv(&s("py"), &s("store_py"), &s("text"), &s("topic"))
.into_iter()
.map(pyjson::Json::Str)
.collect(),
)
});
envelope(result)
}
#[no_mangle]
pub extern "C" fn cc_ctx_budget(arg: *const c_char) -> *mut c_char {
let result = read_arg(arg).and_then(|t| arg_json(&t)).map(|a| {
let window = match get(&a, "window") {
Some(pyjson::Json::Num(n)) => *n,
_ => 8192.0,
};
let local = matches!(get(&a, "local"), Some(pyjson::Json::Bool(true)));
let task_kind = match get(&a, "task_kind") {
Some(pyjson::Json::Str(s)) => s.clone(),
_ => String::new(),
};
pyjson::Json::Num(broker::ctx_budget(window, local, &task_kind))
});
envelope(result)
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
// pinned: #[no_mangle] extern "C" — the Survivor loader ABI requires this exact safe-fn signature; the raw pointer IS the FFI contract
#[no_mangle]
+1 -1
View File
@@ -14,7 +14,7 @@ fn tier_recency_works() {
.unwrap();
let t = cognition_core::broker::tier_get(&e, 7.0, 30.0, "2026-10-05T12:00:00+00:00");
assert!(
format!("{}", cognition_core::pyjson::dumps(&t)).contains("hot"),
cognition_core::pyjson::dumps(&t).contains("hot"),
"expected hot, got {}",
cognition_core::pyjson::dumps(&t)
);