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
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:
1 parent
da481e744d
commit
c8b003e8fe
11 files changed
+2132
-43
No files matched your search
@@ -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())
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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"),
|
||||
))
|
||||
});
|
||||
}
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user