Summary
Add the core memory storage engine that handles Markdown file I/O, SQLite indexing, text chunking, and hybrid search (vector + BM25).
Details
New dependency:
modernc.org/sqlite — pure Go SQLite driver (no CGo, preserves clean cross-compilation)
New files:
internal/memory/store.go
internal/memory/store_test.go
Key types:
type EmbedFunc func(ctx context.Context, text string) ([]float64, error)
type Store struct {
db *sql.DB
dir string // ~/.config/ghost/memory
embedFn EmbedFunc
mu sync.RWMutex
}
type SearchResult struct {
Source string // e.g. "daily/2026-02-07.md" or "MEMORY.md"
Content string
Score float64
}
SQLite schema:
chunks table: id, source, chunk_index, content, embedding (BLOB)
chunks_fts FTS5 virtual table with sync triggers
- WAL mode + busy_timeout for concurrency
Functions:
NewStore(dir, embedFn) — create directory structure, open DB, create schema
SaveToDaily(ctx, content) — append to daily/YYYY-MM-DD.md, index new content
SaveToLongTerm(ctx, content) — append to MEMORY.md, index new content
IndexFile(ctx, source) — chunk file, embed chunks, upsert into SQLite
Search(ctx, query, topK) — hybrid retrieval (70% cosine similarity + 30% BM25)
chunkText(text, maxChars, overlapChars) — paragraph-aware chunking (~400 tokens, 80 overlap)
cosineSimilarity(a, b []float64) — vector math helper
Close() — close SQLite connection
File structure on disk:
~/.config/ghost/memory/
MEMORY.md
daily/
2026-02-07.md
index.db
Design decisions:
EmbedFunc injection (not an interface) for testability
- Brute-force vector search (corpus is small enough, no ANN needed)
- Markdown files are source of truth; SQLite index is derived and rebuildable
- Embedding vectors serialized as binary (compact, fast)
Testing:
- Tests use temp dirs + mock
EmbedFunc returning deterministic vectors
TestChunkText, TestCosineSimilarity, TestNewStore, TestSaveToDaily, TestSaveToLongTerm, TestSearch, TestIndexFile
Dependencies
Acceptance Criteria
Summary
Add the core memory storage engine that handles Markdown file I/O, SQLite indexing, text chunking, and hybrid search (vector + BM25).
Details
New dependency:
modernc.org/sqlite— pure Go SQLite driver (no CGo, preserves clean cross-compilation)New files:
internal/memory/store.gointernal/memory/store_test.goKey types:
SQLite schema:
chunkstable: id, source, chunk_index, content, embedding (BLOB)chunks_ftsFTS5 virtual table with sync triggersFunctions:
NewStore(dir, embedFn)— create directory structure, open DB, create schemaSaveToDaily(ctx, content)— append todaily/YYYY-MM-DD.md, index new contentSaveToLongTerm(ctx, content)— append toMEMORY.md, index new contentIndexFile(ctx, source)— chunk file, embed chunks, upsert into SQLiteSearch(ctx, query, topK)— hybrid retrieval (70% cosine similarity + 30% BM25)chunkText(text, maxChars, overlapChars)— paragraph-aware chunking (~400 tokens, 80 overlap)cosineSimilarity(a, b []float64)— vector math helperClose()— close SQLite connectionFile structure on disk:
Design decisions:
EmbedFuncinjection (not an interface) for testabilityTesting:
EmbedFuncreturning deterministic vectorsTestChunkText,TestCosineSimilarity,TestNewStore,TestSaveToDaily,TestSaveToLongTerm,TestSearch,TestIndexFileDependencies
EmbedFunctype contract, though tests mock it)Acceptance Criteria
NewStore()SaveToDailycreates/appends daily Markdown filesSaveToLongTermcreates/appends MEMORY.mdIndexFilechunks text and stores embeddingsSearchreturns relevant results with hybrid scoring