The architecture as it stands
- The store (
internal/store/memory.go): event-sourced; the events table is truth, memories and memories_fts are materialized views refreshed in the same write transaction, a forget is a tombstone and nothing is ever deleted. Five kinds (fact, preference, decision, correction, project_state), three scopes (user, project, env), caps of 80 title / 512 text / 8 tags, and use/miss ranking counters carried by memory_ranking snapshots so a Rebuild does not zero the ranking.
- Retrieval (
Store.MemoryCandidates, internal/store/memory.go:672): SQL fusion of three arms, lexical bm25 over memories_fts, important (use_count - miss_count), recency (updated_seq); the top eight titles go to the router (internal/reflex), which picks the two or three that render into the <memory> block.
- The per-turn path (
internal/session/memory.go): shortlist before the turn, extractor after it (worth carrying, and which shown lines bore on the answer), decider (add / refine / replace / skip), and the remember tool. Every call fails open; a nil Config.Memory is memory off.
- Consolidation (
internal/session/memory_consolidate.go): one call while nobody is here, over the fifty most recently touched lines, merging duplicates and retiring superseded ones.
The verdict
This architecture is buggy. The seams are known: the router sees eight titles and never bodies, MemoryCandidates takes no scope so scope is validated on write and ignored on read, consolidation never reaches past the newest fifty lines, and a fresh memory with zero counters cannot earn the showing that would let it prove itself. This issue records that state; it fixes none of them by itself.
The rule for every improvement: TDD
No change to this architecture lands without a failing test written first:
- each seam gets a test that reproduces its bug and is red on the current code before any fix exists
- the fix is the change that turns that test green, and the full store and session suites stay green with it
- the store's kept laws (tombstones, supersede atomicity, ranking snapshots surviving a Rebuild, fail-open on every reflex call) are pinned by tests before anything touches this path
—
Drafted with CodeAF
The architecture as it stands
internal/store/memory.go): event-sourced; the events table is truth,memoriesandmemories_ftsare materialized views refreshed in the same write transaction, a forget is a tombstone and nothing is ever deleted. Five kinds (fact, preference, decision, correction, project_state), three scopes (user, project, env), caps of 80 title / 512 text / 8 tags, and use/miss ranking counters carried bymemory_rankingsnapshots so a Rebuild does not zero the ranking.Store.MemoryCandidates,internal/store/memory.go:672): SQL fusion of three arms, lexical bm25 overmemories_fts, important (use_count - miss_count), recency (updated_seq); the top eight titles go to the router (internal/reflex), which picks the two or three that render into the<memory>block.internal/session/memory.go): shortlist before the turn, extractor after it (worth carrying, and which shown lines bore on the answer), decider (add / refine / replace / skip), and theremembertool. Every call fails open; a nilConfig.Memoryis memory off.internal/session/memory_consolidate.go): one call while nobody is here, over the fifty most recently touched lines, merging duplicates and retiring superseded ones.The verdict
This architecture is buggy. The seams are known: the router sees eight titles and never bodies,
MemoryCandidatestakes no scope so scope is validated on write and ignored on read, consolidation never reaches past the newest fifty lines, and a fresh memory with zero counters cannot earn the showing that would let it prove itself. This issue records that state; it fixes none of them by itself.The rule for every improvement: TDD
No change to this architecture lands without a failing test written first:
—
Drafted with CodeAF