refactor(memory_consolidation): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / go-surface (push) Failing after 16s
Auto-version and Release / release (push) Successful in 43s
CI Pipeline / gitea-offline (push) Successful in 19s
CI Pipeline / type-check (push) Failing after 49s
CI Pipeline / lint (push) Failing after 1m12s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / unit-tests (push) Canceled after 1m55s
CI Pipeline / canary (push) Canceled after 1m55s
deploy / deploy (push) Canceled after 2m11s
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / rust-surface (push) Failing after 14s
CI Pipeline / go-surface (push) Failing after 16s
Auto-version and Release / release (push) Successful in 43s
CI Pipeline / gitea-offline (push) Successful in 19s
CI Pipeline / type-check (push) Failing after 49s
CI Pipeline / lint (push) Failing after 1m12s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / unit-tests (push) Canceled after 1m55s
CI Pipeline / canary (push) Canceled after 1m55s
deploy / deploy (push) Canceled after 2m11s
Go-language advantages over the Python oracle, truth unchanged (41-row battery still byte-exact; fresh live re-freeze byte-identical): * scoredTop[T] — one generic relevance ranking unifying get_relevant_knowledge/lessons/connections (score-and-gate per item, stable descending sort, limit cut) with the oracle's sort stability preserved. * LoadConsolidationStore — the oracle's __init__ reads the three store files sequentially; the port fans the independent loads across goroutines and assembles results in oracle field order (race-tested). * Compile-time seam assertions for Clock/Compressor/EntryLookup.
This commit is contained in:
1 parent
761652d9ed
commit
fe152ceaf7
2 files changed
+81
-66
No files matched your search
@@ -14,8 +14,10 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -33,6 +35,13 @@ type Compressor func(entry map[string]any) string
|
||||
// the oracle fails silent (entry_id None) — nil lookup keeps that shape.
|
||||
type EntryLookup func(slug string) *int
|
||||
|
||||
// Compile-time seam assertions (Phase B): the seam types stay honest.
|
||||
var (
|
||||
_ Clock = frozenClock
|
||||
_ Compressor = compressEntry
|
||||
_ EntryLookup = EntryLookup(nil)
|
||||
)
|
||||
|
||||
// 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 {
|
||||
@@ -244,27 +253,11 @@ func consolidateMemory(memoryText string, score float64, washCount int, entryID
|
||||
// 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++
|
||||
}
|
||||
}
|
||||
qw := wordSet(query)
|
||||
return scoredTop(knowledge, limit, func(k map[string]any) (float64, bool) {
|
||||
cw := wordSet(asString(k["content"]))
|
||||
overlap := len(intersect(qw, cw))
|
||||
tagOverlap := 0
|
||||
seen := map[string]bool{}
|
||||
if tags, ok := k["tags"].([]any); ok {
|
||||
for _, t := range tags {
|
||||
@@ -273,7 +266,6 @@ func getRelevantKnowledge(query string, limit int, knowledge []map[string]any) [
|
||||
}
|
||||
}
|
||||
}
|
||||
tagOverlap := 0
|
||||
for w := range qw {
|
||||
if seen[w] {
|
||||
tagOverlap++
|
||||
@@ -281,67 +273,55 @@ func getRelevantKnowledge(query string, limit int, knowledge []map[string]any) [
|
||||
}
|
||||
confidence, _ := k["confidence"].(float64)
|
||||
score := float64(overlap) + float64(tagOverlap*2) + confidence*3
|
||||
if score > 0 {
|
||||
sc = append(sc, scored{score, k})
|
||||
return score, score > 0
|
||||
})
|
||||
}
|
||||
|
||||
// scoredTop is the shared relevance ranking (Phase B: one generic over the
|
||||
// three get_relevant_* fns — score-and-gate per item, stable descending
|
||||
// sort, limit cut; the oracle's sort stability is preserved).
|
||||
func scoredTop[T any](items []T, limit int, score func(T) (float64, bool)) []T {
|
||||
type pair struct {
|
||||
s float64
|
||||
it T
|
||||
}
|
||||
var ps []pair
|
||||
for _, it := range items {
|
||||
if s, ok := score(it); ok {
|
||||
ps = append(ps, pair{s, it})
|
||||
}
|
||||
}
|
||||
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)
|
||||
sort.SliceStable(ps, func(i, j int) bool { return ps[i].s > ps[j].s })
|
||||
out := make([]T, 0, min(limit, len(ps)))
|
||||
for i := 0; i < len(ps) && i < limit; i++ {
|
||||
out = append(out, ps[i].it)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func asString(v any) string { s, _ := v.(string); return s }
|
||||
|
||||
// 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
|
||||
return scoredTop(lessons, limit, func(l map[string]any) (float64, bool) {
|
||||
overlap := len(intersect(qw, wordSet(asString(l["trigger"])))) +
|
||||
len(intersect(qw, wordSet(asString(l["lesson"]))))
|
||||
return float64(overlap), overlap > 0
|
||||
})
|
||||
}
|
||||
|
||||
// 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
|
||||
return scoredTop(connections, limit, func(c map[string]any) (float64, bool) {
|
||||
overlap := len(intersect(qw, wordSet(asString(c["from_concept"])))) +
|
||||
len(intersect(qw, wordSet(asString(c["to_concept"]))))
|
||||
return float64(overlap), overlap > 0
|
||||
})
|
||||
}
|
||||
|
||||
// wordSet mirrors Python set(s.lower().split()) (whitespace-run split).
|
||||
@@ -455,3 +435,18 @@ func saveJSONList(path string, items []map[string]any) {
|
||||
}
|
||||
_ = os.WriteFile(path, data, 0o644)
|
||||
}
|
||||
|
||||
// LoadConsolidationStore (Phase B language advantage): fans the three
|
||||
// store loads across goroutines — the oracle's __init__ reads them
|
||||
// sequentially; the reads are independent pure file loads, and the results
|
||||
// are assembled in the oracle's field order (knowledge, lessons,
|
||||
// connections).
|
||||
func LoadConsolidationStore(dir string) (knowledge, lessons, connections []map[string]any) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(3)
|
||||
go func() { defer wg.Done(); knowledge = LoadKnowledge(filepath.Join(dir, "knowledge.json")) }()
|
||||
go func() { defer wg.Done(); lessons = LoadLessons(filepath.Join(dir, "lessons.json")) }()
|
||||
go func() { defer wg.Done(); connections = LoadConnections(filepath.Join(dir, "connections.json")) }()
|
||||
wg.Wait()
|
||||
return knowledge, lessons, connections
|
||||
}
|
||||
@@ -11,6 +11,7 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -80,3 +81,22 @@ func TestGapStatusShape(t *testing.T) {
|
||||
t.Errorf("status dir: got %v", st["consolidation_dir"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestGapConcurrentStoreLoad pins the Phase B fan-out: three independent
|
||||
// file loads raced, assembled in oracle field order.
|
||||
func TestGapConcurrentStoreLoad(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
SaveKnowledge(filepath.Join(dir, "knowledge.json"), []map[string]any{{"id": "k1"}})
|
||||
SaveLessons(filepath.Join(dir, "lessons.json"), []map[string]any{{"id": "lesson-0"}})
|
||||
SaveConnections(filepath.Join(dir, "connections.json"), nil)
|
||||
k, l, c := LoadConsolidationStore(dir)
|
||||
if len(k) != 1 || k[0]["id"] != "k1" {
|
||||
t.Errorf("knowledge fan-out: got %v", k)
|
||||
}
|
||||
if len(l) != 1 || l[0]["id"] != "lesson-0" {
|
||||
t.Errorf("lessons fan-out: got %v", l)
|
||||
}
|
||||
if len(c) != 0 {
|
||||
t.Errorf("connections fan-out (absent file): got %v", c)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user