Documentation
¶
Overview ¶
Package storage is yaad's persistence layer: a CGO-free SQLite store with FTS5 search, prepared-statement caching, and busy-retry, plus the Storage interface the rest of the system depends on.
Index ¶
- Variables
- func ComputeSchemaFingerprint(chunkerVersion, embeddingModel string) string
- func ComputeSchemaVersion(chunkerVersion, modelName string) string
- func DecodeVector(b []byte) []float32
- func EncodeVector(v []float32) []byte
- func FormatPredictions(predictions []Behavior) string
- func IsAcyclic(edgeType string) bool
- type Behavior
- type BehaviorTracker
- type CodeChunkRecord
- type DoctorStatsResult
- type Edge
- type MockStorage
- func (m *MockStorage) AddFileWatch(_ context.Context, filePath, nodeID, gitHash string) error
- func (m *MockStorage) AddReplayEvent(_ context.Context, sessionID, data string) error
- func (m *MockStorage) AllEmbeddings(_ context.Context, model string) (map[string][]float32, error)
- func (m *MockStorage) CheckCycle(_ context.Context, fromID, toID string) (bool, error)
- func (m *MockStorage) Close() error
- func (m *MockStorage) CountAllEdges(_ context.Context) (int, error)
- func (m *MockStorage) CountEdges(_ context.Context, nodeID string) (int, int, error)
- func (m *MockStorage) CountEdgesBatch(_ context.Context, nodeIDs []string) (map[string][2]int, error)
- func (m *MockStorage) CreateEdge(_ context.Context, e *Edge) error
- func (m *MockStorage) CreateNode(_ context.Context, n *Node) error
- func (m *MockStorage) CreateSession(_ context.Context, sess *Session) error
- func (m *MockStorage) DeleteEdge(_ context.Context, id string) error
- func (m *MockStorage) DeleteEmbedding(_ context.Context, nodeID string) error
- func (m *MockStorage) DeleteNode(_ context.Context, id string) error
- func (m *MockStorage) DoctorStats(_ context.Context) (DoctorStatsResult, error)
- func (m *MockStorage) EdgeCount() int
- func (m *MockStorage) EndSession(_ context.Context, id string, summary string) error
- func (m *MockStorage) FlushAccessLog(_ context.Context) (int, error)
- func (m *MockStorage) GetAllEdgesFor(_ context.Context, nodeIDs []string) ([]*Edge, error)
- func (m *MockStorage) GetAllSignatures(_ context.Context) (map[string]string, error)
- func (m *MockStorage) GetEdge(_ context.Context, id string) (*Edge, error)
- func (m *MockStorage) GetEdgesBetween(_ context.Context, nodeIDs []string) ([]*Edge, error)
- func (m *MockStorage) GetEdgesFrom(_ context.Context, nodeID string) ([]*Edge, error)
- func (m *MockStorage) GetEdgesTo(_ context.Context, nodeID string) ([]*Edge, error)
- func (m *MockStorage) GetEmbedding(_ context.Context, nodeID string) ([]float32, string, error)
- func (m *MockStorage) GetEmbeddingsBatch(_ context.Context, model string, offset, limit int) (map[string][]float32, error)
- func (m *MockStorage) GetNeighbors(_ context.Context, nodeID string) ([]*Node, error)
- func (m *MockStorage) GetNode(_ context.Context, id string) (*Node, error)
- func (m *MockStorage) GetNodeByKey(_ context.Context, key, project string) (*Node, error)
- func (m *MockStorage) GetNodesBatch(_ context.Context, ids []string) ([]*Node, error)
- func (m *MockStorage) GetReplayEvents(_ context.Context, sessionID string) ([]*ReplayEvent, error)
- func (m *MockStorage) GetValidEdgesFrom(_ context.Context, nodeID string, at time.Time) ([]*Edge, error)
- func (m *MockStorage) GetValidEdgesTo(_ context.Context, nodeID string, at time.Time) ([]*Edge, error)
- func (m *MockStorage) GetVersions(_ context.Context, nodeID string) ([]*NodeVersion, error)
- func (m *MockStorage) InjectEdges(edges ...*Edge)
- func (m *MockStorage) InjectNodes(nodes ...*Node)
- func (m *MockStorage) InvalidateEdge(_ context.Context, id string) error
- func (m *MockStorage) LastUpdated(_ context.Context) (time.Time, error)
- func (m *MockStorage) ListNodes(_ context.Context, f NodeFilter) ([]*Node, error)
- func (m *MockStorage) ListSessions(_ context.Context, project string, limit int) ([]*Session, error)
- func (m *MockStorage) LoadNodeMetadata(_ context.Context, nodeIDs []string) (map[string]map[string]string, error)
- func (m *MockStorage) LogAccess(_ context.Context, nodeID string) error
- func (m *MockStorage) NodeCount() int
- func (m *MockStorage) NodeStats(_ context.Context) (map[string]int, int, error)
- func (m *MockStorage) SaveEmbedding(_ context.Context, nodeID, model string, vector []float32) error
- func (m *MockStorage) SaveNodeMetadata(_ context.Context, nodeID string, meta map[string]string) error
- func (m *MockStorage) SaveSignature(_ context.Context, nodeID, signature string) error
- func (m *MockStorage) SaveVersion(_ context.Context, nodeID string, content, changedBy, reason string) error
- func (m *MockStorage) SearchNodeByHash(_ context.Context, hash, scope, project string) (*Node, error)
- func (m *MockStorage) SearchNodes(_ context.Context, query string, limit int) ([]*Node, error)
- func (m *MockStorage) SetError(err error)
- func (m *MockStorage) String() string
- func (m *MockStorage) TopConnected(_ context.Context, limit int) ([]string, error)
- func (m *MockStorage) UpdateNode(_ context.Context, n *Node) error
- func (m *MockStorage) UpdateNodeContent(_ context.Context, id, newContent string) error
- func (m *MockStorage) WithTx(_ context.Context, fn func(Storage) error) error
- type Node
- type NodeFilter
- type NodeVersion
- type ReplayEvent
- type Session
- type Storage
- type Store
- func (s *Store) AddFileWatch(ctx context.Context, filePath, nodeID, gitHash string) error
- func (s *Store) AddReplayEvent(ctx context.Context, sessionID, data string) error
- func (s *Store) AllEmbeddings(ctx context.Context, model string) (map[string][]float32, error)
- func (s *Store) CheckCycle(ctx context.Context, fromID, toID string) (bool, error)
- func (s *Store) Close() error
- func (s *Store) CountAllEdges(ctx context.Context) (int, error)
- func (s *Store) CountEdges(ctx context.Context, nodeID string) (inbound int, outbound int, err error)
- func (s *Store) CountEdgesBatch(ctx context.Context, nodeIDs []string) (map[string][2]int, error)
- func (s *Store) CreateCodeIndex(ctx context.Context) error
- func (s *Store) CreateEdge(ctx context.Context, e *Edge) error
- func (s *Store) CreateNode(ctx context.Context, n *Node) error
- func (s *Store) CreateSession(ctx context.Context, sess *Session) error
- func (s *Store) DB() *sql.DB
- func (s *Store) DeleteChunksByPath(ctx context.Context, path string) error
- func (s *Store) DeleteEdge(ctx context.Context, id string) error
- func (s *Store) DeleteEmbedding(ctx context.Context, nodeID string) error
- func (s *Store) DeleteNode(ctx context.Context, id string) error
- func (s *Store) DiffVersions(ctx context.Context, nodeID string, v1, v2 int) (content1, content2 string, err error)
- func (s *Store) DoctorStats(ctx context.Context) (DoctorStatsResult, error)
- func (s *Store) EndSession(ctx context.Context, id string, summary string) error
- func (s *Store) FillNodeMetadata(ctx context.Context, nodes []*Node) error
- func (s *Store) FindByPrefix(ctx context.Context, prefix string) ([]*Node, error)
- func (s *Store) FlushAccessLog(ctx context.Context) (int, error)
- func (s *Store) GetAllEdgesFor(ctx context.Context, nodeIDs []string) ([]*Edge, error)
- func (s *Store) GetAllSignatures(ctx context.Context) (map[string]string, error)
- func (s *Store) GetEdge(ctx context.Context, id string) (*Edge, error)
- func (s *Store) GetEdgesBetween(ctx context.Context, nodeIDs []string) ([]*Edge, error)
- func (s *Store) GetEdgesFrom(ctx context.Context, nodeID string) ([]*Edge, error)
- func (s *Store) GetEdgesTo(ctx context.Context, nodeID string) ([]*Edge, error)
- func (s *Store) GetEmbedding(ctx context.Context, nodeID string) ([]float32, string, error)
- func (s *Store) GetEmbeddingsBatch(ctx context.Context, model string, offset, limit int) (map[string][]float32, error)
- func (s *Store) GetFileHash(ctx context.Context, path string) (string, error)
- func (s *Store) GetNeighbors(ctx context.Context, nodeID string) ([]*Node, error)
- func (s *Store) GetNode(ctx context.Context, id string) (*Node, error)
- func (s *Store) GetNodeByKey(ctx context.Context, key, project string) (*Node, error)
- func (s *Store) GetNodesBatch(ctx context.Context, ids []string) ([]*Node, error)
- func (s *Store) GetNodesByFile(ctx context.Context, filePath string) ([]*Node, error)
- func (s *Store) GetReplayEvents(ctx context.Context, sessionID string) ([]*ReplayEvent, error)
- func (s *Store) GetValidEdgesFrom(ctx context.Context, nodeID string, at time.Time) ([]*Edge, error)
- func (s *Store) GetValidEdgesTo(ctx context.Context, nodeID string, at time.Time) ([]*Edge, error)
- func (s *Store) GetVersions(ctx context.Context, nodeID string) ([]*NodeVersion, error)
- func (s *Store) InvalidateEdge(ctx context.Context, id string) error
- func (s *Store) InvalidateStaleChunks(ctx context.Context, currentVersion string) (int, error)
- func (s *Store) LastUpdated(ctx context.Context) (time.Time, error)
- func (s *Store) ListIndexedPaths(ctx context.Context) ([]string, error)
- func (s *Store) ListNodes(ctx context.Context, f NodeFilter) ([]*Node, error)
- func (s *Store) ListSessions(ctx context.Context, project string, limit int) ([]*Session, error)
- func (s *Store) LoadNodeMetadata(ctx context.Context, nodeIDs []string) (map[string]map[string]string, error)
- func (s *Store) LogAccess(ctx context.Context, nodeID string) error
- func (s *Store) NodeStats(ctx context.Context) (map[string]int, int, error)
- func (s *Store) RefreshCodeIndex(ctx context.Context, paths []string, hashFn func(string) string) (int, error)
- func (s *Store) RollbackToVersion(ctx context.Context, nodeID string, version int) error
- func (s *Store) SaveEmbedding(ctx context.Context, nodeID, model string, vector []float32) error
- func (s *Store) SaveNodeMetadata(ctx context.Context, nodeID string, meta map[string]string) error
- func (s *Store) SaveSignature(ctx context.Context, nodeID, signature string) error
- func (s *Store) SaveVersion(ctx context.Context, nodeID string, content, changedBy, reason string) error
- func (s *Store) SearchCodeChunksByLanguage(ctx context.Context, query string, languages []string, limit int) ([]*CodeChunkRecord, error)
- func (s *Store) SearchCodeChunksFTS(ctx context.Context, query string, limit int) ([]*CodeChunkRecord, error)
- func (s *Store) SearchCodeChunksHybrid(ctx context.Context, query string, queryVec []float32, limit int, ...) ([]*CodeChunkRecord, error)
- func (s *Store) SearchNodeByHash(ctx context.Context, hash, scope, project string) (*Node, error)
- func (s *Store) SearchNodes(ctx context.Context, query string, limit int) ([]*Node, error)
- func (s *Store) StoreVector(ctx context.Context, chunkID string, vec []float32) error
- func (s *Store) TopConnected(ctx context.Context, limit int) ([]string, error)
- func (s *Store) UpdateNode(ctx context.Context, n *Node) error
- func (s *Store) UpdateNodeContent(ctx context.Context, id, newContent string) error
- func (s *Store) UpsertByTopic(ctx context.Context, n *Node, topicKey string) (*Node, bool, error)
- func (s *Store) UpsertCodeChunk(ctx context.Context, chunk *CodeChunkRecord) error
- func (s *Store) WithTx(ctx context.Context, fn func(Storage) error) error
Constants ¶
This section is empty.
Variables ¶
var ( ErrNodeNotFound = errors.New("node not found") ErrEdgeNotFound = errors.New("edge not found") ErrSessionNotFound = errors.New("session not found") ErrCycleDetected = errors.New("cycle detected") ErrDuplicateNode = errors.New("duplicate node") ErrDuplicateEdge = errors.New("duplicate edge") ErrInvalidNodeType = errors.New("invalid node type") ErrInvalidEdgeType = errors.New("invalid edge type") ErrContentTooLong = errors.New("content exceeds maximum length") ErrEmptyContent = errors.New("content cannot be empty") ErrDatabaseLocked = errors.New("database is locked") ErrBatchTooLarge = errors.New("batch size exceeds database limit") ErrVersionNotFound = errors.New("version not found") ErrEmbeddingNotFound = errors.New("embedding not found") )
Domain-specific errors for the storage layer. Callers should use errors.Is() to check for specific error conditions.
Error return conventions:
- GetNode, GetEdge: returns the sentinel error wrapped with the ID when not found
- SearchNodeByHash, GetNodeByKey: returns (nil, nil) when not found (dedup/upsert pattern)
Functions ¶
func ComputeSchemaFingerprint ¶
ComputeSchemaFingerprint returns a hex-encoded 128-bit hash of the chunker version and embedding model, suitable for invalidating all chunks when the indexing pipeline changes. Uses SHA-256 truncated to 16 bytes.
func ComputeSchemaVersion ¶
ComputeSchemaVersion produces a version string from the chunker version and embedding model name. It is the first 16 hex chars of a SHA-256 hash.
func DecodeVector ¶
DecodeVector decodes a byte slice from SQLite BLOB to a float32 slice.
func EncodeVector ¶
EncodeVector encodes a float32 slice to a byte slice for SQLite BLOB storage.
func FormatPredictions ¶
FormatPredictions formats a list of predicted behaviors into a readable string.
Types ¶
type Behavior ¶
type Behavior struct {
ID string
Pattern string // what the user tends to do
Frequency int // how many times observed
LastSeen time.Time // last time this behavior occurred
Context string // when/where this happens
Confidence float64 // how confident we are this is a real pattern
}
Behavior represents a tracked user pattern or habit.
type BehaviorTracker ¶
type BehaviorTracker struct {
// contains filtered or unexported fields
}
BehaviorTracker tracks user behaviors and predicts future actions.
func NewBehaviorTracker ¶
func NewBehaviorTracker() *BehaviorTracker
NewBehaviorTracker creates a new BehaviorTracker.
func (*BehaviorTracker) GetBehaviors ¶
func (bt *BehaviorTracker) GetBehaviors() []Behavior
GetBehaviors returns a copy of all tracked behaviors.
func (*BehaviorTracker) GetTopPatterns ¶
func (bt *BehaviorTracker) GetTopPatterns(limit int) []Behavior
GetTopPatterns returns the most frequent behaviors across all contexts, limited to the given count.
func (*BehaviorTracker) Predict ¶
func (bt *BehaviorTracker) Predict(context string) []Behavior
Predict returns behaviors that match the given context, ranked by a score combining frequency and recency.
func (*BehaviorTracker) Track ¶
func (bt *BehaviorTracker) Track(action, context string)
Track records a user action within a given context. If a matching pattern already exists for the same context, its frequency is incremented and LastSeen is updated. Otherwise a new Behavior entry is created.
type CodeChunkRecord ¶
type CodeChunkRecord struct {
ID string `json:"id"`
Path string `json:"path"`
StartLine int `json:"start_line"`
EndLine int `json:"end_line"`
Content string `json:"content"`
Symbol string `json:"symbol"`
Language string `json:"language"`
Tokens int `json:"tokens"`
FileHash string `json:"file_hash"`
SchemaVersion string `json:"schema_version"`
Vector []byte `json:"vector,omitempty"` // embedding stored as BLOB
}
CodeChunkRecord represents a stored code chunk in the index.
type DoctorStatsResult ¶
type DoctorStatsResult struct {
TotalNodes int
LowConfidence int // nodes with confidence < 0.2
Pinned int
Orphans int // nodes with zero edges (inbound or outbound)
}
DoctorStatsResult holds aggregated diagnostic counts computed via SQL without loading individual nodes into memory.
type Edge ¶
type Edge struct {
ID, FromID, ToID, Type, Metadata string
Acyclic bool
Weight float64
ValidAt time.Time // when the fact became true (zero = since creation)
InvalidAt time.Time // when the fact stopped being true (zero = still valid)
CreatedAt time.Time
}
Edge represents a relationship between two nodes in the graph.
type MockStorage ¶
type MockStorage struct {
// contains filtered or unexported fields
}
MockStorage is a public, concurrency-safe, in-memory implementation of the Storage interface. It is designed for use in unit tests across packages (engine/, cmd/, etc.) without requiring SQLite.
All methods honour the error injected via SetError when non-nil.
func NewMockStorage ¶
func NewMockStorage() *MockStorage
NewMockStorage returns a ready-to-use MockStorage.
func (*MockStorage) AddFileWatch ¶
func (m *MockStorage) AddFileWatch(_ context.Context, filePath, nodeID, gitHash string) error
func (*MockStorage) AddReplayEvent ¶
func (m *MockStorage) AddReplayEvent(_ context.Context, sessionID, data string) error
func (*MockStorage) AllEmbeddings ¶
func (*MockStorage) CheckCycle ¶
func (*MockStorage) Close ¶
func (m *MockStorage) Close() error
func (*MockStorage) CountAllEdges ¶
func (m *MockStorage) CountAllEdges(_ context.Context) (int, error)
func (*MockStorage) CountEdges ¶
func (*MockStorage) CountEdgesBatch ¶
func (*MockStorage) CreateEdge ¶
func (m *MockStorage) CreateEdge(_ context.Context, e *Edge) error
func (*MockStorage) CreateNode ¶
func (m *MockStorage) CreateNode(_ context.Context, n *Node) error
func (*MockStorage) CreateSession ¶
func (m *MockStorage) CreateSession(_ context.Context, sess *Session) error
func (*MockStorage) DeleteEdge ¶
func (m *MockStorage) DeleteEdge(_ context.Context, id string) error
func (*MockStorage) DeleteEmbedding ¶
func (m *MockStorage) DeleteEmbedding(_ context.Context, nodeID string) error
func (*MockStorage) DeleteNode ¶
func (m *MockStorage) DeleteNode(_ context.Context, id string) error
func (*MockStorage) DoctorStats ¶
func (m *MockStorage) DoctorStats(_ context.Context) (DoctorStatsResult, error)
func (*MockStorage) EdgeCount ¶
func (m *MockStorage) EdgeCount() int
EdgeCount returns the number of stored edges (for quick assertions).
func (*MockStorage) EndSession ¶
func (*MockStorage) FlushAccessLog ¶
func (m *MockStorage) FlushAccessLog(_ context.Context) (int, error)
func (*MockStorage) GetAllEdgesFor ¶
func (*MockStorage) GetAllSignatures ¶
func (*MockStorage) GetEdgesBetween ¶
func (*MockStorage) GetEdgesFrom ¶
func (*MockStorage) GetEdgesTo ¶
func (*MockStorage) GetEmbedding ¶
func (*MockStorage) GetEmbeddingsBatch ¶
func (*MockStorage) GetNeighbors ¶
func (*MockStorage) GetNodeByKey ¶
func (*MockStorage) GetNodesBatch ¶
func (*MockStorage) GetReplayEvents ¶
func (m *MockStorage) GetReplayEvents(_ context.Context, sessionID string) ([]*ReplayEvent, error)
func (*MockStorage) GetValidEdgesFrom ¶
func (*MockStorage) GetValidEdgesTo ¶
func (*MockStorage) GetVersions ¶
func (m *MockStorage) GetVersions(_ context.Context, nodeID string) ([]*NodeVersion, error)
func (*MockStorage) InjectEdges ¶
func (m *MockStorage) InjectEdges(edges ...*Edge)
InjectEdges seeds the mock with pre-built edges (bypasses error check).
func (*MockStorage) InjectNodes ¶
func (m *MockStorage) InjectNodes(nodes ...*Node)
InjectNodes seeds the mock with pre-built nodes (bypasses error check).
func (*MockStorage) InvalidateEdge ¶
func (m *MockStorage) InvalidateEdge(_ context.Context, id string) error
func (*MockStorage) LastUpdated ¶
func (*MockStorage) ListNodes ¶
func (m *MockStorage) ListNodes(_ context.Context, f NodeFilter) ([]*Node, error)
func (*MockStorage) ListSessions ¶
func (*MockStorage) LoadNodeMetadata ¶
func (*MockStorage) LogAccess ¶
func (m *MockStorage) LogAccess(_ context.Context, nodeID string) error
func (*MockStorage) NodeCount ¶
func (m *MockStorage) NodeCount() int
NodeCount returns the number of stored nodes (for quick assertions).
func (*MockStorage) SaveEmbedding ¶
func (*MockStorage) SaveNodeMetadata ¶
func (*MockStorage) SaveSignature ¶
func (m *MockStorage) SaveSignature(_ context.Context, nodeID, signature string) error
func (*MockStorage) SaveVersion ¶
func (*MockStorage) SearchNodeByHash ¶
func (*MockStorage) SearchNodes ¶
func (*MockStorage) SetError ¶
func (m *MockStorage) SetError(err error)
SetError configures a non-nil error that every method will return. Pass nil to clear the injected error.
func (*MockStorage) String ¶
func (m *MockStorage) String() string
String returns a human-readable summary useful in test failure output.
func (*MockStorage) TopConnected ¶
func (*MockStorage) UpdateNode ¶
func (m *MockStorage) UpdateNode(_ context.Context, n *Node) error
func (*MockStorage) UpdateNodeContent ¶
func (m *MockStorage) UpdateNodeContent(_ context.Context, id, newContent string) error
type Node ¶
type Node struct {
ID, Type, Content, ContentHash, Summary, Scope, Project, Tags string
Key string // optional unique key per project (upsert semantics)
Pinned bool // pinned nodes always appear in context
Tier int
Confidence float64
AccessCount int
CreatedAt, UpdatedAt, AccessedAt time.Time
SourceSession, SourceAgent string
Version int
Metadata map[string]string // structured key-value metadata (stored in node_metadata table)
}
Node represents a memory node in the Yaad graph.
type NodeFilter ¶
type NodeFilter struct {
Type, Scope, Project string
Tier int
MinConfidence float64
SourceSession string
Pinned *bool // filter by pinned status (nil = no filter)
Tag string // exact tag match within the CSV tags column (delimiter-aware)
MetadataFilters map[string]string // key-value metadata filters (all must match, AND semantics)
Limit int // max results (0 = default 1000)
Offset int // skip first N results (for pagination)
}
NodeFilter specifies criteria for listing nodes.
type NodeVersion ¶
type NodeVersion struct {
NodeID, Content, ChangedBy, Reason string
Version int
ChangedAt time.Time
}
NodeVersion stores a historical version of a node for audit/rollback.
type ReplayEvent ¶
ReplayEvent is a raw tool event stored for session replay.
type Storage ¶
type Storage interface {
// Nodes
CreateNode(ctx context.Context, n *Node) error
GetNode(ctx context.Context, id string) (*Node, error)
GetNodeByKey(ctx context.Context, key, project string) (*Node, error)
GetNodesBatch(ctx context.Context, ids []string) ([]*Node, error)
UpdateNode(ctx context.Context, n *Node) error
UpdateNodeContent(ctx context.Context, id, newContent string) error
DeleteNode(ctx context.Context, id string) error
ListNodes(ctx context.Context, f NodeFilter) ([]*Node, error)
SearchNodes(ctx context.Context, query string, limit int) ([]*Node, error)
SearchNodeByHash(ctx context.Context, hash, scope, project string) (*Node, error)
GetNeighbors(ctx context.Context, nodeID string) ([]*Node, error)
// Edges
CreateEdge(ctx context.Context, e *Edge) error
GetEdge(ctx context.Context, id string) (*Edge, error)
InvalidateEdge(ctx context.Context, id string) error // non-destructive: sets invalid_at = now
DeleteEdge(ctx context.Context, id string) error
GetEdgesFrom(ctx context.Context, nodeID string) ([]*Edge, error)
GetEdgesTo(ctx context.Context, nodeID string) ([]*Edge, error)
// Point-in-time validity queries: return only edges that were valid at the
// given instant (valid_at <= at AND (invalid_at IS NULL OR invalid_at > at)).
// A zero `at` is treated as "now". Default edge readers are unaffected.
GetValidEdgesFrom(ctx context.Context, nodeID string, at time.Time) ([]*Edge, error)
GetValidEdgesTo(ctx context.Context, nodeID string, at time.Time) ([]*Edge, error)
GetEdgesBetween(ctx context.Context, nodeIDs []string) ([]*Edge, error)
GetAllEdgesFor(ctx context.Context, nodeIDs []string) ([]*Edge, error) // edges where from_id OR to_id in set
CountEdges(ctx context.Context, nodeID string) (inbound int, outbound int, err error)
CountEdgesBatch(ctx context.Context, nodeIDs []string) (map[string][2]int, error)
CountAllEdges(ctx context.Context) (int, error)
// Stats
NodeStats(ctx context.Context) (map[string]int, int, error) // nodesByType, totalNodes, err
DoctorStats(ctx context.Context) (DoctorStatsResult, error) // aggregated diagnostics without loading nodes
LastUpdated(ctx context.Context) (time.Time, error)
TopConnected(ctx context.Context, limit int) ([]string, error)
// Graph queries (encapsulates recursive CTEs)
CheckCycle(ctx context.Context, fromID, toID string) (bool, error)
// Sessions
CreateSession(ctx context.Context, sess *Session) error
EndSession(ctx context.Context, id string, summary string) error
ListSessions(ctx context.Context, project string, limit int) ([]*Session, error)
// Versions
SaveVersion(ctx context.Context, nodeID string, content, changedBy, reason string) error
GetVersions(ctx context.Context, nodeID string) ([]*NodeVersion, error)
// Embeddings
SaveEmbedding(ctx context.Context, nodeID, model string, vector []float32) error
GetEmbedding(ctx context.Context, nodeID string) ([]float32, string, error)
DeleteEmbedding(ctx context.Context, nodeID string) error
// AllEmbeddings / GetEmbeddingsBatch take a model filter ("" = all models):
// vectors from different embedding models occupy incompatible spaces and must
// not be mixed into one similarity comparison.
AllEmbeddings(ctx context.Context, model string) (map[string][]float32, error)
GetEmbeddingsBatch(ctx context.Context, model string, offset, limit int) (map[string][]float32, error)
// Replay
AddReplayEvent(ctx context.Context, sessionID, data string) error
GetReplayEvents(ctx context.Context, sessionID string) ([]*ReplayEvent, error)
// File watch (staleness tracking)
AddFileWatch(ctx context.Context, filePath, nodeID, gitHash string) error
// AccessLog: lightweight access tracking (batched flush)
LogAccess(ctx context.Context, nodeID string) error
FlushAccessLog(ctx context.Context) (int, error)
// Metadata: structured key-value metadata per node
SaveNodeMetadata(ctx context.Context, nodeID string, meta map[string]string) error
LoadNodeMetadata(ctx context.Context, nodeIDs []string) (map[string]map[string]string, error)
// Signatures: HMAC integrity signatures for tamper detection
SaveSignature(ctx context.Context, nodeID, signature string) error
GetAllSignatures(ctx context.Context) (map[string]string, error)
// Transactions
WithTx(ctx context.Context, fn func(Storage) error) error
Close() error
}
Storage is the persistence interface used by Engine and other packages. All graph-aware queries (recursive CTEs, cycle detection) are encapsulated behind methods so callers never need raw *sql.DB access.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
func (*Store) AddFileWatch ¶
func (*Store) AddReplayEvent ¶
AddReplayEvent stores a tool event for replay.
func (*Store) AllEmbeddings ¶
AllEmbeddings returns all stored embeddings as (nodeID, vector) pairs. When model is non-empty, only that model's embeddings are returned; pass "" to retrieve every model. See GetEmbeddingsBatch for why mixing models is unsafe.
func (*Store) CheckCycle ¶
CheckCycle uses a recursive CTE to detect if adding from->to would create a cycle among acyclic edges.
func (*Store) CountAllEdges ¶
CountAllEdges returns the total number of edges in the graph.
func (*Store) CountEdges ¶
func (s *Store) CountEdges(ctx context.Context, nodeID string) (inbound int, outbound int, err error)
CountEdges returns inbound and outbound edge counts for a node.
func (*Store) CountEdgesBatch ¶
CountEdgesBatch returns inbound/outbound edge counts for multiple nodes in a single query.
func (*Store) CreateCodeIndex ¶
CreateCodeIndex creates the code_chunks table and its FTS5 virtual table. Safe to call multiple times (uses IF NOT EXISTS).
func (*Store) CreateSession ¶
func (*Store) DeleteChunksByPath ¶
DeleteChunksByPath removes all code chunks for a given file path.
func (*Store) DeleteEmbedding ¶
DeleteEmbedding removes a vector embedding for a node.
func (*Store) DiffVersions ¶
func (s *Store) DiffVersions(ctx context.Context, nodeID string, v1, v2 int) (content1, content2 string, err error)
DiffVersions returns the content of two versions for comparison.
func (*Store) DoctorStats ¶
func (s *Store) DoctorStats(ctx context.Context) (DoctorStatsResult, error)
DoctorStats returns aggregated diagnostic counts via SQL without loading individual nodes into memory.
func (*Store) EndSession ¶
func (*Store) FillNodeMetadata ¶
FillNodeMetadata loads metadata for all given nodes in one batch query and populates each node's Metadata field in-place. No-op for an empty slice.
func (*Store) FindByPrefix ¶
FindByPrefix returns nodes whose ID starts with the given prefix. Returns at most 10 results. The prefix must be at least 1 character.
func (*Store) FlushAccessLog ¶
FlushAccessLog aggregates access_log entries into nodes.access_count / accessed_at, then truncates the log. Runs atomically within a transaction.
func (*Store) GetAllEdgesFor ¶
GetAllEdgesFor returns all edges where from_id OR to_id is in the given set. Used by IntentBFS for batch edge retrieval (avoids N+1 queries).
func (*Store) GetAllSignatures ¶
func (*Store) GetEdge ¶
GetEdge retrieves an edge by its primary key ID. Returns ErrEdgeNotFound wrapped with the ID when no row is found.
func (*Store) GetEdgesBetween ¶
func (*Store) GetEdgesFrom ¶
func (*Store) GetEdgesTo ¶
func (*Store) GetEmbedding ¶
GetEmbedding retrieves the embedding for a node.
func (*Store) GetEmbeddingsBatch ¶
func (s *Store) GetEmbeddingsBatch(ctx context.Context, model string, offset, limit int) (map[string][]float32, error)
GetEmbeddingsBatch returns a paginated batch of embeddings. When model is non-empty, only embeddings produced by that model are returned — vectors from a different model live in an incompatible vector space and must never be mixed into a single cosine/ANN comparison. Pass "" to retrieve all models (e.g. for maintenance or migration).
func (*Store) GetFileHash ¶
GetFileHash returns the file hash for the first chunk of the given path.
func (*Store) GetNeighbors ¶
func (*Store) GetNode ¶
GetNode retrieves a node by its primary key ID. Returns ErrNodeNotFound wrapped with the ID when no row is found.
func (*Store) GetNodeByKey ¶
GetNodeByKey looks up a node by its unique key within a project. Returns (nil, nil) when no matching node is found (upsert check pattern).
func (*Store) GetNodesBatch ¶
func (*Store) GetNodesByFile ¶
func (*Store) GetReplayEvents ¶
GetReplayEvents returns all events for a session in order.
func (*Store) GetValidEdgesFrom ¶
func (s *Store) GetValidEdgesFrom(ctx context.Context, nodeID string, at time.Time) ([]*Edge, error)
GetValidEdgesFrom returns outbound edges from nodeID that were valid at the given instant: valid_at <= at AND (invalid_at IS NULL OR invalid_at > at). A zero `at` defaults to now, giving a "currently valid" view. This is a point-in-time variant of GetEdgesFrom; the latter is left unchanged so existing callers keep their current behavior.
func (*Store) GetValidEdgesTo ¶
GetValidEdgesTo is the inbound counterpart of GetValidEdgesFrom.
func (*Store) GetVersions ¶
func (*Store) InvalidateStaleChunks ¶
InvalidateStaleChunks deletes all code chunks whose schema_version does not match currentVersion, returning the count of deleted rows.
func (*Store) LastUpdated ¶
LastUpdated returns the most recent updated_at time across all nodes.
func (*Store) ListIndexedPaths ¶
ListIndexedPaths returns all distinct file paths that have indexed chunks.
func (*Store) ListSessions ¶
func (*Store) LoadNodeMetadata ¶
func (*Store) LogAccess ¶
LogAccess records a lightweight access event (INSERT only, no UPDATE churn).
func (*Store) RefreshCodeIndex ¶
func (s *Store) RefreshCodeIndex(ctx context.Context, paths []string, hashFn func(string) string) (int, error)
RefreshCodeIndex checks each path for staleness by comparing the current file hash (via hashFn) against the stored hash. Returns the count of stale files.
func (*Store) RollbackToVersion ¶
RollbackToVersion restores a node's content to a specific version. All operations run inside a single transaction to prevent concurrent modifications from interleaving between the SELECT and UPDATE.
func (*Store) SaveEmbedding ¶
SaveEmbedding stores a vector embedding for a node.
Like every other write path it retries on SQLITE_BUSY: embedding writes run outside the engine's write lock and can briefly contend with the async ingestion goroutine for the single SQLite writer.
func (*Store) SaveNodeMetadata ¶
func (*Store) SaveSignature ¶
func (*Store) SaveVersion ¶
func (*Store) SearchCodeChunksByLanguage ¶
func (s *Store) SearchCodeChunksByLanguage(ctx context.Context, query string, languages []string, limit int) ([]*CodeChunkRecord, error)
SearchCodeChunksByLanguage performs a full-text search filtered by language. If languages is empty, calls existing SearchCodeChunksFTS. If one language, adds a WHERE language = ? filter. If multiple languages, runs separate queries per language and merges via min-heap.
func (*Store) SearchCodeChunksFTS ¶
func (s *Store) SearchCodeChunksFTS(ctx context.Context, query string, limit int) ([]*CodeChunkRecord, error)
SearchCodeChunksFTS performs a full-text search on code chunks.
func (*Store) SearchCodeChunksHybrid ¶
func (s *Store) SearchCodeChunksHybrid(ctx context.Context, query string, queryVec []float32, limit int, languages []string) ([]*CodeChunkRecord, error)
SearchCodeChunksHybrid performs a hybrid search combining FTS5 ranking with cosine similarity on stored vectors. If languages is non-empty, only chunks in those languages are considered.
func (*Store) SearchNodeByHash ¶
SearchNodeByHash finds a node by content hash + scope + project (dedup check). Returns (nil, nil) when not found — used for dedup checks where "not found" is not an error. SearchNodeByHash looks up a node by content hash within a scope/project. Returns (nil, nil) when no matching node is found (dedup check pattern).
func (*Store) SearchNodes ¶
func (*Store) StoreVector ¶
StoreVector saves an embedding vector for a code chunk.
func (*Store) TopConnected ¶
TopConnected returns the content of the most-connected nodes by edge count.
func (*Store) UpdateNodeContent ¶
func (*Store) UpsertByTopic ¶
UpsertByTopic updates an existing node if one with the same project+scope+topic_key exists, otherwise creates a new one. Based on Engram's topic dedup approach. topic_key is stored in the Tags field as "topic:<key>".
func (*Store) UpsertCodeChunk ¶
func (s *Store) UpsertCodeChunk(ctx context.Context, chunk *CodeChunkRecord) error
UpsertCodeChunk inserts or replaces a code chunk record and updates the FTS index.
func (*Store) WithTx ¶
WithTx runs the given function inside a SQL transaction. If the function returns an error, the transaction is rolled back.
The whole transaction is retried on SQLITE_BUSY. A transaction acquires the single SQLite writer more eagerly than a lone statement, so it can lose a race with the async ingestion goroutine; without retry, correctness-critical transactional writes (e.g. the atomic cycle-check+insert in AddEdge) would fail spuriously under concurrency. Re-running fn is safe because a busy error occurs before commit, so no partial state is visible.