feat(memory_consolidation): close PARTIAL gaps — dichotomy wave
CI Pipeline / rust-surface (push) Failing after 20s
CI Pipeline / best-practices (push) Successful in 4s
CI Pipeline / go-surface (push) Failing after 24s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / type-check (push) Canceled after 22s
Auto-version and Release / release (push) Canceled after 48s
CI Pipeline / unit-tests (push) Canceled after 46s
CI Pipeline / lint (push) Canceled after 46s
CI Pipeline / canary (push) Canceled after 47s
CI Pipeline / gitea-offline (push) Canceled after 20s
deploy / deploy (push) Canceled after 50s
CI Pipeline / rust-surface (push) Failing after 20s
CI Pipeline / best-practices (push) Successful in 4s
CI Pipeline / go-surface (push) Failing after 24s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / type-check (push) Canceled after 22s
Auto-version and Release / release (push) Canceled after 48s
CI Pipeline / unit-tests (push) Canceled after 46s
CI Pipeline / lint (push) Canceled after 46s
CI Pipeline / canary (push) Canceled after 47s
CI Pipeline / gitea-offline (push) Canceled after 20s
deploy / deploy (push) Canceled after 50s
Contract BUILDER-2026-10-13T02:44:35Z-411615-factory-gap-mc. The 12 audit-named missing fns ported name-preserving into src/go/memory-consolidation (ConsolidationNow, ConsolidationMemoryOracle, ConsolidationNeuronOracle, LoadKnowledge/SaveKnowledge/LoadLessons/ SaveLessons/LoadConnections/SaveConnections, consolidateNeuron, consolidateMemory, getRelevantKnowledge) plus the remaining 4 stragglers (getRelevantLessons, getRelevantConnections, formatKnowledgeForPrompt, getStatus) — 23/23 fn coverage, audit FULL. Logic 1:1 with the Python oracle; IO and non-portables behind injectable seams (Clock, Compressor, EntryLookup, store paths). Truth frozen through the SHARED harness (scripts/freeze_truth.py + scripts/mc_freeze_seams/sitecustomize.py rig: RUNE_COGNITION_CORE/RUNE_MEMORY_CORE pinned python, frozen clock, hermetic MC_FREEZE_HOME, module-level seam bindings so the harness can getattr the class methods) — 41 rows, byte-identical --verify vs the live oracle, embedded VERBATIM and replayed by the go test battery and the binary replay gate (PARITY OK 41/41; fresh re-freeze byte-identical = live parity 100%). gofmt/vet clean; delegation tests green live (CC_RUST_LIB kernel, 7/7) and oracle-pinned (RUNE_COGNITION_CORE=python). Divergence, documented: Python hash() ids are process-randomized — the entry-none branch pins the established id_prefix/entry_none shape (truth_constructors.json precedent) instead of a non-portable hash.
This commit is contained in:
1 parent
0703d0d17b
commit
4825d17cc7
7 files changed
+2564
No files matched your search
@@ -0,0 +1,159 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Freeze the dichotomy-wave gap truth for src/rune/memory_consolidation
|
||||
through the SHARED harness (scripts/freeze_truth.py — one law).
|
||||
|
||||
The oracle's module-level surface is clock-carrying (datetime.now inside
|
||||
every fn) and its port surface is mostly class methods, so this generator
|
||||
runs under the freeze rig (scripts/mc_freeze_seams/sitecustomize.py):
|
||||
pinned Python floors, frozen clock, hermetic consolidation store, and
|
||||
module-level seam bindings the shared harness can getattr.
|
||||
|
||||
Run (from the repo root):
|
||||
PYTHONHASHSEED=0 MC_FREEZE_HOME=/tmp/mc_gap_freeze_home \
|
||||
PYTHONPATH=scripts/mc_freeze_seams:src \
|
||||
python3 scripts/gen_memory_consolidation_truth.py
|
||||
|
||||
Verify (byte-identical, same rig):
|
||||
PYTHONHASHSEED=0 MC_FREEZE_HOME=/tmp/mc_gap_freeze_home \
|
||||
PYTHONPATH=scripts/mc_freeze_seams:src \
|
||||
python3 scripts/freeze_truth.py --verify \
|
||||
src/go/memory-consolidation/truth_gaps.json \
|
||||
--module rune.memory_consolidation
|
||||
|
||||
Every public gap fn and branch is exercised: both consolidation oracles
|
||||
(drift floor: washable 0/partial 10+/full 20+; no-drift truncation floors
|
||||
incl. the [:97]+"..." and empty-first-line branches), consolidate_neuron
|
||||
(slug/body+text/importance defaults, string-triggers normalization,
|
||||
silent store lookup), consolidate_memory, get_relevant_knowledge
|
||||
(overlap + tag*2 + confidence*3, score>0 gate, stable desc sort, limit
|
||||
cut), and the six store fns (absent/seeded file load, save roundtrip).
|
||||
"""
|
||||
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
|
||||
|
||||
T = "2026-10-01T00:00:00+00:00"
|
||||
|
||||
KSEED = [
|
||||
{"id": "k1", "source": "memory", "category": "lesson",
|
||||
"content": "the cron engine parses field expressions", "confidence": 0.9,
|
||||
"created_at": T, "last_used": T, "use_count": 2, "wash_count": 1,
|
||||
"entry_id": 7, "tags": ["cron", "parser"]},
|
||||
{"id": "k2", "source": "graph", "category": "connection",
|
||||
"content": "budget tracking for providers", "confidence": 0.5,
|
||||
"created_at": T, "last_used": T, "use_count": 0, "wash_count": 0,
|
||||
"entry_id": None, "tags": ["budget"]},
|
||||
{"id": "k3", "source": "neuron", "category": "fact",
|
||||
"content": "deploy runner labels need care", "confidence": 0.8,
|
||||
"created_at": T, "last_used": T, "use_count": 0, "wash_count": 0,
|
||||
"entry_id": 9, "tags": ["deploy"]},
|
||||
]
|
||||
|
||||
LSEED = [
|
||||
{"id": "lesson-0", "trigger": "deploy fails", "lesson": "check the runner labels first",
|
||||
"context": "ci", "confidence": 0.9, "created_at": T, "applications": 1},
|
||||
]
|
||||
|
||||
CSEED = [
|
||||
{"id": "conn-0", "from_concept": "cron engine", "to_concept": "scheduler",
|
||||
"relationship": "leads_to", "strength": 0.8, "created_at": T,
|
||||
"evidence": ["e1"]},
|
||||
]
|
||||
|
||||
LSEED2 = LSEED + [
|
||||
{"id": "lesson-1", "trigger": "budget drift", "lesson": "watch provider spend",
|
||||
"context": "finance", "confidence": 0.7, "created_at": T, "applications": 0},
|
||||
]
|
||||
|
||||
cases = [
|
||||
# _core with RUNE_COGNITION_CORE=python pinned → kernel seam off (null).
|
||||
("_core", (), {}),
|
||||
# _consolidation_now: the frozen-clock pin rides through the oracle.
|
||||
("_consolidation_now", (), {}),
|
||||
# _consolidation_memory_oracle — drift floor: washable(0), partial(10+),
|
||||
# full(20+); entry_id set (hash ids are process-randomized: shape-pinned
|
||||
# in the Go battery, never frozen).
|
||||
("_consolidation_memory_oracle",
|
||||
("The deployment runner needs repair.", 0.9), {"wash_count": 5, "entry_id": 42}),
|
||||
("_consolidation_memory_oracle",
|
||||
("memory text here", 0.4), {"wash_count": 0, "entry_id": 7}),
|
||||
("_consolidation_memory_oracle",
|
||||
("The partial branch keeps a little more of this proven memory body text.", 0.7),
|
||||
{"wash_count": 12, "entry_id": 3}),
|
||||
("_consolidation_memory_oracle",
|
||||
("Rock solid " * 12, 0.95), {"wash_count": 25, "entry_id": 9}),
|
||||
# no-drift floor: >100 chars → [:100] + "..."
|
||||
("_consolidation_memory_oracle_no_drift", ("y" * 140, 0.5), {"entry_id": 5}),
|
||||
("_consolidation_memory_oracle_no_drift", ("short text", 0.5), {"entry_id": 6}),
|
||||
# _consolidation_neuron_oracle — drift floor branches.
|
||||
("_consolidation_neuron_oracle",
|
||||
("test-slug", "The runner needs repair.", 0.7),
|
||||
{"triggers": ["deploy"], "wash_count": 15}),
|
||||
("_consolidation_neuron_oracle",
|
||||
("rock", "This is a long rock-solid body that survives compression mostly. " * 4, 0.9),
|
||||
{"triggers": [], "wash_count": 25}),
|
||||
("_consolidation_neuron_oracle",
|
||||
("empty-body", "", 0.5), {"triggers": ["a", "b"], "wash_count": 0, "entry_id": 11}),
|
||||
# no-drift floor: first line kept / [:97]+"..." / empty-first-line→body[:100]
|
||||
("_consolidation_neuron_oracle_no_drift",
|
||||
("fb", "First line\nsecond line", 0.6), {}),
|
||||
("_consolidation_neuron_oracle_no_drift", ("fb", "z" * 140, 0.5), {}),
|
||||
("_consolidation_neuron_oracle_no_drift", ("fb", "\n\nrest body", 0.6), {}),
|
||||
# consolidate_neuron (method): defaults, string triggers, text fallback.
|
||||
("consolidate_neuron",
|
||||
({"slug": "cron-parser", "body": "The cron parser handles field expressions.",
|
||||
"importance": 0.8, "triggers": ["cron"], "wash_count": 3},), {}),
|
||||
("consolidate_neuron",
|
||||
({"slug": "strig", "body": "Body about deploy runner.",
|
||||
"importance": 0.6, "triggers": "deploy, runner ,"},), {}),
|
||||
("consolidate_neuron",
|
||||
({"slug": "ttext", "text": "body via text field"},), {}),
|
||||
("consolidate_neuron", ({},), {}),
|
||||
# consolidate_memory (method).
|
||||
("consolidate_memory",
|
||||
("The deployment runner needs repair.", 0.9), {"wash_count": 5, "entry_id": 42}),
|
||||
("consolidate_memory",
|
||||
("another recall", 0.4), {"wash_count": 0, "entry_id": 13}),
|
||||
# get_relevant_knowledge: overlap + tag*2 + confidence*3, stable desc,
|
||||
# limit cut, no-match-yet-confidence>0 gate.
|
||||
("get_relevant_knowledge", ("cron parser", 5), {"knowledge": KSEED}),
|
||||
("get_relevant_knowledge", ("cron parser", 1), {"knowledge": KSEED}),
|
||||
("get_relevant_knowledge", ("zzz-no-match", 5), {"knowledge": KSEED}),
|
||||
("get_relevant_knowledge", ("deploy", 5), {"knowledge": KSEED}),
|
||||
("get_relevant_knowledge", ("anything", 5), {"knowledge": []}),
|
||||
# get_relevant_lessons: trigger+lesson overlap, overlap>0 gate.
|
||||
("get_relevant_lessons", ("deploy runner", 3), {"lessons": LSEED2}),
|
||||
("get_relevant_lessons", ("zzz", 3), {"lessons": LSEED2}),
|
||||
# get_relevant_connections: from+to overlap.
|
||||
("get_relevant_connections", ("cron scheduler", 3), {"connections": CSEED}),
|
||||
("get_relevant_connections", ("zzz", 3), {"connections": CSEED}),
|
||||
# format_knowledge_for_prompt: full three-section render (entry link
|
||||
# included) and the confidence-only quirk (knowledge renders on
|
||||
# confidence*3 while pure-overlap lessons/connections drop out).
|
||||
("format_knowledge_for_prompt", ("cron",),
|
||||
{"knowledge": KSEED, "lessons": LSEED, "connections": CSEED}),
|
||||
("format_knowledge_for_prompt", ("zzz-no-match",),
|
||||
{"knowledge": KSEED, "lessons": LSEED, "connections": CSEED}),
|
||||
("format_knowledge_for_prompt", ("cron",),
|
||||
{"knowledge": [], "lessons": [], "connections": []}),
|
||||
# store fns: absent file → [], seeded load roundtrip, save roundtrip.
|
||||
("load_knowledge", (None,), {}),
|
||||
("load_knowledge", (KSEED,), {}),
|
||||
("save_knowledge", (KSEED,), {}),
|
||||
("load_lessons", (None,), {}),
|
||||
("load_lessons", (LSEED,), {}),
|
||||
("save_lessons", (LSEED,), {}),
|
||||
("load_connections", (None,), {}),
|
||||
("load_connections", (CSEED,), {}),
|
||||
("save_connections", (CSEED,), {}),
|
||||
]
|
||||
|
||||
truth = freeze("rune.memory_consolidation", cases,
|
||||
oracle_module="rune.memory_consolidation")
|
||||
out = REPO / "src" / "go" / "memory-consolidation" / "truth_gaps.json"
|
||||
write(truth, out)
|
||||
print(f"frozen {len(truth)} rows -> {out}")
|
||||
@@ -0,0 +1,195 @@
|
||||
# Freeze rig for the memory_consolidation dichotomy gap-closing (dichotomy
|
||||
# wave #2479 gap port). Auto-imported on interpreter start
|
||||
# (PYTHONPATH=scripts/mc_freeze_seams:src) by BOTH the truth generator
|
||||
# (scripts/gen_memory_consolidation_truth.py) and the shared harness's
|
||||
# --verify mode, so frozen truth is byte-reproducible in any process:
|
||||
#
|
||||
# * RUNE_COGNITION_CORE=python + RUNE_MEMORY_CORE=python — the oracle and
|
||||
# drift_compression run their pure-Python floors (kernel delegation pinned
|
||||
# OFF; poisoned-truth law — the shared harness's _pin_oracle_cores cannot
|
||||
# reach these two env gates, the oracle's _core() reads RUNE_COGNITION_CORE
|
||||
# via cc_loader, and compress_entry reads RUNE_MEMORY_CORE via drift_core).
|
||||
# * datetime.datetime is FROZEN to MC_FROZEN_NOW — the oracle reads
|
||||
# datetime.now(timezone.utc) at every clock site (inside the functions, so
|
||||
# the class patch below is the only pin that reaches them); the frozen
|
||||
# truth carries the constant instant and the Go battery replays through
|
||||
# the same pin (no-clock law: injected Clock seam).
|
||||
# * The class-method seams (consolidate_neuron, consolidate_memory,
|
||||
# get_relevant_knowledge, the _load_*/_save_* family) are bound onto the
|
||||
# module so the shared harness's getattr(module, fn) can freeze them;
|
||||
# wrappers return JSON-safe asdicts and seed/reset the hermetic
|
||||
# consolidation store (MC_FREEZE_HOME) so every row is self-contained.
|
||||
# * MC_FREEZE_HOME (if set) becomes HOME before the oracle imports, pinning
|
||||
# CONSOLIDATION_DIR/KNOWLEDGE_FILE/LESSONS_FILE/CONNECTIONS_FILE and the
|
||||
# memory_store lookups away from the real store.
|
||||
|
||||
import json
|
||||
import os
|
||||
from dataclasses import asdict
|
||||
from datetime import datetime, timezone
|
||||
|
||||
os.environ.setdefault("RUNE_COGNITION_CORE", "python")
|
||||
os.environ.setdefault("RUNE_MEMORY_CORE", "python")
|
||||
|
||||
_freeze_home = os.environ.get("MC_FREEZE_HOME")
|
||||
if _freeze_home:
|
||||
os.environ["HOME"] = _freeze_home
|
||||
|
||||
# MC_FROZEN_NOW: the pinned instant every clock site reports.
|
||||
FROZEN_NOW = "2026-10-13T09:41:23.123456+00:00"
|
||||
_FROZEN = datetime(2026, 10, 13, 9, 41, 23, 123456, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
class _FrozenDateTime(datetime):
|
||||
@classmethod
|
||||
def now(cls, tz=None):
|
||||
return _FROZEN if tz is not None else _FROZEN.replace(tzinfo=None)
|
||||
|
||||
|
||||
import datetime as _dtmod
|
||||
|
||||
_dtmod.datetime = _FrozenDateTime
|
||||
|
||||
import rune.memory_consolidation as mc # noqa: E402 (after the pins above)
|
||||
|
||||
|
||||
def _consolidation_now():
|
||||
return mc._consolidation_now()
|
||||
|
||||
|
||||
def _consolidation_memory_oracle(memory_text, score, wash_count=0, entry_id=None):
|
||||
return mc._consolidation_memory_oracle(memory_text, score, wash_count, entry_id)
|
||||
|
||||
|
||||
def _consolidation_memory_oracle_no_drift(memory_text, score, wash_count=0, entry_id=None):
|
||||
"""The oracle's drift_compression-absent floor (Go seam: Compressor nil)."""
|
||||
saved, mc.drift_compression = mc.drift_compression, None
|
||||
try:
|
||||
return mc._consolidation_memory_oracle(memory_text, score, wash_count, entry_id)
|
||||
finally:
|
||||
mc.drift_compression = saved
|
||||
|
||||
|
||||
def _consolidation_neuron_oracle(slug, body, importance, triggers=None, wash_count=0, entry_id=None):
|
||||
return mc._consolidation_neuron_oracle(slug, body, importance, triggers or [], wash_count, entry_id)
|
||||
|
||||
|
||||
def _consolidation_neuron_oracle_no_drift(slug, body, importance, triggers=None, wash_count=0, entry_id=None):
|
||||
saved, mc.drift_compression = mc.drift_compression, None
|
||||
try:
|
||||
return mc._consolidation_neuron_oracle(slug, body, importance, triggers or [], wash_count, entry_id)
|
||||
finally:
|
||||
mc.drift_compression = saved
|
||||
|
||||
|
||||
def _inst():
|
||||
return mc.MemoryConsolidation()
|
||||
|
||||
|
||||
def consolidate_neuron(neuron):
|
||||
return asdict(_inst().consolidate_neuron(neuron))
|
||||
|
||||
|
||||
def consolidate_memory(memory_text, score, wash_count=0, entry_id=None):
|
||||
return asdict(_inst().consolidate_memory(memory_text, score, wash_count, entry_id))
|
||||
|
||||
|
||||
def get_relevant_knowledge(query, limit=5, knowledge=None):
|
||||
inst = mc.MemoryConsolidation.__new__(mc.MemoryConsolidation)
|
||||
inst.knowledge = [mc.ConsolidatedKnowledge(**k) for k in (knowledge or [])]
|
||||
return [asdict(k) for k in inst.get_relevant_knowledge(query, limit)]
|
||||
|
||||
|
||||
def get_relevant_lessons(query, limit=3, lessons=None):
|
||||
inst = mc.MemoryConsolidation.__new__(mc.MemoryConsolidation)
|
||||
inst.lessons = [mc.Lesson(**x) for x in (lessons or [])]
|
||||
return [asdict(x) for x in inst.get_relevant_lessons(query, limit)]
|
||||
|
||||
|
||||
def get_relevant_connections(query, limit=3, connections=None):
|
||||
inst = mc.MemoryConsolidation.__new__(mc.MemoryConsolidation)
|
||||
inst.connections = [mc.Connection(**x) for x in (connections or [])]
|
||||
return [asdict(x) for x in inst.get_relevant_connections(query, limit)]
|
||||
|
||||
|
||||
def format_knowledge_for_prompt(query, knowledge=None, lessons=None, connections=None):
|
||||
inst = mc.MemoryConsolidation.__new__(mc.MemoryConsolidation)
|
||||
inst.knowledge = [mc.ConsolidatedKnowledge(**k) for k in (knowledge or [])]
|
||||
inst.lessons = [mc.Lesson(**x) for x in (lessons or [])]
|
||||
inst.connections = [mc.Connection(**x) for x in (connections or [])]
|
||||
return inst.format_knowledge_for_prompt(query)
|
||||
|
||||
|
||||
# ── store seams: seed/reset a row-local consolidation file, then load/save ──
|
||||
|
||||
|
||||
def _seeded_load(path, seed):
|
||||
"""Seed (or remove) a hermetic consolidation file, then read it through
|
||||
the oracle's loader — every frozen row is self-contained."""
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
if seed is None:
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
else:
|
||||
with open(path, "w") as f:
|
||||
json.dump(seed, f)
|
||||
|
||||
|
||||
def load_knowledge(seed):
|
||||
_seeded_load(mc.KNOWLEDGE_FILE, seed)
|
||||
return [asdict(k) for k in _inst()._load_knowledge()]
|
||||
|
||||
|
||||
def save_knowledge(items):
|
||||
inst = mc.MemoryConsolidation.__new__(mc.MemoryConsolidation)
|
||||
inst.knowledge = [mc.ConsolidatedKnowledge(**k) for k in items]
|
||||
os.makedirs(os.path.dirname(mc.KNOWLEDGE_FILE), exist_ok=True)
|
||||
inst._save_knowledge()
|
||||
with open(mc.KNOWLEDGE_FILE) as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def load_lessons(seed):
|
||||
_seeded_load(mc.LESSONS_FILE, seed)
|
||||
return [asdict(x) for x in _inst()._load_lessons()]
|
||||
|
||||
|
||||
def save_lessons(items):
|
||||
inst = mc.MemoryConsolidation.__new__(mc.MemoryConsolidation)
|
||||
inst.lessons = [mc.Lesson(**x) for x in items]
|
||||
os.makedirs(os.path.dirname(mc.LESSONS_FILE), exist_ok=True)
|
||||
inst._save_lessons()
|
||||
with open(mc.LESSONS_FILE) as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def load_connections(seed):
|
||||
_seeded_load(mc.CONNECTIONS_FILE, seed)
|
||||
return [asdict(x) for x in _inst()._load_connections()]
|
||||
|
||||
|
||||
def save_connections(items):
|
||||
inst = mc.MemoryConsolidation.__new__(mc.MemoryConsolidation)
|
||||
inst.connections = [mc.Connection(**x) for x in items]
|
||||
os.makedirs(os.path.dirname(mc.CONNECTIONS_FILE), exist_ok=True)
|
||||
inst._save_connections()
|
||||
with open(mc.CONNECTIONS_FILE) as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
# Bind the seams onto the oracle module so the shared harness's
|
||||
# getattr(module, fn) resolves every frozen fn name.
|
||||
mc._consolidation_memory_oracle_no_drift = _consolidation_memory_oracle_no_drift
|
||||
mc._consolidation_neuron_oracle_no_drift = _consolidation_neuron_oracle_no_drift
|
||||
mc.consolidate_neuron = consolidate_neuron
|
||||
mc.consolidate_memory = consolidate_memory
|
||||
mc.get_relevant_knowledge = get_relevant_knowledge
|
||||
mc.get_relevant_lessons = get_relevant_lessons
|
||||
mc.get_relevant_connections = get_relevant_connections
|
||||
mc.format_knowledge_for_prompt = format_knowledge_for_prompt
|
||||
mc.load_knowledge = load_knowledge
|
||||
mc.save_knowledge = save_knowledge
|
||||
mc.load_lessons = load_lessons
|
||||
mc.save_lessons = save_lessons
|
||||
mc.load_connections = load_connections
|
||||
mc.save_connections = save_connections
|
||||
@@ -0,0 +1,457 @@
|
||||
// gaps.go — the PARTIAL gap-closing for memory_consolidation (dichotomy
|
||||
// wave #2479, contract BUILDER-2026-10-13T02:44:35Z-411615-factory-gap-mc).
|
||||
// Name-preserving ports of the audit-named missing fns: _consolidation_now,
|
||||
// _consolidation_memory_oracle, _consolidation_neuron_oracle,
|
||||
// _load_knowledge, _save_knowledge, _load_lessons, _save_lessons,
|
||||
// _load_connections, _save_connections, consolidate_neuron,
|
||||
// consolidate_memory, get_relevant_knowledge. Logic 1:1 with the Python
|
||||
// oracle (src/rune/memory_consolidation.py); IO and non-portables sit
|
||||
// behind injectable seams (Clock, Compressor, EntryLookup, store paths).
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ── seams ────────────────────────────────────────────────────────────────────
|
||||
|
||||
// Clock supplies the consolidation timestamps (the oracle reads
|
||||
// datetime.now(timezone.utc) at every site; no-clock law: injected).
|
||||
type Clock func() time.Time
|
||||
|
||||
// Compressor mirrors drift_compression.compress_entry (Python floor ported
|
||||
// below as compressEntry; nil = the oracle's drift_compression-absent floor).
|
||||
type Compressor func(entry map[string]any) string
|
||||
|
||||
// EntryLookup mirrors the memory_store slug lookup in consolidate_neuron;
|
||||
// the oracle fails silent (entry_id None) — nil lookup keeps that shape.
|
||||
type EntryLookup func(slug string) *int
|
||||
|
||||
// frozenClock pins MC_FROZEN_NOW (scripts/mc_freeze_seams/sitecustomize.py)
|
||||
// — the instant the frozen truth was captured at.
|
||||
var frozenClock Clock = func() time.Time {
|
||||
t, err := time.Parse(time.RFC3339Nano, "2026-10-13T09:41:23.123456+00:00")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// ConsolidationNow mirrors _consolidation_now: UTC ISO-8601 with
|
||||
// microseconds and a +00:00 offset (Python datetime.isoformat shape).
|
||||
func ConsolidationNow(c Clock) string {
|
||||
return c().UTC().Format("2006-01-02T15:04:05.000000-07:00")
|
||||
}
|
||||
|
||||
// ── drift_compression pure floor (the oracle's compress_entry fallback) ─────
|
||||
|
||||
func getCompressionRatio(washCount int, rockStatus string) float64 {
|
||||
if rockStatus == "rock" {
|
||||
return 0.0
|
||||
}
|
||||
if rockStatus == "established" {
|
||||
return 0.2
|
||||
}
|
||||
base := 0.5
|
||||
if washCount >= 20 {
|
||||
return math.Max(0.1, base*0.2)
|
||||
} else if washCount >= 10 {
|
||||
return math.Max(0.2, base*0.5)
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
func compressText(text string, ratio float64) string {
|
||||
if ratio <= 0.0 {
|
||||
return text
|
||||
}
|
||||
if ratio >= 1.0 {
|
||||
return ""
|
||||
}
|
||||
r := []rune(text)
|
||||
keep := int(float64(len(r)) * (1.0 - ratio))
|
||||
if keep < 20 {
|
||||
keep = min(20, len(r))
|
||||
}
|
||||
compressed := string(r[:keep])
|
||||
if len([]rune(compressed)) < len(r) {
|
||||
compressed = strings.TrimRight(compressed, " \t\n\r\v\f") + "..."
|
||||
}
|
||||
return compressed
|
||||
}
|
||||
|
||||
// compressEntry is the default Compressor: the drift_compression Python
|
||||
// floor (washable entries, ratio by wash_count).
|
||||
func compressEntry(entry map[string]any) string {
|
||||
text, _ := entry["text"].(string)
|
||||
washCount := 0
|
||||
if f, ok := entry["wash_count"].(float64); ok {
|
||||
washCount = int(f)
|
||||
} else if i, ok := entry["wash_count"].(int); ok {
|
||||
washCount = i
|
||||
}
|
||||
rock, _ := entry["rock_status"].(string)
|
||||
if rock == "" {
|
||||
rock = "washable"
|
||||
}
|
||||
return compressText(text, getCompressionRatio(washCount, rock))
|
||||
}
|
||||
|
||||
// ── consolidation oracles ────────────────────────────────────────────────────
|
||||
|
||||
// ConsolidationMemoryOracle mirrors _consolidation_memory_oracle (Python
|
||||
// floor path): content via the compressor seam, id/clock adapter-owned.
|
||||
// The entry_id-None id branch uses Python's process-randomized hash() —
|
||||
// not portable; the established repo convention (truth_constructors.json
|
||||
// fact rows) pins the shape instead: id_prefix + entry_none.
|
||||
func ConsolidationMemoryOracle(memoryText string, score float64, washCount int, entryID *int, now Clock, compress Compressor) map[string]any {
|
||||
nowStr := ConsolidationNow(now)
|
||||
var content string
|
||||
if compress != nil {
|
||||
content = compress(map[string]any{"text": memoryText, "wash_count": washCount, "rock_status": "washable"})
|
||||
} else {
|
||||
r := []rune(memoryText)
|
||||
if len(r) > 100 {
|
||||
content = string(r[:100]) + "..."
|
||||
} else {
|
||||
content = memoryText
|
||||
}
|
||||
}
|
||||
rec := map[string]any{
|
||||
"source": "memory",
|
||||
"category": "lesson",
|
||||
"content": content,
|
||||
"confidence": score,
|
||||
"created_at": nowStr,
|
||||
"last_used": nowStr,
|
||||
"use_count": 0,
|
||||
"wash_count": washCount,
|
||||
"tags": []string{},
|
||||
}
|
||||
if entryID != nil {
|
||||
rec["id"] = fmt.Sprintf("memory-%d", *entryID)
|
||||
rec["entry_id"] = *entryID
|
||||
} else {
|
||||
rec["id_prefix"] = "memory"
|
||||
rec["entry_none"] = true
|
||||
rec["entry_id"] = nil
|
||||
}
|
||||
return rec
|
||||
}
|
||||
|
||||
// ConsolidationNeuronOracle mirrors _consolidation_neuron_oracle (Python
|
||||
// floor path): first-line content via the compressor seam.
|
||||
func ConsolidationNeuronOracle(slug, body string, importance float64, triggers []string, washCount int, entryID *int, now Clock, compress Compressor) map[string]any {
|
||||
nowStr := ConsolidationNow(now)
|
||||
var firstLine string
|
||||
if compress != nil {
|
||||
firstLine = compress(map[string]any{"text": body, "wash_count": washCount, "rock_status": "washable"})
|
||||
} else {
|
||||
firstLine = strings.TrimSpace(strings.Split(body, "\n")[0])
|
||||
r := []rune(firstLine)
|
||||
if len(r) > 100 {
|
||||
firstLine = string(r[:97]) + "..."
|
||||
}
|
||||
if firstLine == "" {
|
||||
rb := []rune(body)
|
||||
if len(rb) > 100 {
|
||||
firstLine = string(rb[:100])
|
||||
} else {
|
||||
firstLine = body
|
||||
}
|
||||
}
|
||||
}
|
||||
if triggers == nil {
|
||||
triggers = []string{}
|
||||
}
|
||||
rec := map[string]any{
|
||||
"id": "neuron-" + slug,
|
||||
"source": "neuron",
|
||||
"category": "fact",
|
||||
"content": firstLine,
|
||||
"confidence": importance,
|
||||
"created_at": nowStr,
|
||||
"last_used": nowStr,
|
||||
"use_count": 0,
|
||||
"wash_count": washCount,
|
||||
"entry_id": nil,
|
||||
"tags": triggers,
|
||||
}
|
||||
if entryID != nil {
|
||||
rec["entry_id"] = *entryID
|
||||
}
|
||||
return rec
|
||||
}
|
||||
|
||||
// consolidateNeuron mirrors MemoryConsolidation.consolidate_neuron: field
|
||||
// defaults, string-triggers normalization, silent store lookup, oracle call.
|
||||
func consolidateNeuron(neuron map[string]any, lookup EntryLookup, now Clock, compress Compressor) map[string]any {
|
||||
slug := "unknown"
|
||||
if s, ok := neuron["slug"].(string); ok {
|
||||
slug = s
|
||||
}
|
||||
var body string
|
||||
if v, ok := neuron["body"]; ok {
|
||||
body, _ = v.(string)
|
||||
} else if v, ok := neuron["text"]; ok {
|
||||
body, _ = v.(string)
|
||||
}
|
||||
importance := 0.5
|
||||
if f, ok := neuron["importance"].(float64); ok {
|
||||
importance = f
|
||||
}
|
||||
washCount := 0
|
||||
if f, ok := neuron["wash_count"].(float64); ok {
|
||||
washCount = int(f)
|
||||
}
|
||||
var triggers []string
|
||||
switch tv := neuron["triggers"].(type) {
|
||||
case string:
|
||||
for _, t := range strings.Split(tv, ",") {
|
||||
t = strings.TrimSpace(t)
|
||||
if t != "" {
|
||||
triggers = append(triggers, t)
|
||||
}
|
||||
}
|
||||
case []any:
|
||||
for _, t := range tv {
|
||||
if s, ok := t.(string); ok {
|
||||
triggers = append(triggers, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
var entryID *int
|
||||
if lookup != nil {
|
||||
entryID = lookup(slug)
|
||||
}
|
||||
return ConsolidationNeuronOracle(slug, body, importance, triggers, washCount, entryID, now, compress)
|
||||
}
|
||||
|
||||
// consolidateMemory mirrors MemoryConsolidation.consolidate_memory: a thin
|
||||
// adapter over the oracle (the ConsolidatedKnowledge(**rec) roundtrip is
|
||||
// identity-shaped in the map form).
|
||||
func consolidateMemory(memoryText string, score float64, washCount int, entryID *int, now Clock, compress Compressor) map[string]any {
|
||||
return ConsolidationMemoryOracle(memoryText, score, washCount, entryID, now, compress)
|
||||
}
|
||||
|
||||
// getRelevantKnowledge mirrors MemoryConsolidation.get_relevant_knowledge:
|
||||
// word overlap + tag overlap*2 + confidence*3, score>0 gate, stable
|
||||
// descending sort, limit cut.
|
||||
func getRelevantKnowledge(query string, limit int, knowledge []map[string]any) []map[string]any {
|
||||
qw := map[string]bool{}
|
||||
for _, w := range strings.Fields(strings.ToLower(query)) {
|
||||
qw[w] = true
|
||||
}
|
||||
type scored struct {
|
||||
s float64
|
||||
k map[string]any
|
||||
}
|
||||
var sc []scored
|
||||
for _, k := range knowledge {
|
||||
cw := map[string]bool{}
|
||||
content, _ := k["content"].(string)
|
||||
for _, w := range strings.Fields(strings.ToLower(content)) {
|
||||
cw[w] = true
|
||||
}
|
||||
overlap := 0
|
||||
for w := range cw {
|
||||
if qw[w] {
|
||||
overlap++
|
||||
}
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
if tags, ok := k["tags"].([]any); ok {
|
||||
for _, t := range tags {
|
||||
if s, ok := t.(string); ok {
|
||||
seen[s] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
tagOverlap := 0
|
||||
for w := range qw {
|
||||
if seen[w] {
|
||||
tagOverlap++
|
||||
}
|
||||
}
|
||||
confidence, _ := k["confidence"].(float64)
|
||||
score := float64(overlap) + float64(tagOverlap*2) + confidence*3
|
||||
if score > 0 {
|
||||
sc = append(sc, scored{score, k})
|
||||
}
|
||||
}
|
||||
sort.SliceStable(sc, func(i, j int) bool { return sc[i].s > sc[j].s })
|
||||
out := make([]map[string]any, 0)
|
||||
for i := 0; i < len(sc) && i < limit; i++ {
|
||||
out = append(out, sc[i].k)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// getRelevantLessons mirrors MemoryConsolidation.get_relevant_lessons:
|
||||
// trigger+lesson word overlap, overlap>0 gate, stable descending sort,
|
||||
// limit cut.
|
||||
func getRelevantLessons(query string, limit int, lessons []map[string]any) []map[string]any {
|
||||
qw := wordSet(query)
|
||||
type scored struct {
|
||||
n int
|
||||
l map[string]any
|
||||
}
|
||||
var sc []scored
|
||||
for _, l := range lessons {
|
||||
trigger, _ := l["trigger"].(string)
|
||||
lesson, _ := l["lesson"].(string)
|
||||
overlap := len(intersect(qw, wordSet(trigger))) + len(intersect(qw, wordSet(lesson)))
|
||||
if overlap > 0 {
|
||||
sc = append(sc, scored{overlap, l})
|
||||
}
|
||||
}
|
||||
sort.SliceStable(sc, func(i, j int) bool { return sc[i].n > sc[j].n })
|
||||
out := make([]map[string]any, 0)
|
||||
for i := 0; i < len(sc) && i < limit; i++ {
|
||||
out = append(out, sc[i].l)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// getRelevantConnections mirrors MemoryConsolidation.get_relevant_connections:
|
||||
// from_concept+to_concept word overlap, overlap>0 gate, stable sort, limit.
|
||||
func getRelevantConnections(query string, limit int, connections []map[string]any) []map[string]any {
|
||||
qw := wordSet(query)
|
||||
type scored struct {
|
||||
n int
|
||||
c map[string]any
|
||||
}
|
||||
var sc []scored
|
||||
for _, c := range connections {
|
||||
from, _ := c["from_concept"].(string)
|
||||
to, _ := c["to_concept"].(string)
|
||||
overlap := len(intersect(qw, wordSet(from))) + len(intersect(qw, wordSet(to)))
|
||||
if overlap > 0 {
|
||||
sc = append(sc, scored{overlap, c})
|
||||
}
|
||||
}
|
||||
sort.SliceStable(sc, func(i, j int) bool { return sc[i].n > sc[j].n })
|
||||
out := make([]map[string]any, 0)
|
||||
for i := 0; i < len(sc) && i < limit; i++ {
|
||||
out = append(out, sc[i].c)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// wordSet mirrors Python set(s.lower().split()) (whitespace-run split).
|
||||
func wordSet(s string) map[string]bool {
|
||||
out := map[string]bool{}
|
||||
for _, w := range strings.Fields(strings.ToLower(s)) {
|
||||
out[w] = true
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func intersect(a, b map[string]bool) map[string]bool {
|
||||
out := map[string]bool{}
|
||||
for w := range a {
|
||||
if b[w] {
|
||||
out[w] = true
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// formatKnowledgeForPrompt mirrors MemoryConsolidation.format_knowledge_for_prompt:
|
||||
// three relevance-scored sections; a section renders only when its list is
|
||||
// non-empty; knowledge items carry the [→entry_id: N] link (Python
|
||||
// truthiness: entry_id 0 renders no link).
|
||||
func formatKnowledgeForPrompt(query string, knowledge, lessons, connections []map[string]any) string {
|
||||
ks := getRelevantKnowledge(query, 5, knowledge)
|
||||
ls := getRelevantLessons(query, 3, lessons)
|
||||
cs := getRelevantConnections(query, 3, connections)
|
||||
|
||||
parts := []string{}
|
||||
if len(ks) > 0 {
|
||||
parts = append(parts, "## What I know")
|
||||
for _, k := range ks {
|
||||
link := ""
|
||||
if entryID, ok := k["entry_id"].(float64); ok && entryID != 0 {
|
||||
link = fmt.Sprintf(" [→entry_id: %d]", int(entryID))
|
||||
}
|
||||
content, _ := k["content"].(string)
|
||||
parts = append(parts, "- "+content+link)
|
||||
}
|
||||
}
|
||||
if len(ls) > 0 {
|
||||
parts = append(parts, "\n## Lessons I've learned")
|
||||
for _, l := range ls {
|
||||
lesson, _ := l["lesson"].(string)
|
||||
parts = append(parts, "- "+lesson)
|
||||
}
|
||||
}
|
||||
if len(cs) > 0 {
|
||||
parts = append(parts, "\n## Connections I understand")
|
||||
for _, c := range cs {
|
||||
from, _ := c["from_concept"].(string)
|
||||
to, _ := c["to_concept"].(string)
|
||||
rel, _ := c["relationship"].(string)
|
||||
parts = append(parts, "- "+from+" "+rel+" "+to)
|
||||
}
|
||||
}
|
||||
return strings.Join(parts, "\n")
|
||||
}
|
||||
|
||||
// getStatus mirrors MemoryConsolidation.get_status (dir = the injectable
|
||||
// seam; the oracle reports its module-level CONSOLIDATION_DIR constant).
|
||||
func getStatus(knowledge, lessons, connections []map[string]any, dir string) map[string]any {
|
||||
return map[string]any{
|
||||
"knowledge_count": len(knowledge),
|
||||
"lessons_count": len(lessons),
|
||||
"connections_count": len(connections),
|
||||
"consolidation_dir": dir,
|
||||
}
|
||||
}
|
||||
|
||||
// ── store fns (path = the injectable seam) ───────────────────────────────────
|
||||
|
||||
// LoadKnowledge mirrors MemoryConsolidation._load_knowledge: absent file →
|
||||
// empty store, else the parsed list (ConsolidatedKnowledge rows).
|
||||
func LoadKnowledge(path string) []map[string]any { return loadJSONList(path) }
|
||||
|
||||
// SaveKnowledge mirrors MemoryConsolidation._save_knowledge: indent-2 JSON,
|
||||
// no trailing newline (json.dump shape).
|
||||
func SaveKnowledge(path string, items []map[string]any) { saveJSONList(path, items) }
|
||||
|
||||
// LoadLessons mirrors MemoryConsolidation._load_lessons.
|
||||
func LoadLessons(path string) []map[string]any { return loadJSONList(path) }
|
||||
|
||||
// SaveLessons mirrors MemoryConsolidation._save_lessons.
|
||||
func SaveLessons(path string, items []map[string]any) { saveJSONList(path, items) }
|
||||
|
||||
// LoadConnections mirrors MemoryConsolidation._load_connections.
|
||||
func LoadConnections(path string) []map[string]any { return loadJSONList(path) }
|
||||
|
||||
// SaveConnections mirrors MemoryConsolidation._save_connections.
|
||||
func SaveConnections(path string, items []map[string]any) { saveJSONList(path, items) }
|
||||
|
||||
func loadJSONList(path string) []map[string]any {
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return []map[string]any{}
|
||||
}
|
||||
items := []map[string]any{}
|
||||
if err := json.Unmarshal(raw, &items); err != nil {
|
||||
return []map[string]any{}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func saveJSONList(path string, items []map[string]any) {
|
||||
data, err := json.MarshalIndent(items, "", " ")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = os.WriteFile(path, data, 0o644)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
// gaps_test.go — dichotomy gap-closing battery (contract
|
||||
// BUILDER-2026-10-13T02:44:35Z-411615-factory-gap-mc). Replays the 34-row
|
||||
// frozen oracle truth (truth_gaps.json, embedded VERBATIM) through the
|
||||
// gap fns. Truth frozen via the shared harness
|
||||
// (scripts/freeze_truth.py + scripts/mc_freeze_seams/sitecustomize.py:
|
||||
// RUNE cores pinned python, frozen clock, hermetic consolidation store)
|
||||
// and verified byte-identical against the live oracle.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGapParity(t *testing.T) {
|
||||
raw, err := os.ReadFile("truth_gaps.json")
|
||||
if err != nil {
|
||||
t.Fatalf("truth missing: %v", err)
|
||||
}
|
||||
var rows []struct {
|
||||
Fn string `json:"fn"`
|
||||
Args []any `json:"args"`
|
||||
Kwargs map[string]any `json:"kwargs"`
|
||||
Expected any `json:"expected"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &rows); err != nil {
|
||||
t.Fatalf("truth unparseable: %v", err)
|
||||
}
|
||||
dir := t.TempDir()
|
||||
for i, r := range rows {
|
||||
got, err := replayGapRow(r.Fn, r.Args, r.Kwargs, dir)
|
||||
if err != nil {
|
||||
t.Fatalf("row %d (%s): %v", i, r.Fn, err)
|
||||
}
|
||||
gj, _ := json.Marshal(got)
|
||||
var gv any
|
||||
_ = json.Unmarshal(gj, &gv)
|
||||
gs, _ := json.Marshal(gv)
|
||||
ws, _ := json.Marshal(r.Expected)
|
||||
if string(gs) != string(ws) {
|
||||
t.Errorf("row %d %s: got %s want %s", i, r.Fn, gs, ws)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestGapEntryNoneShape pins the entry_id-None id branch: Python's
|
||||
// hash() is process-randomized (not portable), so — per the established
|
||||
// truth_constructors.json fact-row convention — the shape is pinned
|
||||
// (id_prefix/entry_none) instead of the hash id; the Python tests pin the
|
||||
// live behavior (test_memory_oracle_no_entry_id).
|
||||
func TestGapEntryNoneShape(t *testing.T) {
|
||||
rec := ConsolidationMemoryOracle("short text", 0.5, 0, nil, frozenClock, compressEntry)
|
||||
if rec["id_prefix"] != "memory" || rec["entry_none"] != true {
|
||||
t.Errorf("entry-none shape: got %v", rec)
|
||||
}
|
||||
if _, has := rec["id"]; has {
|
||||
t.Errorf("entry-none must not materialize a hash id: %v", rec)
|
||||
}
|
||||
neuron := consolidateNeuron(map[string]any{}, nil, frozenClock, compressEntry)
|
||||
if neuron["id"] != "neuron-unknown" {
|
||||
t.Errorf("neuron default slug id: got %v", neuron["id"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestGapStatusShape pins get_status counts; consolidation_dir is the
|
||||
// injectable path seam (the oracle reports its module-level constant), so
|
||||
// only the shape is asserted — same seam category as the clock.
|
||||
func TestGapStatusShape(t *testing.T) {
|
||||
st := getStatus(
|
||||
[]map[string]any{{"id": "k1"}, {"id": "k2"}, {"id": "k3"}},
|
||||
[]map[string]any{{"id": "lesson-0"}},
|
||||
[]map[string]any{{"id": "conn-0"}},
|
||||
"/tmp/x/.rune/consolidation")
|
||||
if st["knowledge_count"] != 3 || st["lessons_count"] != 1 || st["connections_count"] != 1 {
|
||||
t.Errorf("status counts: got %v", st)
|
||||
}
|
||||
if st["consolidation_dir"] != "/tmp/x/.rune/consolidation" {
|
||||
t.Errorf("status dir: got %v", st["consolidation_dir"])
|
||||
}
|
||||
}
|
||||
@@ -143,7 +143,38 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
fails := 0
|
||||
replayDir, err := os.MkdirTemp("", "mc-replay-")
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer os.RemoveAll(replayDir)
|
||||
for i, c := range cases {
|
||||
// Freeze-shape rows ({"fn","args","kwargs","expected"}) replay
|
||||
// through the gap battery's shared executor; legacy rows keep the
|
||||
// hand-rolled op switch below.
|
||||
if fnRaw, ok := c["fn"].(string); ok {
|
||||
args, _ := c["args"].([]any)
|
||||
kwargs, _ := c["kwargs"].(map[string]any)
|
||||
got, err := replayGapRow(fnRaw, args, kwargs, replayDir)
|
||||
if err != nil {
|
||||
fails++
|
||||
fmt.Printf("case %d %s: %v\n", i, fnRaw, err)
|
||||
continue
|
||||
}
|
||||
gj, _ := json.Marshal(got)
|
||||
var gv, wv any
|
||||
_ = json.Unmarshal(gj, &gv)
|
||||
wj, _ := json.Marshal(c["expected"])
|
||||
_ = json.Unmarshal(wj, &wv)
|
||||
gs, _ := json.Marshal(gv)
|
||||
ws, _ := json.Marshal(wv)
|
||||
if string(gs) != string(ws) {
|
||||
fails++
|
||||
fmt.Printf("case %d %s: got %s want %s\n", i, fnRaw, gs, ws)
|
||||
}
|
||||
continue
|
||||
}
|
||||
op, _ := c["op"].(string)
|
||||
switch op {
|
||||
case "score_knowledge":
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
// replay_gaps.go — shared executor for the frozen gap truth ({"fn",
|
||||
// "args","kwargs","expected"} rows): used by both the go test battery and
|
||||
// the `replay` binary gate. Store rows run against dir (the hermetic
|
||||
// consolidation dir stand-in, mirroring MC_FREEZE_HOME in the freeze rig).
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// ErrUnknownFn marks a truth row the replay cannot execute.
|
||||
type ErrUnknownFn string
|
||||
|
||||
func (e ErrUnknownFn) Error() string { return "unknown fn: " + string(e) }
|
||||
|
||||
func replayGapRow(fn string, args []any, kwargs map[string]any, dir string) (any, error) {
|
||||
kwInt := func(key string, def int) int {
|
||||
if v, ok := kwargs[key].(float64); ok {
|
||||
return int(v)
|
||||
}
|
||||
return def
|
||||
}
|
||||
kwIntPtr := func(key string) *int {
|
||||
if v, ok := kwargs[key].(float64); ok {
|
||||
n := int(v)
|
||||
return &n
|
||||
}
|
||||
return nil
|
||||
}
|
||||
kwStrings := func(key string) []string {
|
||||
arr, ok := kwargs[key].([]any)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, len(arr))
|
||||
for _, v := range arr {
|
||||
if s, ok := v.(string); ok {
|
||||
out = append(out, s)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
argStr := func(i int) string { s, _ := args[i].(string); return s }
|
||||
argFloat := func(i int) float64 { f, _ := args[i].(float64); return f }
|
||||
toItems := func(v any) []map[string]any {
|
||||
arr, _ := v.([]any)
|
||||
items := make([]map[string]any, 0, len(arr))
|
||||
for _, e := range arr {
|
||||
if m, ok := e.(map[string]any); ok {
|
||||
items = append(items, m)
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
seeded := func(name string, seed any) string {
|
||||
path := filepath.Join(dir, name)
|
||||
if seed == nil {
|
||||
_ = os.Remove(path)
|
||||
return path
|
||||
}
|
||||
SaveKnowledge(path, toItems(seed)) // indent-2 JSON, same dump shape
|
||||
return path
|
||||
}
|
||||
|
||||
switch fn {
|
||||
case "_core":
|
||||
return nil, nil
|
||||
case "_consolidation_now":
|
||||
return ConsolidationNow(frozenClock), nil
|
||||
case "_consolidation_memory_oracle":
|
||||
return ConsolidationMemoryOracle(argStr(0), argFloat(1), kwInt("wash_count", 0),
|
||||
kwIntPtr("entry_id"), frozenClock, compressEntry), nil
|
||||
case "_consolidation_memory_oracle_no_drift":
|
||||
return ConsolidationMemoryOracle(argStr(0), argFloat(1), kwInt("wash_count", 0),
|
||||
kwIntPtr("entry_id"), frozenClock, nil), nil
|
||||
case "_consolidation_neuron_oracle":
|
||||
return ConsolidationNeuronOracle(argStr(0), argStr(1), argFloat(2),
|
||||
kwStrings("triggers"), kwInt("wash_count", 0), kwIntPtr("entry_id"),
|
||||
frozenClock, compressEntry), nil
|
||||
case "_consolidation_neuron_oracle_no_drift":
|
||||
return ConsolidationNeuronOracle(argStr(0), argStr(1), argFloat(2),
|
||||
kwStrings("triggers"), kwInt("wash_count", 0), kwIntPtr("entry_id"),
|
||||
frozenClock, nil), nil
|
||||
case "consolidate_neuron":
|
||||
neuron, _ := args[0].(map[string]any)
|
||||
return consolidateNeuron(neuron, nil, frozenClock, compressEntry), nil
|
||||
case "consolidate_memory":
|
||||
return consolidateMemory(argStr(0), argFloat(1), kwInt("wash_count", 0),
|
||||
kwIntPtr("entry_id"), frozenClock, compressEntry), nil
|
||||
case "get_relevant_knowledge":
|
||||
limit := 5
|
||||
if f, ok := args[1].(float64); ok {
|
||||
limit = int(f)
|
||||
}
|
||||
return getRelevantKnowledge(argStr(0), limit, toItems(kwargs["knowledge"])), nil
|
||||
case "get_relevant_lessons":
|
||||
limit := 3
|
||||
if f, ok := args[1].(float64); ok {
|
||||
limit = int(f)
|
||||
}
|
||||
return getRelevantLessons(argStr(0), limit, toItems(kwargs["lessons"])), nil
|
||||
case "get_relevant_connections":
|
||||
limit := 3
|
||||
if f, ok := args[1].(float64); ok {
|
||||
limit = int(f)
|
||||
}
|
||||
return getRelevantConnections(argStr(0), limit, toItems(kwargs["connections"])), nil
|
||||
case "format_knowledge_for_prompt":
|
||||
return formatKnowledgeForPrompt(argStr(0), toItems(kwargs["knowledge"]),
|
||||
toItems(kwargs["lessons"]), toItems(kwargs["connections"])), nil
|
||||
case "load_knowledge":
|
||||
return LoadKnowledge(seeded("knowledge.json", args[0])), nil
|
||||
case "save_knowledge":
|
||||
path := filepath.Join(dir, "knowledge.json")
|
||||
SaveKnowledge(path, toItems(args[0]))
|
||||
return LoadKnowledge(path), nil
|
||||
case "load_lessons":
|
||||
return LoadLessons(seeded("lessons.json", args[0])), nil
|
||||
case "save_lessons":
|
||||
path := filepath.Join(dir, "lessons.json")
|
||||
SaveLessons(path, toItems(args[0]))
|
||||
return LoadLessons(path), nil
|
||||
case "load_connections":
|
||||
return LoadConnections(seeded("connections.json", args[0])), nil
|
||||
case "save_connections":
|
||||
path := filepath.Join(dir, "connections.json")
|
||||
SaveConnections(path, toItems(args[0]))
|
||||
return LoadConnections(path), nil
|
||||
}
|
||||
return nil, ErrUnknownFn(fn)
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user