feat(memory_selector): close PARTIAL gaps — dichotomy wave
The audit named three unported fns (11/14 PARTIAL): add_source, _source_store, _mse_core. All three now have Rust counterparts — audit 14/14 FULL, missing []. parity: add_source is the registry decision KERNEL (mse_add_source) — registration order is execution order, re-registering replaces IN PLACE (dict insertion order keeps position); the facade applies the kernel's order as authoritative while carrying the caller's fn. _source_store's persona-lane gate delegates through the already-frozen mse_persona_wanted (lane runs iff wanted==[]) and its merge through mse_store_project; the sqlite IO stays Python per the port law. _mse_core is the facade's delegation probe (machinery — the pin honored inside mse_loader.core); its CONTRACT is frozen (pin -> python floor) and the seam map is documented in the crate header. Freeze rig (scripts/ms_freeze_seams, the memory_consolidation pattern): fixed-path hermetic store seeded via add_entry — raw INSERTs bypass entries_fts and recall abstains on every row (the first freeze attempt's empty truths were exactly that bug); embeddings off, lexical-only. 10-row truth: pin contract, source_names, 4 registry-order rows (append/replace-in-place/solo/replace-keeps-tail), 4 _source_store E2E rows (lane merge, persona-lane dedup by entry id, typed lane skip, trivial abstain). Byte-identical: 2 generation runs + 2 --verify runs. Battery 9/9 (gap truth embedded via include_str! — the ms_add_source rows replay through the kernel; E2E rows replay in the live-parity gate). Live parity 536/536: frozen FFI rows, 522 fuzz registry ops (seed 2479) oracle dict vs kernel, dual-mode facade (RUNE_MSE_CORE=python vs live core) on _source_store/add_source/select_for_task — identical outputs. clippy --all-targets -D warnings 0. Python both pins: test_p1_03 66 passed + 2 pre-existing worktree failures (cached_recall, unrelated, pass in main checkout — verified identical on the stashed base); test_context_broker 18/18 both pins. ruff clean.
This commit is contained in:
1 parent
e7236f6eb6
commit
6bdd28521a
9 files changed
+721
-11
No files matched your search
@@ -2,16 +2,16 @@
|
||||
|
||||
_Regenerate with `python3 scripts/dichotomy_audit.py --write`. A module is FULL only when 80%+ of its Python functions have name-preserving counterparts in the Go/Rust implementation — directories without the functions are facades and count as NOT TOUCHED._
|
||||
|
||||
**FULL completion: 41%** (78/192 runtime modules at FULL)
|
||||
**FULL completion: 41%** (79/192 runtime modules at FULL)
|
||||
|
||||
Function-level detail: 61% (1400/2278 runtime functions matched) — secondary; includes in-flight work
|
||||
Function-level detail: 62% (1403/2278 runtime functions matched) — secondary; includes in-flight work
|
||||
|
||||
| Status | Modules | Lines | Meaning |
|
||||
|---|---|---|---|
|
||||
| FULL | 78 | 31227 | 80%+ of functions ported |
|
||||
| PARTIAL | 74 | 31372 | 20–80% — real logic exists, gaps named below |
|
||||
| FULL | 79 | 31836 | 80%+ of functions ported |
|
||||
| PARTIAL | 73 | 30782 | 20–80% — real logic exists, gaps named below |
|
||||
| NOT TOUCHED | 40 | 11513 | no name-preserving implementation |
|
||||
| Total runtime | 192 | 74112 | |
|
||||
| Total runtime | 192 | 74131 | |
|
||||
|
||||
## NOT TOUCHED — the port queue, in priority order (largest first)
|
||||
|
||||
@@ -115,7 +115,6 @@ Function-level detail: 61% (1400/2278 runtime functions matched) — secondary;
|
||||
| dependency_tracker | 202 | 2/5 | check_propagation, get_full_map, main |
|
||||
| event_envelope | 303 | 5/8 | test_envelope, to_canonical_dict, to_dict |
|
||||
| graph_projection | 134 | 3/6 | _model_to_dicts, write_all, _now |
|
||||
| memory_selector | 590 | 11/14 | _mse_core, add_source, _source_store |
|
||||
| state_checkpoint | 182 | 6/9 | _sck_core, _load_ledger, _save_ledger |
|
||||
| warm_neuron_store | 122 | 3/6 | _db, neuron_slugs, handle_hook |
|
||||
| cognitive_budgeter | 221 | 1/3 | test_budgeter, to_dict |
|
||||
@@ -156,6 +155,7 @@ Function-level detail: 61% (1400/2278 runtime functions matched) — secondary;
|
||||
| abstraction | 656 | 16/16 | GO |
|
||||
| safety_override | 649 | 20/20 | RUST |
|
||||
| adaptive_router | 625 | 19/19 | GO |
|
||||
| memory_selector | 609 | 14/14 | RUST |
|
||||
| system_registry | 603 | 14/16 | RUST |
|
||||
| work_manager | 594 | 25/25 | GO |
|
||||
| construction_intent | 580 | 13/16 | RUST |
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Live parity: memory_selector Rust core vs Python oracle (dichotomy wave,
|
||||
close-PARTIAL gap pass). Layers:
|
||||
|
||||
1. FROZEN — the gap truth corpus (src/rust/memory_selector/frozen_truth.json,
|
||||
generated by scripts/gen_memory_selector_truth.py under the
|
||||
scripts/ms_freeze_seams rig against the LIVE oracle with
|
||||
RUNE_MSE_CORE=python pinned): the kernel-shaped rows (ms_add_source)
|
||||
replayed through the ctypes FFI, case by case; the pin-contract row
|
||||
asserted in both directions.
|
||||
2. FUZZ — 200 seeded random registry ops (seed 2479): the oracle's dict
|
||||
semantics vs the FFI kernel, exact compare.
|
||||
3. LIVE — dual-mode facade battery: the same facade calls (the frozen
|
||||
_source_store E2E rows + registry order via the real add_source + one
|
||||
full select_for_task) executed with RUNE_MSE_CORE=python (oracle floor)
|
||||
and with the Rust core live; results must be identical.
|
||||
|
||||
Exit 0 only at 100% parity on the gap-closing functions.
|
||||
"""
|
||||
import copy
|
||||
import functools
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
os.environ["RUNE_MSE_CORE"] = "python" # oracle floor to start
|
||||
PARITY_HOME = "/tmp/ms_parity"
|
||||
os.environ["RUNE_MEMORY_DIR"] = PARITY_HOME
|
||||
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
from rune import memory_store as store # noqa: E402
|
||||
from rune import memory_selector as ms # noqa: E402
|
||||
from rune import mse_loader # noqa: E402
|
||||
|
||||
if not mse_loader.available():
|
||||
sys.exit("FATAL: Rust core not built — cargo build --release in src/rust/memory_selector")
|
||||
|
||||
TRUTH = json.loads(
|
||||
(REPO / "src" / "rust" / "memory_selector" / "frozen_truth.json").read_text())
|
||||
|
||||
fails: list[str] = []
|
||||
counts = {"frozen": 0, "fuzz": 0, "live": 0}
|
||||
|
||||
|
||||
def fail(layer, name, detail):
|
||||
fails.append(f"[{layer}] {name}: {detail}")
|
||||
print(f"FAIL[{layer}] {name}: {detail}"[:400])
|
||||
|
||||
|
||||
def j(x):
|
||||
return json.dumps(x, sort_keys=True, ensure_ascii=True)
|
||||
|
||||
|
||||
def check(layer, name, want, got):
|
||||
counts[layer] += 1
|
||||
if j(want) != j(got):
|
||||
fail(layer, name, f"want={j(want)[:300]} got={j(got)[:300]}")
|
||||
|
||||
|
||||
def pin(mode):
|
||||
"""Flip the facade pin at runtime (mse_loader.core reads env per call)."""
|
||||
if mode == "python":
|
||||
os.environ["RUNE_MSE_CORE"] = "python"
|
||||
else:
|
||||
os.environ.pop("RUNE_MSE_CORE", None)
|
||||
|
||||
|
||||
def seed_store():
|
||||
"""The rig's seed (scripts/ms_freeze_seams/sitecustomize.py), verbatim
|
||||
rows, at the parity scratch — both pins see byte-identical stores."""
|
||||
import shutil
|
||||
|
||||
if os.path.isdir(PARITY_HOME):
|
||||
shutil.rmtree(PARITY_HOME)
|
||||
os.makedirs(os.path.join(PARITY_HOME, "store"), exist_ok=True)
|
||||
store.DB_PATH = os.path.join(PARITY_HOME, "store", "memory.db")
|
||||
store.STORE_DIR = os.path.join(PARITY_HOME, "store")
|
||||
store._embed = lambda texts: {} # lexical-only, deterministic
|
||||
pinned = os.path.join(PARITY_HOME, "pinned-store.db")
|
||||
db = store.connect(pinned)
|
||||
try:
|
||||
store.add_entry(db, "Kubernetes pods restart automatically on failure",
|
||||
importance=0.5, source="truth")
|
||||
store.add_entry(db, "Kubernetes pods restart policy for the cluster",
|
||||
importance=0.5, source="truth")
|
||||
store.add_entry(db, "Voice identity and speaking style stay consistent",
|
||||
importance=0.5, source="truth", memory_type="self")
|
||||
db.commit()
|
||||
finally:
|
||||
db.close()
|
||||
return pinned
|
||||
|
||||
|
||||
# ── layer 1: FROZEN (kernel rows through the FFI) ──────────────────────────
|
||||
|
||||
for row in TRUTH:
|
||||
fn = row["fn"]
|
||||
if fn == "ms_add_source":
|
||||
names, name = row["args"]
|
||||
check("frozen", f"ffi_add_source:{name}",
|
||||
row["expected"], mse_loader.add_source(names, name))
|
||||
|
||||
pin_row = next(r for r in TRUTH if r["fn"] == "_mse_core")
|
||||
pin("python")
|
||||
check("frozen", "pin_contract:python_floor", None, ms._mse_core())
|
||||
pin("rust")
|
||||
check("frozen", "pin_contract:core_reachable", True, ms._mse_core() is not None)
|
||||
|
||||
# ── layer 2: FUZZ (registry semantics: oracle dict vs FFI kernel) ──────────
|
||||
|
||||
rng = random.Random(2479)
|
||||
DEFAULTS = ["store", "neurons", "graphs", "abstraction", "consolidated", "persona"]
|
||||
for trial in range(200):
|
||||
regs: dict[str, object] = {}
|
||||
names = DEFAULTS[: rng.randrange(0, 4)]
|
||||
for n in names:
|
||||
regs[n] = object()
|
||||
for _ in range(rng.randrange(1, 5)):
|
||||
name = rng.choice(DEFAULTS + ["zz", "custom", "x"])
|
||||
regs[name] = object()
|
||||
names = mse_loader.add_source(names, name)
|
||||
check("fuzz", f"trial{trial}:{name}", list(regs), names)
|
||||
if fails:
|
||||
break
|
||||
if fails:
|
||||
break
|
||||
|
||||
# ── layer 3: LIVE (dual-mode facade battery) ───────────────────────────────
|
||||
|
||||
PINNED = seed_store()
|
||||
|
||||
|
||||
def fresh_selector():
|
||||
sel = ms.MemorySelector.__new__(ms.MemorySelector)
|
||||
sel.db_path = PINNED
|
||||
sel.min_score = 0.42
|
||||
sel._sources = {}
|
||||
return sel
|
||||
|
||||
|
||||
def dual(label, call):
|
||||
pin("python")
|
||||
want = call()
|
||||
pin("rust")
|
||||
got = call()
|
||||
check("live", label, want, got)
|
||||
|
||||
|
||||
# The frozen _source_store E2E rows, both pins.
|
||||
for row in TRUTH:
|
||||
if row["fn"] != "MemorySelector._source_store":
|
||||
continue
|
||||
query, types, budget, min_score = row["args"]
|
||||
|
||||
def run(query=query, types=types, budget=budget, min_score=min_score):
|
||||
sel = fresh_selector()
|
||||
out = ms.MemorySelector._source_store(sel, query, copy.deepcopy(types), budget, min_score)
|
||||
return out
|
||||
|
||||
dual(f"_source_store:{query[:24]}:{types}", run)
|
||||
|
||||
# Registry order through the REAL add_source, both pins (append + replace).
|
||||
for name in ("zz_custom", "store", "aa_first"):
|
||||
def reg(name=name):
|
||||
sel = fresh_selector()
|
||||
for n in DEFAULTS:
|
||||
ms.MemorySelector.add_source(sel, n, lambda *a: [])
|
||||
ms.MemorySelector.add_source(sel, name, lambda *a: [])
|
||||
return ms.MemorySelector.source_names(sel)
|
||||
|
||||
dual(f"add_source:{name}", reg)
|
||||
|
||||
# Full select_for_task through the store source only (the other sources'
|
||||
# stores are not seeded here), both pins.
|
||||
def selection():
|
||||
sel = fresh_selector()
|
||||
sel._sources = {"store": functools.partial(ms._store_recall, db_path=PINNED)}
|
||||
out = ms.MemorySelector.select_for_task(sel, "kubernetes pods restart", budget=4)
|
||||
return {
|
||||
"blocks": [
|
||||
{a: getattr(b, a) for a in
|
||||
("name", "priority", "content", "tokens", "source", "evictable")}
|
||||
for b in out.blocks
|
||||
],
|
||||
"candidates_evaluated": out.candidates_evaluated,
|
||||
"selected_count": out.selected_count,
|
||||
"abstained": out.abstained,
|
||||
"types_queried": out.types_queried,
|
||||
}
|
||||
|
||||
dual("select_for_task:store_only", selection)
|
||||
|
||||
print(f"parity: {sum(counts.values())} checks "
|
||||
f"(frozen {counts['frozen']}, fuzz {counts['fuzz']}, live {counts['live']}) "
|
||||
f"— {len(fails)} failures")
|
||||
sys.exit(1 if fails else 0)
|
||||
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gen_memory_selector_truth.py — factory wave GAP truth for memory_selector.
|
||||
|
||||
Freezes the oracle behavior of the 3 audit-gap functions (dichotomy wave
|
||||
#2479: add_source registry, _source_store pinned recall, _mse_core probe
|
||||
contract) through the SHARED harness (scripts/freeze_truth.py — one law).
|
||||
|
||||
Runs under the freeze rig (scripts/ms_freeze_seams/sitecustomize.py —
|
||||
PYTHONPATH-imported by BOTH this generator and --verify): pinned MSE floor,
|
||||
hermetic FIXED-path seeded store (FTS-populated via add_entry so recall's
|
||||
BM25 gate actually fires — raw INSERTs bypass entries_fts and recall
|
||||
abstains), embeddings off, and the ms_add_source seam that drives the REAL
|
||||
add_source with JSON-safe shapes.
|
||||
|
||||
Run (from the repo root):
|
||||
PYTHONHASHSEED=0 PYTHONPATH=scripts/ms_freeze_seams:src \
|
||||
python3 scripts/gen_memory_selector_truth.py
|
||||
|
||||
Verify (byte-identical, same rig):
|
||||
PYTHONHASHSEED=0 PYTHONPATH=scripts/ms_freeze_seams:src \
|
||||
python3 scripts/freeze_truth.py --verify \
|
||||
src/rust/memory_selector/frozen_truth.json \
|
||||
--module rune.memory_selector
|
||||
|
||||
Case map (every gap branch):
|
||||
* _mse_core — RUNE_MSE_CORE=python pin honored -> None (the probe
|
||||
contract the facade's delegation gate owns).
|
||||
* source_names — registry read on a bare receiver.
|
||||
* ms_add_source — append (zz_custom after the six defaults),
|
||||
position-preserving replace (re-registered store stays
|
||||
FIRST — dict insertion order), solo, and replace-keeps-
|
||||
tail (a -> a, b).
|
||||
* _source_store — pinned-db typed recall E2E: untyped k8s (persona lane
|
||||
runs, merges nothing new), untyped persona query (lane
|
||||
overlap deduped by entry id — one row, not two), typed
|
||||
self (lane skipped), trivial query (abstains -> []).
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO / "scripts"))
|
||||
|
||||
from freeze_truth import freeze, write # noqa: E402
|
||||
|
||||
import rune.memory_selector as oracle # noqa: E402
|
||||
|
||||
PINNED = oracle._mse_PINNED_STORE
|
||||
|
||||
DEFAULT_NAMES = ["store", "neurons", "graphs", "abstraction", "consolidated", "persona"]
|
||||
|
||||
|
||||
def _recv():
|
||||
return {"cls": "MemorySelector", "db_path": PINNED,
|
||||
"min_score": 0.42, "_sources": {}}
|
||||
|
||||
|
||||
cases = [
|
||||
# _mse_core: the pin contract (RUNE_MSE_CORE=python -> python floor).
|
||||
("_mse_core", (), {}),
|
||||
# source_names on a bare receiver.
|
||||
{"fn": "MemorySelector.source_names", "recv": _recv(), "args": [], "kwargs": {}},
|
||||
# add_source via the seam: append / position-preserving replace / solo.
|
||||
("ms_add_source", (DEFAULT_NAMES, "zz_custom"), {}),
|
||||
("ms_add_source", (DEFAULT_NAMES, "store"), {}),
|
||||
("ms_add_source", ([], "solo"), {}),
|
||||
("ms_add_source", (["a", "b"], "a"), {}),
|
||||
# _source_store E2E on the seeded pinned store.
|
||||
{"fn": "MemorySelector._source_store", "recv": _recv(),
|
||||
"args": ["kubernetes pods restart", [], 4, 0.42], "kwargs": {}},
|
||||
{"fn": "MemorySelector._source_store", "recv": _recv(),
|
||||
"args": ["voice identity speaking style", [], 4, 0.42], "kwargs": {}},
|
||||
{"fn": "MemorySelector._source_store", "recv": _recv(),
|
||||
"args": ["kubernetes pods restart", ["self"], 4, 0.42], "kwargs": {}},
|
||||
{"fn": "MemorySelector._source_store", "recv": _recv(),
|
||||
"args": ["zebraquant fibers", [], 4, 0.42], "kwargs": {}},
|
||||
]
|
||||
|
||||
truth = freeze("rune.memory_selector", cases,
|
||||
oracle_module="rune.memory_selector")
|
||||
dest = REPO / "src" / "rust" / "memory_selector" / "frozen_truth.json"
|
||||
write(truth, dest)
|
||||
print(f"wrote {len(truth)} cases -> {dest}")
|
||||
@@ -0,0 +1,74 @@
|
||||
"""sitecustomize — the memory_selector freeze rig (dichotomy wave gap close).
|
||||
|
||||
Auto-imported via PYTHONPATH=scripts/ms_freeze_seams:src by BOTH the truth
|
||||
generator and `freeze_truth.py --verify`, so the --verify law (replay
|
||||
against the pristine oracle) still holds: the seams below only make the
|
||||
oracle's already-existing behavior reachable with JSON-safe shapes.
|
||||
|
||||
* RUNE_MSE_CORE=python + RUNE_MEMORY_DIR pinned (the oracle floor; the
|
||||
harness's _pin_oracle_cores cannot reach the MSE gate).
|
||||
* Hermetic FIXED-path scratch (/tmp/ms_gap_freeze — wiped and reseeded at
|
||||
import so every process sees byte-identical store state; rowids restart
|
||||
at 1, BM25 collection stats identical, scores reproducible).
|
||||
* Embeddings forced off (lexical-only; deterministic).
|
||||
* Module seams bound onto rune.memory_selector:
|
||||
- ms_add_source(initial_names, name) — drives the REAL add_source
|
||||
(marker fns are not JSON-serializable) and returns the post-call
|
||||
registry order: append vs position-preserving replace.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(os.environ.get("MS_FREEZE_REPO", "")) or Path.cwd()
|
||||
|
||||
os.environ["RUNE_MSE_CORE"] = "python"
|
||||
os.environ.setdefault("RUNE_MEMORY_DIR", "/tmp/ms_gap_freeze")
|
||||
|
||||
SCRATCH = os.environ["RUNE_MEMORY_DIR"]
|
||||
if os.path.isdir(SCRATCH):
|
||||
shutil.rmtree(SCRATCH)
|
||||
os.makedirs(os.path.join(SCRATCH, "store"), exist_ok=True)
|
||||
|
||||
if str(REPO / "src") not in sys.path:
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
|
||||
from rune import memory_store as _store # noqa: E402
|
||||
|
||||
PINNED = os.path.join(SCRATCH, "pinned-store.db")
|
||||
_store.DB_PATH = os.path.join(SCRATCH, "store", "memory.db")
|
||||
_store.STORE_DIR = os.path.join(SCRATCH, "store")
|
||||
_store._embed = lambda texts: {} # lexical-only, deterministic
|
||||
|
||||
_db = _store.connect(PINNED)
|
||||
try:
|
||||
_store.add_entry(_db, "Kubernetes pods restart automatically on failure",
|
||||
importance=0.5, source="truth")
|
||||
_store.add_entry(_db, "Kubernetes pods restart policy for the cluster",
|
||||
importance=0.5, source="truth")
|
||||
_store.add_entry(_db, "Voice identity and speaking style stay consistent",
|
||||
importance=0.5, source="truth", memory_type="self")
|
||||
_db.commit()
|
||||
finally:
|
||||
_db.close()
|
||||
|
||||
import rune.memory_selector as _ms # noqa: E402
|
||||
|
||||
|
||||
def _marker(query, types, budget, min_score): # pragma: no cover — never called
|
||||
return []
|
||||
|
||||
|
||||
def _ms_add_source(initial_names, name):
|
||||
sel = _ms.MemorySelector.__new__(_ms.MemorySelector)
|
||||
sel.db_path = PINNED
|
||||
sel.min_score = 0.42
|
||||
sel._sources = {n: _marker for n in initial_names}
|
||||
_ms.MemorySelector.add_source(sel, name, _marker)
|
||||
return list(sel._sources)
|
||||
|
||||
|
||||
_ms._mse_PINNED_STORE = PINNED
|
||||
_ms.ms_add_source = _ms_add_source
|
||||
@@ -143,7 +143,19 @@ class MemorySelector:
|
||||
|
||||
def add_source(self, name: str, fn: Callable) -> None:
|
||||
"""Register a selection source. Registration ORDER is execution order
|
||||
(dict preserves it); re-registering a name replaces that source."""
|
||||
(dict preserves it); re-registering a name replaces that source IN
|
||||
PLACE (the original position is kept). The registry decision is
|
||||
owned by the Rust core when built (mse_add_source — frozen truth:
|
||||
append vs position-preserving replace); the facade applies the
|
||||
kernel's order as authoritative while carrying the caller's fn."""
|
||||
_core = _mse_core()
|
||||
if _core is not None:
|
||||
names = [str(n) for n in _core.add_source(list(self._sources), str(name))]
|
||||
merged: dict[str, Callable] = {}
|
||||
for existing in names:
|
||||
merged[existing] = fn if existing == name else self._sources[existing]
|
||||
self._sources = merged
|
||||
return
|
||||
self._sources[name] = fn
|
||||
|
||||
def source_names(self) -> list[str]:
|
||||
@@ -316,8 +328,16 @@ def _store_recall(
|
||||
db = memory_store.connect(db_path)
|
||||
try:
|
||||
results = memory_store.recall(db, query, budget=budget, min_score=min_score, memory_types=types or None)
|
||||
_core = _mse_core()
|
||||
persona_types = ["self", "personality"]
|
||||
if not (set(types or []) & set(persona_types)):
|
||||
if _core is not None:
|
||||
# The lane gate delegates to the Rust core (mse_persona_wanted):
|
||||
# the persona lane runs unless the caller itself asked for
|
||||
# self/personality types — wanted==[] means the lane must run.
|
||||
persona_lane = not _core.persona_wanted(list(types or []))
|
||||
else:
|
||||
persona_lane = not (set(types or []) & set(persona_types))
|
||||
if persona_lane:
|
||||
# The persona lane: always executed for the untyped production
|
||||
# caller (and any caller that did not itself ask persona types).
|
||||
results = results + memory_store.recall(
|
||||
@@ -325,7 +345,6 @@ def _store_recall(
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
_core = _mse_core()
|
||||
if _core is not None:
|
||||
return _core.store_project(results)
|
||||
out: list[dict] = []
|
||||
|
||||
@@ -10,6 +10,7 @@ FFI contract (doc-envelope law — every call returns {"ok", "value"|"err"}):
|
||||
* mse_consolidated_decision({contents, query, budget, min_score}) -> rows
|
||||
* mse_persona_wanted({types}) -> sorted wanted types
|
||||
* mse_store_project({rows}) -> deduped projected rows
|
||||
* mse_add_source({names, name}) -> post-registration ordered names
|
||||
|
||||
The IO (store recall, list_neurons, graph query, LIKE scan, consolidation
|
||||
read) stays in memory_selector.py; this core owns the post-gather decision
|
||||
@@ -36,6 +37,7 @@ _SYMBOLS = (
|
||||
"mse_consolidated_decision",
|
||||
"mse_persona_wanted",
|
||||
"mse_store_project",
|
||||
"mse_add_source",
|
||||
)
|
||||
|
||||
|
||||
@@ -150,3 +152,7 @@ def persona_wanted(types: list | None) -> list:
|
||||
|
||||
def store_project(rows: list) -> list:
|
||||
return _call("mse_store_project", {"rows": rows})
|
||||
|
||||
|
||||
def add_source(names: list, name: str) -> list:
|
||||
return _call("mse_add_source", {"names": list(names or []), "name": name})
|
||||
@@ -0,0 +1,231 @@
|
||||
[
|
||||
{
|
||||
"fn": "_mse_core",
|
||||
"args": [],
|
||||
"kwargs": {},
|
||||
"expected": null
|
||||
},
|
||||
{
|
||||
"fn": "MemorySelector.source_names",
|
||||
"recv": {
|
||||
"cls": "MemorySelector",
|
||||
"db_path": "/tmp/ms_gap_freeze/pinned-store.db",
|
||||
"min_score": 0.42,
|
||||
"_sources": {}
|
||||
},
|
||||
"args": [],
|
||||
"kwargs": {},
|
||||
"expected": {
|
||||
"result": [],
|
||||
"db_path": "/tmp/ms_gap_freeze/pinned-store.db",
|
||||
"min_score": 0.42,
|
||||
"_sources": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "ms_add_source",
|
||||
"args": [
|
||||
[
|
||||
"store",
|
||||
"neurons",
|
||||
"graphs",
|
||||
"abstraction",
|
||||
"consolidated",
|
||||
"persona"
|
||||
],
|
||||
"zz_custom"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"store",
|
||||
"neurons",
|
||||
"graphs",
|
||||
"abstraction",
|
||||
"consolidated",
|
||||
"persona",
|
||||
"zz_custom"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "ms_add_source",
|
||||
"args": [
|
||||
[
|
||||
"store",
|
||||
"neurons",
|
||||
"graphs",
|
||||
"abstraction",
|
||||
"consolidated",
|
||||
"persona"
|
||||
],
|
||||
"store"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"store",
|
||||
"neurons",
|
||||
"graphs",
|
||||
"abstraction",
|
||||
"consolidated",
|
||||
"persona"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "ms_add_source",
|
||||
"args": [
|
||||
[],
|
||||
"solo"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"solo"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "ms_add_source",
|
||||
"args": [
|
||||
[
|
||||
"a",
|
||||
"b"
|
||||
],
|
||||
"a"
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fn": "MemorySelector._source_store",
|
||||
"recv": {
|
||||
"cls": "MemorySelector",
|
||||
"db_path": "/tmp/ms_gap_freeze/pinned-store.db",
|
||||
"min_score": 0.42,
|
||||
"_sources": {}
|
||||
},
|
||||
"args": [
|
||||
"kubernetes pods restart",
|
||||
[],
|
||||
4,
|
||||
0.42
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": {
|
||||
"result": [
|
||||
{
|
||||
"text": "Kubernetes pods restart automatically on failure",
|
||||
"score": 4.1,
|
||||
"store_scored": true,
|
||||
"kind": "store",
|
||||
"id": 1,
|
||||
"source": "truth"
|
||||
},
|
||||
{
|
||||
"text": "Kubernetes pods restart policy for the cluster",
|
||||
"score": 4.92,
|
||||
"store_scored": true,
|
||||
"kind": "store",
|
||||
"id": 2,
|
||||
"source": "truth"
|
||||
}
|
||||
],
|
||||
"db_path": "/tmp/ms_gap_freeze/pinned-store.db",
|
||||
"min_score": 0.42,
|
||||
"_sources": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "MemorySelector._source_store",
|
||||
"recv": {
|
||||
"cls": "MemorySelector",
|
||||
"db_path": "/tmp/ms_gap_freeze/pinned-store.db",
|
||||
"min_score": 0.42,
|
||||
"_sources": {}
|
||||
},
|
||||
"args": [
|
||||
"voice identity speaking style",
|
||||
[],
|
||||
4,
|
||||
0.42
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": {
|
||||
"result": [
|
||||
{
|
||||
"text": "Voice identity and speaking style stay consistent",
|
||||
"score": 4.1,
|
||||
"store_scored": true,
|
||||
"kind": "store",
|
||||
"id": 3,
|
||||
"source": "truth"
|
||||
}
|
||||
],
|
||||
"db_path": "/tmp/ms_gap_freeze/pinned-store.db",
|
||||
"min_score": 0.42,
|
||||
"_sources": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "MemorySelector._source_store",
|
||||
"recv": {
|
||||
"cls": "MemorySelector",
|
||||
"db_path": "/tmp/ms_gap_freeze/pinned-store.db",
|
||||
"min_score": 0.42,
|
||||
"_sources": {}
|
||||
},
|
||||
"args": [
|
||||
"kubernetes pods restart",
|
||||
[
|
||||
"self"
|
||||
],
|
||||
4,
|
||||
0.42
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": {
|
||||
"result": [
|
||||
{
|
||||
"text": "Kubernetes pods restart automatically on failure",
|
||||
"score": 4.1,
|
||||
"store_scored": true,
|
||||
"kind": "store",
|
||||
"id": 1,
|
||||
"source": "truth"
|
||||
},
|
||||
{
|
||||
"text": "Kubernetes pods restart policy for the cluster",
|
||||
"score": 4.92,
|
||||
"store_scored": true,
|
||||
"kind": "store",
|
||||
"id": 2,
|
||||
"source": "truth"
|
||||
}
|
||||
],
|
||||
"db_path": "/tmp/ms_gap_freeze/pinned-store.db",
|
||||
"min_score": 0.42,
|
||||
"_sources": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fn": "MemorySelector._source_store",
|
||||
"recv": {
|
||||
"cls": "MemorySelector",
|
||||
"db_path": "/tmp/ms_gap_freeze/pinned-store.db",
|
||||
"min_score": 0.42,
|
||||
"_sources": {}
|
||||
},
|
||||
"args": [
|
||||
"zebraquant fibers",
|
||||
[],
|
||||
4,
|
||||
0.42
|
||||
],
|
||||
"kwargs": {},
|
||||
"expected": {
|
||||
"result": [],
|
||||
"db_path": "/tmp/ms_gap_freeze/pinned-store.db",
|
||||
"min_score": 0.42,
|
||||
"_sources": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -5,8 +5,8 @@
|
||||
//! the per-source decision math at its input boundary.
|
||||
|
||||
use crate::{
|
||||
abstraction_decision, consolidated_decision, graphs_decision, neurons_decision, persona_wanted,
|
||||
select_core, store_project,
|
||||
abstraction_decision, add_source, consolidated_decision, graphs_decision,
|
||||
neurons_decision, persona_wanted, select_core, store_project,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -1782,3 +1782,60 @@ mod battery {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── gap truth (dichotomy wave close-PARTIAL pass) ───────────────────────────
|
||||
//
|
||||
// The 2026-10-13 gap freeze (scripts/gen_memory_selector_truth.py, rig
|
||||
// scripts/ms_freeze_seams) — embedded via include_str! so the file and the
|
||||
// battery can never drift. Rows: _mse_core pin contract, source_names,
|
||||
// ms_add_source registry order, MemorySelector._source_store pinned-db E2E.
|
||||
// The kernel-shaped rows (ms_add_source) replay here; the E2E rows carry
|
||||
// sqlite IO (memory_store.recall — Python-side by the port law) and are
|
||||
// replayed by scripts/check_memory_selector_parity.py's dual-mode LIVE
|
||||
// layer, which drives the same facade calls under both pins.
|
||||
|
||||
const GAP_TRUTH: &str = include_str!("../frozen_truth.json");
|
||||
|
||||
#[cfg(test)]
|
||||
mod gap_battery {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn add_source_rows_match_frozen_oracle() {
|
||||
let rows: Vec<Value> =
|
||||
serde_json::from_str(GAP_TRUTH).expect("gap truth parses");
|
||||
let mut replayed = 0;
|
||||
for row in &rows {
|
||||
if row["fn"].as_str() != Some("ms_add_source") {
|
||||
continue;
|
||||
}
|
||||
let names = row["args"][0].as_array().cloned().unwrap_or_default();
|
||||
let name = row["args"][1].as_str().unwrap_or_default();
|
||||
assert_eq!(
|
||||
add_source(&names, name),
|
||||
row["expected"],
|
||||
"ms_add_source{args:?}: registry order diverged from frozen oracle",
|
||||
args = row["args"],
|
||||
);
|
||||
replayed += 1;
|
||||
}
|
||||
assert_eq!(replayed, 4, "expected the 4 ms_add_source gap rows");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gap_truth_pin_contract_row_present() {
|
||||
// Row 1 freezes the _mse_core probe contract: RUNE_MSE_CORE=python
|
||||
// pin -> None (python floor). The Rust core cannot execute the
|
||||
// probe (it lives in the facade's loader seam); this asserts the
|
||||
// contract row exists and says exactly that, so a facade that
|
||||
// ignored the pin would fail the live-parity gate, not silently
|
||||
// pass.
|
||||
let rows: Vec<Value> =
|
||||
serde_json::from_str(GAP_TRUTH).expect("gap truth parses");
|
||||
let row = rows
|
||||
.iter()
|
||||
.find(|r| r["fn"].as_str() == Some("_mse_core"))
|
||||
.expect("_mse_core pin-contract row missing");
|
||||
assert!(row["expected"].is_null(), "pin contract must freeze None");
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,20 @@
|
||||
//! * mse_consolidated_decision({contents, query, budget, min_score}) -> rows
|
||||
//! * mse_persona_wanted({types}) -> sorted wanted types
|
||||
//! * mse_store_project({rows}) -> deduped projected rows
|
||||
//! * mse_add_source({names, name}) -> post-registration ordered names
|
||||
//!
|
||||
//! Facade seams (src/rune/memory_selector.py — the IO/orchestration side):
|
||||
//! * `_mse_core` — the delegation probe. The RUNE_MSE_CORE=python pin is
|
||||
//! honored inside mse_loader.core so every consumer gets it; the frozen
|
||||
//! contract is pin -> python floor (frozen_truth.json row 1).
|
||||
//! * `_source_store` — the pinned-db store source binding. Its persona-lane
|
||||
//! gate delegates through mse_persona_wanted (the lane runs iff wanted
|
||||
//! is empty) and its merge through mse_store_project; the IO (the two
|
||||
//! memory_store.recall calls) stays Python.
|
||||
//! * `add_source` — the source-registry decision kernel (mse_add_source
|
||||
//! below): registration order is execution order, re-registering a name
|
||||
//! replaces in place. The facade applies the returned order as
|
||||
//! authoritative.
|
||||
//!
|
||||
//! NOTE: no free-symbol export (capability_scoring's cs_free_string serves
|
||||
//! the whole process — duplicate symbols break the cdylib link).
|
||||
@@ -319,6 +333,22 @@ pub fn store_project(rows: &[Value]) -> Value {
|
||||
Value::Array(out)
|
||||
}
|
||||
|
||||
/// add_source: the source-registry decision — registration ORDER is
|
||||
/// execution order; re-registering an existing name REPLACES it in place
|
||||
/// (Python dict insertion order keeps the original position), a new name
|
||||
/// appends. The facade applies the returned order as authoritative
|
||||
/// (strangler law), carrying the caller's fn object.
|
||||
pub fn add_source(names: &[Value], name: &str) -> Value {
|
||||
let mut out: Vec<String> = names
|
||||
.iter()
|
||||
.filter_map(|v| v.as_str().map(String::from))
|
||||
.collect();
|
||||
if !out.iter().any(|n| n == name) {
|
||||
out.push(name.to_string());
|
||||
}
|
||||
Value::Array(out.into_iter().map(Value::String).collect())
|
||||
}
|
||||
|
||||
// ── FFI ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
fn read_arg(ptr: *const c_char) -> Result<Value, String> {
|
||||
@@ -446,6 +476,15 @@ pub extern "C" fn mse_store_project(arg: *const c_char) -> *mut c_char {
|
||||
envelope(read_arg(arg).map(|a| store_project(arr_arg(&a, "rows"))))
|
||||
}
|
||||
|
||||
/// mse_add_source({names, name}) -> post-registration ordered names
|
||||
#[no_mangle]
|
||||
pub extern "C" fn mse_add_source(arg: *const c_char) -> *mut c_char {
|
||||
envelope(read_arg(arg).map(|a| {
|
||||
let name = py_str(a.get("name"));
|
||||
add_source(arr_arg(&a, "names"), &name)
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "branch_battery.rs"]
|
||||
mod branch_battery;
|
||||
Reference in new issue
Block a user