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

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:
Nick committed 2026-10-13 06:13:25 +01:00
1 parent 0703d0d17b
commit 4825d17cc7
7 files changed
+2564

No files matched your search

+159
View File
@@ -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}")
+195
View File
@@ -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
+457
View File
@@ -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)
}
+82
View File
@@ -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"])
}
}
+31
View File
@@ -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":
+132
View File
@@ -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