feat(memory_store): close stats-family audit gaps — dichotomy wave

WORKING-SET/STATS/AUDIT lane (builder factory-heavy-ms-stats): the 5
currently-unmatched slice fns ported name-preserving into the
memory-store-deep Go corpus in a NEW family file gaps_stats.go (siblings
untouched) — reindex_entries (gap census + dry-run + full rebuild with
contentless-FTS5 DROP/recreate + vec_missing/fts_missing counting),
wake_up (CHAR-budgeted '- text' lines — rune count, never bytes),
storage_backend (R2 W2.0 read-backend seam: sqlite default,
RUNE_MEMORY_STORAGE=rust opt-in via injectable probe; loader is FFI
machinery), stats_for_path (stats through the seam; by_topic EMISSION
order preserved via parallel pair list — Go maps sort it away),
handle_hook (default envelope). The other 12 slice fns were already
landed by earlier waves (main.go/gaps_retrieval.go) — audit re-run first
per law.

Truth: 24-row truth_stats_gaps.json from the live oracle under pinned
rigs (frozen clock, _embed off/scripted, fake mems_loader sys.modules
injection, plain-table entries_vec mirror — vec0 observed only through
COUNT/JOIN/DELETE/INSERT). Battery gaps_stats_test.go replays
canonically (GAPS_TRUTH_PATH override); live parity 24/24 (fresh
capture → diff vs frozen → Go battery); gofmt/vet/build/full-test
clean; Python module suite failure sets byte-identical across both
pins (RUNE_RETRIEVAL_CORE live + python, 9 pre-existing, none this
port — no tracked Python/tests files touched); ruff clean. Documented
divergences in verification/msstats-summary.md: contentless-FTS5 NULL
text readback (both engines), json.dumps float-list format (pyJSONFloatList
mirror, integral floats keep .0), by_topic tie order engine-shaped
(distinct-count fixtures). Audit: memory_store 44/68 → 51/68; the
read_query ×2 counters (dm lane's slice) also flipped via this port's
ReadQuery interface method name — name-collision flip, NOT a dm port.
This commit is contained in:
Nick committed 2026-10-15 02:38:09 +01:00
1 parent 2092134c2a
commit 5591f3a921
10 files changed
+2305

No files matched your search

@@ -0,0 +1,64 @@
#!/usr/bin/env python3
"""check_memory_store_stats_parity.py — live parity for the memory_store
STATS family gap port (builder factory-heavy-ms-stats).
Regenerates the truth corpus from the LIVE oracle (all rigs active) into a
temp file, byte-compares it against the committed frozen truth (drift gate),
then replays the FRESH corpus through the Go battery via GAPS_TRUTH_PATH.
Green = the Go kernels in src/go/memory-store-deep/gaps_stats.go agree with
the live Python oracle on every row, in both directions.
Usage: python3 scripts/check_memory_store_stats_parity.py
"""
import subprocess
import sys
import tempfile
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
GEN = REPO / "scripts/gen_memory_store_stats_truth.py"
TRUTH = REPO / "src/go/memory-store-deep/truth_stats_gaps.json"
GO_DIR = REPO / "src/go/memory-store-deep"
def run(cmd, **kw):
print("+", " ".join(str(c) for c in cmd))
return subprocess.run(cmd, capture_output=True, text=True, **kw)
def main() -> int:
tmp = Path(tempfile.mkdtemp(prefix="ms_stats_parity_")) / "truth_fresh.json"
r = run([sys.executable, str(GEN), "--out", str(tmp)])
print(r.stdout.strip() or r.stderr.strip())
if r.returncode != 0:
print("FAIL: generator crashed")
return 1
# drift gate: fresh capture must equal the committed frozen truth
r = run(["diff", str(TRUTH), str(tmp)])
if r.returncode != 0:
print("FAIL: live oracle drifted from frozen truth:")
print(r.stdout[:2000])
return 1
print("frozen truth == fresh live capture (byte-identical)")
# battery replay against the FRESH corpus
import os
env = dict(os.environ, GAPS_TRUTH_PATH=str(tmp))
r = run(["go", "test", "-run", "TestStatsGapsParity", "-v", "./..."],
cwd=GO_DIR, env=env)
out = r.stdout + r.stderr
print(out[-1500:])
if r.returncode != 0:
print("FAIL: Go battery diverges from live-oracle truth")
return 1
n = TRUTH.read_text().count('"fn"')
print(f"PARITY OK: {n} frozen rows byte-stable + Go battery green on fresh truth")
return 0
if __name__ == "__main__":
sys.exit(main())
+390
View File
@@ -0,0 +1,390 @@
#!/usr/bin/env python3
"""gen_memory_store_stats_truth.py — memory_store WORKING-SET/STATS/AUDIT
family truth battery (dichotomy wave #2479, builder factory-heavy-ms-stats).
Freezes the currently-audit-missing stats-family fns against the LIVE oracle
(src/rune/memory_store.py): reindex_entries, wake_up, storage_backend,
stats_for_path, handle_hook. Every case runs the live oracle under pinned
rigs and captures both the fn inputs (so the Go battery in
src/go/memory-store-deep/gaps_stats_test.go replays standalone) and the
oracle output. Seams per the landed crate's law: the SQLite row-fetch for
wake_up stays facade-side (rows ride the case input); _embed is pinned OFF
or scripted deterministic vectors (no network); the rust read backend is a
sys.modules-injected fake mems_loader (the loader is Python FFI machinery —
its availability + traffic is the contract, not the .so); the vec side of
reindex runs on a plain entries_vec mirror table (production vec0 is only
observed through COUNT/LEFT JOIN/DELETE/INSERT, which a plain table serves
identically — Go has no vec0). No wall-clock, no HOME-derived or absolute
paths in any truth row.
Usage:
python3 scripts/gen_memory_store_stats_truth.py # write truth
python3 scripts/gen_memory_store_stats_truth.py --check # byte-compare
"""
import json
import os
import sqlite3
import sys
import tempfile
import types
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO / "scripts"))
sys.path.insert(0, str(REPO / "src"))
sys.path.insert(0, str(REPO / "src" / "rune"))
RIG_HOME = tempfile.mkdtemp(prefix="ms_stats_rig_")
os.environ["HOME"] = RIG_HOME # import-time path derivation pin
from freeze_truth import _frozen_clock # noqa: E402 — shared harness rig helper
import memory_store as ms # noqa: E402
FROZEN_NOW = "2026-10-15T00:00:00"
ms.datetime = _frozen_clock(FROZEN_NOW)
# ── fixture DDLs (rig mirrors; the Go battery builds the identical shape) ────
WAKE_DDL = """
CREATE TABLE entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT NOT NULL,
importance REAL DEFAULT 0.5,
created_at TEXT,
last_accessed TEXT,
topic TEXT DEFAULT '',
source TEXT DEFAULT '',
status TEXT DEFAULT 'active'
)
"""
REINDEX_DDL = """
CREATE TABLE entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT NOT NULL,
status TEXT DEFAULT 'active'
);
CREATE TABLE entries_vec (rowid INTEGER PRIMARY KEY, embedding TEXT);
CREATE VIRTUAL TABLE entries_fts USING fts5(text, content='', tokenize='porter unicode61');
"""
STATS_DDL = """
CREATE TABLE entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT NOT NULL,
topic TEXT DEFAULT '',
status TEXT DEFAULT 'active'
);
CREATE TABLE working (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id TEXT NOT NULL,
text TEXT NOT NULL,
created_at TEXT NOT NULL
);
CREATE TABLE audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
prev_hash TEXT NOT NULL,
hash TEXT NOT NULL,
claim TEXT,
verdict TEXT,
evidence TEXT,
created_at TEXT NOT NULL
);
"""
def mem_db(ddl: str) -> sqlite3.Connection:
db = sqlite3.connect(":memory:")
db.row_factory = sqlite3.Row
db.executescript(ddl)
return db
def reindex_fixture(entries, vec_rows, fts_rows):
"""entries: [(id, text, status)]; vec: [(rowid, emb_text)]; fts: [(rowid, text)]."""
db = mem_db(REINDEX_DDL)
for (eid, text, status) in entries:
db.execute("INSERT INTO entries (id, text, status) VALUES (?,?,?)", (eid, text, status))
for (rid, emb) in vec_rows:
db.execute("INSERT INTO entries_vec (rowid, embedding) VALUES (?,?)", (rid, emb))
for (rid, text) in fts_rows:
db.execute("INSERT INTO entries_fts (rowid, text) VALUES (?,?)", (rid, text))
db.commit()
return db
def reindex_post(db):
fts = [[r[0], r[1]] for r in db.execute("SELECT rowid, text FROM entries_fts ORDER BY rowid").fetchall()]
vec = [[r[0], r[1]] for r in db.execute("SELECT rowid, embedding FROM entries_vec ORDER BY rowid").fetchall()]
return {"fts": fts, "vec": vec}
def wake_fixture(entries):
"""entries: [(id, text, importance, topic, source, status)] — importances distinct (SQL tie order is engine-shaped, not contract)."""
db = mem_db(WAKE_DDL)
for (eid, text, imp, topic, source, status) in entries:
db.execute(
"INSERT INTO entries (id, text, importance, topic, source, status) VALUES (?,?,?,?,?,?)",
(eid, text, imp, topic, source, status))
db.commit()
return db
def wake_rows(db):
return [
{"text": r["text"], "topic": r["topic"], "importance": r["importance"], "source": r["source"]}
for r in db.execute(
"SELECT text, topic, importance, source FROM entries"
" WHERE status='active' ORDER BY importance DESC LIMIT 10"
).fetchall()
]
def stats_fixture_file(entries, working, audit):
"""Build the stats_for_path store on a real FILE (the backend opens per read)."""
d = tempfile.mkdtemp(prefix="ms_stats_store_")
path = os.path.join(d, "store.db")
db = sqlite3.connect(path)
db.executescript(STATS_DDL)
for (eid, text, topic, status) in entries:
db.execute("INSERT INTO entries (id, text, topic, status) VALUES (?,?,?,?)", (eid, text, topic, status))
for (i, tid) in enumerate(working, start=1):
db.execute("INSERT INTO working (id, task_id, text, created_at) VALUES (?,?,?,?)",
(i, tid, f"working note {i}", "2026-01-01T00:00:00+00:00"))
for i in range(1, len(audit) + 1):
db.execute("INSERT INTO audit_log (id, prev_hash, hash, claim, verdict, evidence, created_at)"
" VALUES (?,?,?,?,?,?,?)", (i, "GENESIS", "h" * 24, f"claim {i}", "pass", "", "2026-01-01T00:00:00+00:00"))
db.commit()
db.close()
return path
def set_env(key, value):
"""Pin/unpin an env var; returns the restore fn."""
saved = os.environ.get(key)
if value is None:
os.environ.pop(key, None)
else:
os.environ[key] = value
def restore():
if saved is None:
os.environ.pop(key, None)
else:
os.environ[key] = saved
return restore
def main():
cases = []
def add(fn, name, inp, expected):
cases.append({"fn": fn, "name": name, "input": inp, "expected": expected})
# ── handle_hook (default envelope — no rigs needed) ──────────────────────
for name, event, data in [
("hh_basic", "session_start", None),
("hh_data_dict", "memory_write", {"id": 7, "topic": "coffee"}),
("hh_data_list_unicode", "grüße-Ω-event", ["alpha", "βeta"]),
("hh_empty_event", "", None),
]:
add("handle_hook", name, {"event": event, "data": data}, ms.handle_hook(event, data))
# ── wake_up (rows pre-fetched facade-side; budget in CHARACTERS) ─────────
W = [
# id, text, importance, topic, source, status — importances distinct
(1, "nick prefers black coffee in the morning", 0.95, "person", "curator", "active"),
(2, "the spine rebuilds at three", 0.85, "ops", "curator", "active"),
(3, "αβγδε ζηθικ λμνξο — the greek row", 0.75, "unicode", "curator", "active"),
(4, "budget pressure trims the tail", 0.65, "ops", "curator", "active"),
(5, "inactive rows never surface", 0.99, "person", "curator", "inactive"),
(6, "tail line one", 0.55, "", "curator", "active"),
(7, "tail line two", 0.45, "", "curator", "active"),
]
wdb = wake_fixture(W)
wrows = wake_rows(wdb)
for name, budget in [
("wu_default_800", 800),
("wu_tight_95", 95),
("wu_budget_zero", 0),
]:
add("wake_up", name, {"rows": wrows, "budget_chars": budget}, ms.wake_up(wdb, budget_chars=budget))
# char-vs-byte discriminator: greek row FIRST, budget 20 — the line is 17
# CHARACTERS (fits) but 32 UTF-8 BYTES (byte-len would break the budget)
CU = [(1, "αβγδεζηθικλμνξο", 0.9, "", "", "active"), (2, "beta row", 0.5, "", "", "active")]
cdb = wake_fixture(CU)
add("wake_up", "wu_chars_not_bytes", {"rows": wake_rows(cdb), "budget_chars": 20},
ms.wake_up(cdb, budget_chars=20))
wdb_empty = wake_fixture([(1, "only an inactive row", 0.9, "", "", "inactive")])
add("wake_up", "wu_no_active", {"rows": wake_rows(wdb_empty), "budget_chars": 800}, ms.wake_up(wdb_empty, budget_chars=800))
many = [(i, f"line number {i:02d} of many", 0.9 - i * 0.01, "", "", "active") for i in range(1, 13)]
wdb12 = wake_fixture(many)
add("wake_up", "wu_ten_cap", {"rows": wake_rows(wdb12), "budget_chars": 800}, ms.wake_up(wdb12, budget_chars=800))
# ── storage_backend (env branches; loader = injected fake) ───────────────
restore_storage = set_env("RUNE_MEMORY_STORAGE", None)
add("storage_backend", "sb_default_sqlite", {"env": None, "loader_available": None},
{"name": ms.storage_backend().name})
fake = types.ModuleType("mems_loader")
fake.available = lambda: False
sys.modules["rune.mems_loader"] = fake
try:
restore_storage = set_env("RUNE_MEMORY_STORAGE", "rust")
add("storage_backend", "sb_rust_unavailable", {"env": "rust", "loader_available": False},
{"name": ms.storage_backend().name})
fake.available = lambda: True
add("storage_backend", "sb_rust_available", {"env": "rust", "loader_available": True},
{"name": ms.storage_backend().name})
finally:
restore_storage()
del sys.modules["rune.mems_loader"]
# ── stats_for_path (real file through the sqlite backend) ────────────────
SFULL = [
# id, text, topic, status — counts distinct (GROUP BY tie order is engine-shaped)
(1, "espresso beans rest four days", "coffee", "active"),
(2, "the grinder burrs matter", "coffee", "active"),
(3, "water at ninety three degrees", "coffee", "active"),
(4, "first flush darjeeling", "tea", "active"),
(5, "oolong rolled not cut", "tea", "active"),
(6, "an empty topic row", "", "active"),
(7, "a null topic row", None, "active"),
(8, "retired row never counted", "coffee", "inactive"),
]
path = stats_fixture_file(SFULL, working=["t1", "t2"], audit=[(1,)])
got = ms.stats_for_path(path)
order = [[k, v] for k, v in got["top_topics"].items()]
add("stats_for_path", "sp_sqlite_full",
{"backend": "sqlite", "entries": SFULL, "working": 2, "audit": 1},
{"stats": got, "top_topics_order": order})
path_sparse = stats_fixture_file([(1, "lonely entry", "misc", "active")], working=[], audit=[])
got = ms.stats_for_path(path_sparse)
add("stats_for_path", "sp_sqlite_sparse",
{"backend": "sqlite", "entries": [(1, "lonely entry", "misc", "active")], "working": 0, "audit": 0},
{"stats": got, "top_topics_order": [[k, v] for k, v in got["top_topics"].items()]})
path_none = stats_fixture_file([(1, "empty topic one", "", "active"), (2, "empty topic two", None, "active")],
working=[], audit=[])
got = ms.stats_for_path(path_none)
add("stats_for_path", "sp_sqlite_no_topics",
{"backend": "sqlite", "entries": [(1, "empty topic one", "", "active"), (2, "empty topic two", None, "active")],
"working": 0, "audit": 0},
{"stats": got, "top_topics_order": [[k, v] for k, v in got["top_topics"].items()]})
# rust backend branch: scripted read_query rows + traffic recorder (the
# query sequence through the seam is the contract any backend implements)
scripted = [
[(5,)], # active entries count
[(2,)], # working count
[(1,)], # audit count
[("coffee", 3), ("tea", 2), ("", 1), ("misc", 1)], # by_topic rows ('' filtered)
]
fake_rust = types.ModuleType("mems_loader")
fake_rust.available = lambda: True
traffic = []
def fake_read_query(db_path, sql, params):
traffic.append({"sql": sql, "params": list(params)})
return scripted[len(traffic) - 1]
fake_rust.read_query = fake_read_query
sys.modules["rune.mems_loader"] = fake_rust
try:
restore_storage = set_env("RUNE_MEMORY_STORAGE", "rust")
backend = ms.storage_backend()
assert backend.name == "rust-memory-storage"
got = ms.stats_for_path("whatever.db")
finally:
restore_storage()
del sys.modules["rune.mems_loader"]
add("stats_for_path", "sp_rust_scripted",
{"backend": "rust", "scripted": scripted},
{"stats": got, "top_topics_order": [[k, v] for k, v in got["top_topics"].items()],
"queries": traffic})
# ── reindex_entries (fixture stores; _embed pinned) ──────────────────────
E3 = [(1, "alpha fact about espresso", "active"), (2, "beta fact about grinders", "active"),
(3, "gamma fact about water", "active")]
def reindex_case(name, entries, vec_rows, fts_rows, embed_map, dry_run):
if embed_map is None:
ms._embed = lambda texts: {}
else:
ms._embed = lambda texts: {t: embed_map.get(t) for t in texts}
db = reindex_fixture(entries, vec_rows, fts_rows)
got = ms.reindex_entries(db, dry_run=dry_run)
post = reindex_post(db)
db.close()
add("reindex_entries", name,
{"entries": [list(e) for e in entries], "vec_rows": [list(v) for v in vec_rows],
"fts_rows": [list(f) for f in fts_rows], "embed": embed_map, "dry_run": dry_run},
{"result": got, "post": post})
# dry-run over a gapped store: reports, writes nothing (post == pre state)
reindex_case("ri_dry_run_gap", E3,
[(1, "[0.5, 0.25]")], [(1, "alpha fact about espresso"), (98, "dangling fts row")],
None, True)
# missing vec rows -> full rebuild; embed off -> every active row counts vec_missing
reindex_case("ri_missing_rebuild", E3,
[(1, "[0.5, 0.25]")], [(1, "alpha fact about espresso"), (2, "beta fact about grinders"),
(3, "gamma fact about water")],
None, False)
# complete counts but dangling rows -> rebuild
reindex_case("ri_dangling_rebuild", E3,
[(1, "a"), (2, "b"), (3, "c"), (99, "dangling vec")],
[(1, "alpha fact about espresso"), (2, "beta fact about grinders"),
(3, "gamma fact about water"), (98, "dangling fts row")],
None, False)
# vec complete, fts EMPTY (recreated table): zero dangling but stale -> rebuild
reindex_case("ri_fts_stale_rebuild", E3[:2],
[(1, "a"), (2, "b")], [],
None, False)
# no gap at all -> rebuilt False, store untouched
reindex_case("ri_no_gap", E3[:2],
[(1, "a"), (2, "b")],
[(1, "alpha fact about espresso"), (2, "beta fact about grinders")],
None, False)
# scripted embeddings ride the vec side (json.dumps separators + the
# integral-float '1.0' shape pinned by the post state)
reindex_case("ri_embed_on", E3[:2],
[], [],
{"alpha fact about espresso": [0.5, 0.25], "beta fact about grinders": [1.0, 0.5]},
False)
# inactive rows never re-enter the indexes (dangling-by-staleness rebuild)
E5 = E3 + [(4, "retired fact one", "inactive"), (5, "retired fact two", "inactive")]
reindex_case("ri_inactive_excluded", E5,
[(1, "a"), (2, "b"), (3, "c"), (4, "d")],
[(1, "alpha fact about espresso"), (2, "beta fact about grinders"),
(3, "gamma fact about water"), (5, "retired fts row")],
None, False)
out = {
"oracle": "src/rune/memory_store.py (stats/working-set family; rigs: _embed pinned,"
" fake mems_loader via sys.modules, vec side on plain-table mirror)",
"family": "working-set/stats/audit — builder factory-heavy-ms-stats",
"frozen_now": FROZEN_NOW,
"generated": "2026-10-15",
"cases": cases,
}
target = REPO / "src/go/memory-store-deep" / "truth_stats_gaps.json"
if "--out" in sys.argv:
target = Path(sys.argv[sys.argv.index("--out") + 1])
body = json.dumps(out, indent=1, ensure_ascii=False) + "\n"
if "--check" in sys.argv:
current = target.read_text()
if current == body:
print(f"OK: byte-identical ({len(cases)} cases) — {target}")
return 0
print(f"MISMATCH: regenerated truth differs from {target}")
return 1
target.write_text(body)
print(f"wrote {target} ({len(cases)} cases)")
return 0
if __name__ == "__main__":
sys.exit(main())
+305
View File
@@ -0,0 +1,305 @@
package main
// gaps_stats.go — memory_store WORKING-SET/STATS/AUDIT family gap-close
// (dichotomy wave #2479): the audit-missing stats-family fns ported
// name-preserving into the memory-store-deep crate. Oracle:
// src/rune/memory_store.py; truth: truth_stats_gaps.json (battery
// gaps_stats_test.go, GAPS_TRUTH_PATH override powers live parity via
// scripts/check_memory_store_stats_parity.py).
//
// Seams per the landed crate's law: wake_up's row-fetch stays facade-side
// (rows ride the truth input); the rust read backend is an injectable probe
// (mems_loader is Python FFI machinery — its availability + query traffic is
// the contract, not an in-process .so); reindex's vec side runs on the
// plain-table entries_vec mirror (production vec0 is observed only through
// COUNT/LEFT JOIN/DELETE/INSERT, which a plain table serves identically).
import (
"database/sql"
"fmt"
"os"
"strings"
"unicode/utf8"
)
// ── wake_up ──────────────────────────────────────────────────────────────────
// WakeRow — one pre-fetched wake-up row (the facade applies
// ORDER BY importance DESC LIMIT 10; topic/importance/source ride the row
// because the oracle's SELECT carries them even though only text is used).
type WakeRow struct {
Text string `json:"text"`
Topic string `json:"topic"`
Importance float64 `json:"importance"`
Source string `json:"source"`
}
// WakeUp — session-start context (L0 core + L1 top memories): full-text
// lines under a budget. Python len() counts CHARACTERS — the budget check
// is rune-count, never byte-count (quirk law; pinned by wu_chars_not_bytes).
func WakeUp(rows []WakeRow, budgetChars int) string {
parts := []string{}
used := 0
for _, r := range rows {
line := "- " + r.Text
n := utf8.RuneCountInString(line)
if used+n > budgetChars {
break
}
parts = append(parts, line)
used += n
}
if len(parts) > 0 {
return "# What matters (wake-up context)\n\n" + strings.Join(parts, "\n")
}
return ""
}
// ── handle_hook ──────────────────────────────────────────────────────────────
// HandleHook — the default hook envelope (specific scripts override).
func HandleHook(event string, data interface{}) map[string]interface{} {
return map[string]interface{}{"status": "ok", "event": event, "handler": "default"}
}
// ── storage backend seam (R2 W2.0) ──────────────────────────────────────────
// ReadBackend — the storage-backend seam: query logic separated from
// storage mechanics. READS only; writes keep connection-side semantics.
type ReadBackend interface {
Name() string
ReadQuery(dbPath, query string, params ...interface{}) [][]interface{}
}
// SQLiteBackend — the default backend: one pure-Go sqlite connection per
// read, rows returned as tuples (the oracle's [tuple(r) for r in ...]).
type SQLiteBackend struct{}
func (SQLiteBackend) Name() string { return "sqlite" }
func (SQLiteBackend) ReadQuery(dbPath, query string, params ...interface{}) [][]interface{} {
db, err := sql.Open("sqlite", dbPath)
if err != nil {
return nil
}
defer db.Close()
rows, err := db.Query(query, params...)
if err != nil {
return nil
}
defer rows.Close()
cols, _ := rows.Columns()
out := [][]interface{}{}
vals := make([]interface{}, len(cols))
ptrs := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
for rows.Next() {
newVals := make([]interface{}, len(cols))
for i := range vals {
ptrs[i] = &newVals[i]
}
rows.Scan(ptrs...)
for i, v := range newVals {
if b, ok := v.([]byte); ok {
newVals[i] = string(b)
}
}
out = append(out, newVals)
}
return out
}
// RustReadBackend — the Rust read path through the memory_storage crate
// (read-only by construction). The Go crate reaches libmemory_storage only
// through the facade's FFI, so the reader is injectable; the battery (and
// any future cgo membrane) supplies it.
type RustReadBackend struct {
Read func(dbPath, query string, params []interface{}) [][]interface{}
}
func (RustReadBackend) Name() string { return "rust-memory-storage" }
func (b RustReadBackend) ReadQuery(dbPath, query string, params ...interface{}) [][]interface{} {
return b.Read(dbPath, query, params)
}
// The mems_loader.available() probe + backend factory seams (battery
// injects both; nil = the loader is absent, which is the Go crate's
// truthful default — the oracle's fallback-not-silence law).
var (
rustStorageProbe func() bool
rustBackendFactory func() ReadBackend
)
// StorageBackend — the active read backend: default sqlite;
// RUNE_MEMORY_STORAGE=rust opts in when the probe admits it, falling back
// loudly-free to sqlite otherwise.
func StorageBackend() ReadBackend {
if os.Getenv("RUNE_MEMORY_STORAGE") == "rust" &&
rustStorageProbe != nil && rustStorageProbe() &&
rustBackendFactory != nil {
return rustBackendFactory()
}
return SQLiteBackend{}
}
// TopicCount — one by_topic GROUP BY row; insertion order is part of the
// contract (Python dict emission order — Go maps would sort it away).
type TopicCount struct {
Topic string
N int64
}
// StatsForPath — stats() through the storage backend seam: the opt-in
// path-based consumer. Returns the result dict plus the top_topics rows in
// emission order.
func StatsForPath(backend ReadBackend, dbPath string) (map[string]interface{}, []TopicCount) {
e := backend.ReadQuery(dbPath, "SELECT COUNT(*) FROM entries WHERE status='active'")[0][0]
w := backend.ReadQuery(dbPath, "SELECT COUNT(*) FROM working")[0][0]
a := backend.ReadQuery(dbPath, "SELECT COUNT(*) FROM audit_log")[0][0]
rows := backend.ReadQuery(
dbPath,
"SELECT topic, COUNT(*) AS n FROM entries WHERE status='active' GROUP BY topic ORDER BY n DESC LIMIT 6",
)
topics := map[string]interface{}{}
var order []TopicCount
for _, r := range rows {
topic, _ := r[0].(string)
if topic == "" { // the oracle's `if r[0]` filters '' and NULL alike
continue
}
var n int64
switch v := r[1].(type) { // native sqlite scans int64; JSON-membrane rows float64
case int64:
n = v
case float64:
n = int64(v)
}
topics[topic] = n
order = append(order, TopicCount{Topic: topic, N: n})
}
return map[string]interface{}{
"active_entries": e, "working": w, "audit": a, "top_topics": topics,
}, order
}
// ── reindex_entries ──────────────────────────────────────────────────────────
// EmbedFn — the _embed seam (facade-side network call). nil or a nil
// return leaves the dense side off for that text.
type EmbedFn func(text string) []float64
// pyJSONFloatList — Python json.dumps over a float slice: ", " separators
// and Python float repr (integral floats keep the trailing .0 — the quirk
// law; Go json.Marshal would emit "1" and ",").
func pyJSONFloatList(xs []float64) string {
parts := make([]string, len(xs))
for i, x := range xs {
if x == float64(int64(x)) && x > -1e15 && x < 1e15 {
parts[i] = fmt.Sprintf("%.1f", x)
} else {
parts[i] = fmt.Sprintf("%g", x)
}
}
return "[" + strings.Join(parts, ", ") + "]"
}
func countQuery(db *sql.DB, query string) int {
var n int
if err := db.QueryRow(query).Scan(&n); err != nil {
return 0
}
return n
}
// ReindexEntries — rebuild the vector + FTS indexes for all active entries:
// dry-run reports the gap without writing; a missing/dangling/stale FTS
// state triggers the full rebuild (vec clear, contentless FTS5 DROP +
// recreate — DELETE cannot touch contentless tables), then reinsert of every
// active entry. Mirrors the oracle's per-statement try/except tolerance:
// every index-side statement failure counts and continues.
func ReindexEntries(db *sql.DB, embed EmbedFn, dryRun bool) map[string]interface{} {
total := countQuery(db, "SELECT COUNT(*) FROM entries WHERE status='active'")
indexed := countQuery(db, "SELECT COUNT(*) FROM entries_vec")
missing := total - indexed
if missing < 0 {
missing = 0
}
dangling := 0
if err := db.QueryRow(
"SELECT COUNT(*) AS c FROM entries_vec v LEFT JOIN entries e ON e.id = v.rowid WHERE e.id IS NULL",
).Scan(&dangling); err != nil {
dangling = 0
}
ftsDangling := 0
if err := db.QueryRow(
"SELECT COUNT(*) AS c FROM entries_fts f LEFT JOIN entries e ON e.id = f.rowid WHERE e.id IS NULL",
).Scan(&ftsDangling); err != nil {
ftsDangling = 0
}
ftsTotal := -1
if err := db.QueryRow("SELECT COUNT(*) FROM entries_fts").Scan(&ftsTotal); err != nil {
ftsTotal = -1
}
if dryRun {
return map[string]interface{}{
"indexed": indexed, "total": total, "missing": missing,
"dangling": dangling, "fts_dangling": ftsDangling, "fts_total": ftsTotal,
"dry_run": true,
}
}
ftsStale := ftsTotal >= 0 && ftsTotal != total
if missing > 0 || dangling > 0 || ftsDangling > 0 || ftsStale {
db.Exec("DELETE FROM entries_vec")
db.Exec("DELETE FROM entries_fts") // contentless: raises, swallowed (oracle try/except)
if _, err := db.Exec("DROP TABLE IF EXISTS entries_fts"); err == nil {
db.Exec("CREATE VIRTUAL TABLE entries_fts USING fts5(text, content='', tokenize='porter unicode61')")
}
rows, err := db.Query("SELECT id, text FROM entries WHERE status='active'")
type idText struct {
id int64
text string
}
var list []idText
if err == nil {
for rows.Next() {
var r idText
rows.Scan(&r.id, &r.text)
list = append(list, r)
}
rows.Close()
}
vecMissing, ftsMissing := 0, 0
for _, r := range list {
var vec []float64
if embed != nil {
vec = embed(r.text)
}
if vec != nil {
if _, err := db.Exec("INSERT INTO entries_vec (rowid, embedding) VALUES (?,?)",
r.id, pyJSONFloatList(vec)); err != nil {
vecMissing++
}
} else {
vecMissing++
}
if _, err := db.Exec("INSERT INTO entries_fts (rowid, text) VALUES (?,?)", r.id, r.text); err != nil {
ftsMissing++
}
}
db.Exec("COMMIT")
return map[string]interface{}{
"indexed": len(list), "total": total, "missing": missing,
"dangling": dangling, "fts_dangling": ftsDangling,
"vec_missing": vecMissing, "fts_missing": ftsMissing,
"dry_run": false, "rebuilt": true,
}
}
return map[string]interface{}{
"indexed": indexed, "total": total, "missing": 0,
"dangling": 0, "fts_dangling": 0,
"dry_run": false, "rebuilt": false,
}
}
+316
View File
@@ -0,0 +1,316 @@
package main
// gaps_stats_test.go — memory_store STATS family parity gate: replays
// truth_stats_gaps.json (live oracle capture under the generator's rigs —
// builder factory-heavy-ms-stats). Canonical-JSON comparison (canonEq);
// GAPS_TRUTH_PATH overrides the truth file for live-parity runs against
// freshly regenerated truth. Fixture builders mirror the generator's DDLs
// exactly (same shape both sides of the membrane).
import (
"database/sql"
"encoding/json"
"fmt"
"os"
"path/filepath"
"testing"
_ "modernc.org/sqlite"
)
func execDDL(t *testing.T, db *sql.DB, stmts ...string) {
t.Helper()
for _, s := range stmts {
if _, err := db.Exec(s); err != nil {
t.Fatalf("ddl: %v", err)
}
}
}
const statsDDL = `
CREATE TABLE entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT NOT NULL,
topic TEXT DEFAULT '',
status TEXT DEFAULT 'active'
)`
const statsWorkingDDL = `
CREATE TABLE working (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id TEXT NOT NULL,
text TEXT NOT NULL,
created_at TEXT NOT NULL
)`
const statsAuditDDL = `
CREATE TABLE audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
prev_hash TEXT NOT NULL,
hash TEXT NOT NULL,
claim TEXT,
verdict TEXT,
evidence TEXT,
created_at TEXT NOT NULL
)`
const reindexEntriesDDL = `
CREATE TABLE entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT NOT NULL,
status TEXT DEFAULT 'active'
)`
const reindexVecDDL = `CREATE TABLE entries_vec (rowid INTEGER PRIMARY KEY, embedding TEXT)`
const reindexFtsDDL = `CREATE VIRTUAL TABLE entries_fts USING fts5(text, content='', tokenize='porter unicode61')`
// buildStatsStore — the stats_for_path fixture on a real FILE (the backend
// opens one connection per read, mirroring the oracle's sqlite3.connect).
func buildStatsStore(t *testing.T, entries [][]interface{}, working, audit int) string {
t.Helper()
path := filepath.Join(t.TempDir(), "store.db")
db, err := sql.Open("sqlite", path)
if err != nil {
t.Fatalf("open store: %v", err)
}
defer db.Close()
execDDL(t, db, statsDDL, statsWorkingDDL, statsAuditDDL)
for _, e := range entries {
db.Exec("INSERT INTO entries (id, text, topic, status) VALUES (?,?,?,?)", e[0], e[1], e[2], e[3])
}
for i := 1; i <= working; i++ {
db.Exec("INSERT INTO working (id, task_id, text, created_at) VALUES (?,?,?,?)",
i, fmt.Sprintf("t%d", i), fmt.Sprintf("working note %d", i), "2026-01-01T00:00:00+00:00")
}
for i := 1; i <= audit; i++ {
db.Exec("INSERT INTO audit_log (id, prev_hash, hash, claim, verdict, evidence, created_at) VALUES (?,?,?,?,?,?,?)",
i, "GENESIS", "hhhhhhhhhhhhhhhhhhhhhhhh", fmt.Sprintf("claim %d", i), "pass", "", "2026-01-01T00:00:00+00:00")
}
return path
}
// buildReindexStore — the reindex fixture in-memory (same DDL as the rig).
func buildReindexStore(t *testing.T, entries, vecRows, ftsRows [][]interface{}) *sql.DB {
t.Helper()
db, err := sql.Open("sqlite", ":memory:")
if err != nil {
t.Fatalf("open reindex store: %v", err)
}
execDDL(t, db, reindexEntriesDDL, reindexVecDDL, reindexFtsDDL)
for _, e := range entries {
db.Exec("INSERT INTO entries (id, text, status) VALUES (?,?,?)", e[0], e[1], e[2])
}
for _, v := range vecRows {
db.Exec("INSERT INTO entries_vec (rowid, embedding) VALUES (?,?)", v[0], v[1])
}
for _, f := range ftsRows {
db.Exec("INSERT INTO entries_fts (rowid, text) VALUES (?,?)", f[0], f[1])
}
return db
}
func reindexPostState(t *testing.T, db *sql.DB) map[string]interface{} {
t.Helper()
post := map[string]interface{}{}
fts := []interface{}{}
rows, err := db.Query("SELECT rowid, text FROM entries_fts ORDER BY rowid")
if err == nil {
for rows.Next() {
var rid int64
var text sql.NullString
rows.Scan(&rid, &text)
var tv interface{}
if text.Valid {
tv = text.String
}
fts = append(fts, []interface{}{rid, tv})
}
rows.Close()
}
vec := []interface{}{}
rows, err = db.Query("SELECT rowid, embedding FROM entries_vec ORDER BY rowid")
if err == nil {
for rows.Next() {
var rid int64
var emb sql.NullString
rows.Scan(&rid, &emb)
var ev interface{}
if emb.Valid {
ev = emb.String
}
vec = append(vec, []interface{}{rid, ev})
}
rows.Close()
}
post["fts"] = fts
post["vec"] = vec
return post
}
func TestStatsGapsParity(t *testing.T) {
path := "truth_stats_gaps.json"
if p := os.Getenv("GAPS_TRUTH_PATH"); p != "" {
path = p
}
b, err := os.ReadFile(path)
if err != nil {
t.Fatalf("truth file missing: %v", err)
}
var doc struct {
Cases []struct {
Fn string `json:"fn"`
Name string `json:"name"`
Input map[string]interface{} `json:"input"`
Expected interface{} `json:"expected"`
} `json:"cases"`
}
if err := json.Unmarshal(b, &doc); err != nil {
t.Fatalf("unparseable: %v", err)
}
ran := 0
// restore the backend seams after the battery (each case re-injects before use)
defer func() { rustStorageProbe = nil; rustBackendFactory = nil }()
for _, c := range doc.Cases {
switch c.Fn {
case "handle_hook":
var data interface{}
if v, ok := c.Input["data"]; ok {
data = v
}
got := HandleHook(str(c.Input["event"]), data)
canonEq(t, c.Name, "envelope", got, c.Expected)
case "wake_up":
var rows []WakeRow
for _, r := range objList(c.Input["rows"]) {
rows = append(rows, WakeRow{
Text: str(r["text"]),
Topic: str(r["topic"]),
Importance: num(r["importance"]),
Source: str(r["source"]),
})
}
got := WakeUp(rows, int(num(c.Input["budget_chars"])))
canonEq(t, c.Name, "context", got, c.Expected)
case "storage_backend":
envVal := ""
if v, ok := c.Input["env"]; ok && v != nil {
envVal = str(v)
}
t.Setenv("RUNE_MEMORY_STORAGE", envVal)
rustStorageProbe = func() bool {
if v, ok := c.Input["loader_available"]; ok {
return boolOf(v)
}
return false
}
rustBackendFactory = func() ReadBackend {
return RustReadBackend{Read: func(dbPath, query string, params []interface{}) [][]interface{} {
return nil
}}
}
got := StorageBackend().Name()
canonEq(t, c.Name, "name", map[string]interface{}{"name": got}, c.Expected)
case "stats_for_path":
exp, _ := c.Expected.(map[string]interface{})
backendKind := str(c.Input["backend"])
var backend ReadBackend
var traffic []map[string]interface{}
if backendKind == "rust" {
scripted := listy(c.Input["scripted"])
t.Setenv("RUNE_MEMORY_STORAGE", "rust")
rustStorageProbe = func() bool { return true }
rustBackendFactory = func() ReadBackend {
return RustReadBackend{Read: func(dbPath, query string, params []interface{}) [][]interface{} {
call := map[string]interface{}{"sql": query}
pl := []interface{}{}
for _, p := range params {
pl = append(pl, p)
}
call["params"] = pl
traffic = append(traffic, call)
var rows [][]interface{}
if len(traffic) <= len(scripted) {
for _, r := range listy(scripted[len(traffic)-1]) {
l := listy(r)
row := make([]interface{}, len(l))
for i, v := range l {
row[i] = v
}
rows = append(rows, row)
}
}
return rows
}}
}
backend = StorageBackend()
} else {
entries := listy(c.Input["entries"])
ent := make([][]interface{}, 0, len(entries))
for _, e := range entries {
l := listy(e)
row := make([]interface{}, len(l))
for i, v := range l {
row[i] = v
}
ent = append(ent, row)
}
store := buildStatsStore(t, ent, int(num(c.Input["working"])), int(num(c.Input["audit"])))
got, order := StatsForPath(SQLiteBackend{}, store)
canonEq(t, c.Name, "stats", got, exp["stats"])
checkTopicOrder(t, c.Name, order, exp["top_topics_order"])
ran++
continue
}
got, order := StatsForPath(backend, "scripted.db")
canonEq(t, c.Name, "stats", got, exp["stats"])
checkTopicOrder(t, c.Name, order, exp["top_topics_order"])
canonEq(t, c.Name, "queries", traffic, listy(exp["queries"]))
case "reindex_entries":
toRows := func(v interface{}) [][]interface{} {
var out [][]interface{}
for _, r := range listy(v) {
l := listy(r)
row := make([]interface{}, len(l))
for i, x := range l {
row[i] = x
}
out = append(out, row)
}
return out
}
db := buildReindexStore(t, toRows(c.Input["entries"]), toRows(c.Input["vec_rows"]), toRows(c.Input["fts_rows"]))
var embedFn EmbedFn
if m, ok := c.Input["embed"].(map[string]interface{}); ok && m != nil {
embedFn = func(text string) []float64 {
raw, ok := m[text]
if !ok {
return nil
}
var vec []float64
for _, x := range listy(raw) {
vec = append(vec, num(x))
}
return vec
}
}
got := ReindexEntries(db, embedFn, boolOf(c.Input["dry_run"]))
exp, _ := c.Expected.(map[string]interface{})
canonEq(t, c.Name, "result", got, exp["result"])
canonEq(t, c.Name, "post", reindexPostState(t, db), exp["post"])
db.Close()
default:
t.Errorf("unknown fn kind: %s", c.Fn)
}
ran++
}
if ran == 0 {
t.Fatalf("no cases ran")
}
t.Logf("%d cases replayed", ran)
}
func checkTopicOrder(t *testing.T, name string, got []TopicCount, want interface{}) {
t.Helper()
gotPairs := []interface{}{}
for _, tc := range got {
gotPairs = append(gotPairs, []interface{}{tc.Topic, tc.N})
}
canonEq(t, name, "top_topics_order", gotPairs, listy(want))
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,9 @@
FAILED tests/test_rust_core_shims.py::test_capability_shims_delegation_agrees_with_fallback
FAILED tests/test_rust_core_shims.py::test_construction_decision_placement_delegation
FAILED tests/test_rust_core_shims.py::test_drift_shims_delegation_agrees_with_fallback
FAILED tests/test_rust_core_shims.py::test_memory_store_helpers_delegate_to_kernel
FAILED tests/test_rust_core_shims.py::test_mr_loader_chunk_sql_gather_through_seam
FAILED tests/test_rust_core_shims.py::test_spine_delegation_agrees_with_fallback
FAILED tests/test_rust_core_shims.py::test_token_optimizer_class_surface - Ru...
FAILED tests/test_rust_core_shims.py::test_token_optimizer_delegation_agrees_with_fallback
FAILED tests/test_rust_core_shims.py::test_warm_base_level_math_and_gather - ...
+1
View File
@@ -0,0 +1 @@
exit=0 (22 passed, 9 failed, 25 skipped — failure set byte-identical across both pins and to HEAD by construction: no tracked Python/tests files touched)
+9
View File
@@ -0,0 +1,9 @@
FAILED tests/test_rust_core_shims.py::test_capability_shims_delegation_agrees_with_fallback
FAILED tests/test_rust_core_shims.py::test_construction_decision_placement_delegation
FAILED tests/test_rust_core_shims.py::test_drift_shims_delegation_agrees_with_fallback
FAILED tests/test_rust_core_shims.py::test_memory_store_helpers_delegate_to_kernel
FAILED tests/test_rust_core_shims.py::test_mr_loader_chunk_sql_gather_through_seam
FAILED tests/test_rust_core_shims.py::test_spine_delegation_agrees_with_fallback
FAILED tests/test_rust_core_shims.py::test_token_optimizer_class_surface - Ru...
FAILED tests/test_rust_core_shims.py::test_token_optimizer_delegation_agrees_with_fallback
FAILED tests/test_rust_core_shims.py::test_warm_base_level_math_and_gather - ...
+1
View File
@@ -0,0 +1 @@
exit=0 (RUNE_RETRIEVAL_CORE=python pin: 22 passed, 9 failed, 25 skipped — set identical to live pin, see msstats-py-*-baseline.txt)
+108
View File
@@ -0,0 +1,108 @@
# memory_store STATS family gap-close — verification summary
- **Module**: memory_store (audit alias: memory-ops + memory-store-deep GO corpus)
- **Family**: working-set/stats/audit — builder `factory-heavy-ms-stats`
- **Worktree**: /mnt/workdrive/rune-wave-en (isolated, tracks gitea/master)
- **Date**: 2026-10-15
## What was ported (name-preserving, src/go/memory-store-deep/gaps_stats.go — NEW file, siblings untouched)
The 5 CURRENTLY-UNMATCHED fns of this lane's slice (audit re-run first, per
law; the other 12 slice fns — working_set/get/clear, audit_append/verify,
stats, touch, memory_health_check, tier_demotion_sweep, _map_reduce, main —
were already landed by earlier waves in main.go/gaps_retrieval.go):
- `reindex_entries` → `ReindexEntries`: gap census (total/indexed/missing,
vec+fts dangling via LEFT JOIN, fts staleness), dry-run report, full
rebuild (vec clear; contentless-FTS5 DROP+recreate — DELETE cannot touch
contentless tables, error swallowed mirror), per-row reinsert with
vec_missing/fts_missing counting, exact result-dict shapes for all three
branches.
- `wake_up` → `WakeUp`: pre-fetched rows → "- text" lines under a
CHAR-budget (rune count, never bytes; pinned by `wu_chars_not_bytes` —
17-char greek line fits budget 20 where 32 UTF-8 bytes would not).
- `storage_backend` → `StorageBackend` + `SQLiteBackend`/`RustReadBackend`:
the R2 W2.0 read-backend seam. Default sqlite; RUNE_MEMORY_STORAGE=rust
opts in when the probe seam admits it (fallback loudly-free to sqlite).
The Go crate reaches libmemory_storage only via the facade FFI, so the
probe + reader are injectable seams (mems_loader is Python FFI machinery).
- `stats_for_path` → `StatsForPath`: stats() through the backend seam; real
file reads through SQLiteBackend (one connection per read, mirroring the
oracle), by_topic rows in EMISSION order (`top_topics` insertion order is
contract — Go maps would sort it away; order rides a parallel
TopicCount list), '' and NULL topics filtered (the `if r[0]` law).
- `handle_hook` → `HandleHook`: the default hook envelope.
Seam decisions (documented, not bugs): the vec side of reindex runs on the
plain-table entries_vec mirror — production vec0 is observed only through
COUNT/LEFT JOIN/DELETE/INSERT, which a plain table serves identically (Go
has no vec0; the rig uses the same mirror, parity by construction).
## Gates (all exit 0; logs in this directory)
| Gate | Evidence |
|---|---|
| gofmt -l (clean) | msstats-gofmt.log |
| go vet ./... | msstats-vet.log |
| go build ./... | msstats-build.log |
| go test ./... (whole crate, incl. prior batteries) | msstats-gotest.log |
| frozen truth byte-identical, run 1 | msstats-freeze-verify-1.log |
| frozen truth byte-identical, run 2 | msstats-freeze-verify-2.log |
| live parity (fresh oracle capture → Go battery) | msstats-parity.log |
| ruff (both scripts) | msstats-ruff.log |
| Python module suite, live pin | msstats-py-live.exit |
| Python module suite, RUNE_RETRIEVAL_CORE=python | msstats-py-pin.exit |
| failure-set identity, both pins | msstats-py-{live,pin}-baseline.txt (9=9, byte-identical) |
Truth: `src/go/memory-store-deep/truth_stats_gaps.json` — 24 cases.
All cases run the live oracle under pinned rigs (HOME pinned before import,
frozen clock on memory_store.datetime, _embed pinned off or scripted
deterministic vectors, fake mems_loader via sys.modules injection,
RUNE_MEMORY_STORAGE juggling with restore). No wall-clock, no absolute
paths, no HOME-derived values in any truth row. Generator:
scripts/gen_memory_store_stats_truth.py (--check / --out) — byte-identical
across regenerations. Battery gaps_stats_test.go replays canonically
(canonEq) with GAPS_TRUTH_PATH override; parity:
scripts/check_memory_store_stats_parity.py (fresh live-oracle capture →
diff vs frozen → Go battery on fresh truth).
Python both-pins: tests/test_memory_port.py + test_memory_types_port.py +
test_rust_core_shims.py — 22 passed / 9 failed / 25 skipped in BOTH pins,
failure sets byte-identical (see baseline files). The 9 are pre-existing
delegation/seam tests (mr_loader/capability/drift/spine/token_optimizer
.sho dependencies), untouched by this port — the worktree modifies no
tracked Python or tests files (git status: docs/ audit doc + new
scripts/go files only), so HEAD-equivalence is by construction.
## Parity findings (documented divergences, not bugs)
1. **Contentless FTS5 reads back NULL text**: `SELECT rowid, text FROM
entries_fts` returns NULL for text (content='' stores nothing — the
oracle inserts text on rebuild, reads still see NULL). Both engines
agree (cpython + modernc); the post-state truth rows pin `[[id, null]]`.
2. **Python json.dumps float-list formatting in the vec corpus**:
"[0.5, 0.25]" (", " separators) and integral floats keep ".0"
("[1.0, 0.5]") — Go json.Marshal would emit "," and "1". Mirrored by
pyJSONFloatList (quirk law), pinned by `ri_embed_on` post state.
3. **stats_for_path by_topic tie order is engine-shaped**: GROUP BY topic
ORDER BY n DESC leaves count-ties to the engine's sorter. Fixtures use
distinct counts only (tie behavior is not contract; both engines run
real SQLite here anyway).
4. **read_query ×2 counters flipped by this port's interface**: the audit
now matches Python `read_query` (both backend-class methods — the dm
lane's slice) against Go `ReadQuery`, the seam interface method name
this port introduces. NOT a dm-lane port; dm still owns Python
read_query's real parity surface. Reported as a name-collision flip.
## Audit delta
memory_store: 44/68 → **51/68** (75%; ≥80% FULL needs 55). This lane's
slice (working_set/get/clear, audit_append/verify, stats, storage_backend,
stats_for_path, touch, reindex_entries, memory_health_check,
tier_demotion_sweep, _map_reduce, wake_up, handle_hook, main) is now fully
matched. Remaining missing fns belong to the IO/schema (dm) and mining
(em) lanes: _ctx_budget, now_iso, ensure_memory_type_column, _temp_json,
_entities_to_json, _aaak_summary, _auto_associate, associate,
_run_lexicon_reconcile, _extract_epub, add_chunk, hierarchy, list_scenes,
make_scene, mine_directory, mine_file, scene_entries. Module NOT FULL →
worktree KEPT per spawn rule.