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:
Nick committed 2026-10-13 18:31:51 +01:00
1 parent e7236f6eb6
commit 6bdd28521a
9 files changed
+721 -11

No files matched your search

+6 -6
View File
@@ -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 |
+200
View File
@@ -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)
+84
View File
@@ -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}")
+74
View File
@@ -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
+22 -3
View File
@@ -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] = []
+6
View File
@@ -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})
+231
View File
@@ -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": {}
}
}
]
+59 -2
View File
@@ -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");
}
}
+39
View File
@@ -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;