Documentation
¶
Overview ¶
Package graph — store.go defines the pluggable storage interface for persisting and restoring projected graphs to/from remote backends.
The unit of persistence is a SQLite .db file (produced by SQLiteWriter or mache build). This keeps the abstraction simple: backends just need to store and retrieve opaque blobs keyed by repo identity + generation.
Implementations: local disk (default), Cloudflare R2, GCS, S3.
Index ¶
- Constants
- Variables
- func AggregateRefs(refs map[string][]string, g Granularity, scope string) map[string][]string
- func ColumnExists(db *sql.DB, table, col string) bool
- func ConnectedComponents(refs map[string][]string) [][]string
- func CreateArena(dbPath, arenaPath string) error
- func DecodeProps(raw []byte) map[string]json.RawMessage
- func DefTokenFor(g Graph, nodeID string) string
- func ExportSQLite(store *MemoryStore, dbPath string) error
- func ExtractActiveDB(arenaPath string) (string, error)
- func FilterTestRefs(refs map[string][]string) map[string][]string
- func FindSourceChild(g Graph, dirID string) string
- func IsCalleesPath(path string) bool
- func IsCallersPath(path string) bool
- func IsDiagPath(path string) bool
- func IsFileLevelSentinel(nodeID string) bool
- func NormalizeID(id string) string
- func ParseCalleesPath(path string) (parentDir, entryName string)
- func ParseCallersPath(path string) (parentDir, entryName string)
- func ParseDiagPath(path string) (parentDir, fileName string)
- func ProjectionVersion(db *sql.DB) (string, bool)
- func PropRaw(n *Node, key string) []byte
- func PropString(n *Node, key string) string
- func RequireProps(db *sql.DB) error
- func SetPropRaw(n *Node, key string, raw []byte)
- func SetPropString(n *Node, key, value string)
- func SliceContent(data, buf []byte, offset int64) int
- func VDirSymlinkTarget(vdirParentDir, targetID string) string
- func WriteArenaHeader(f *os.File, h *ArenaHeader) error
- type ASTKinder
- type ActionResult
- type ArenaFlusher
- type ArenaHeader
- type CallExtractor
- type Class
- type Community
- type CommunityResult
- type CompositeGraph
- func (c *CompositeGraph) Act(id, action, payload string) (*ActionResult, error)
- func (c *CompositeGraph) DefsMap() map[string][]string
- func (c *CompositeGraph) GetCallees(id string) ([]*Node, error)
- func (c *CompositeGraph) GetCallers(token string) ([]*Node, error)
- func (c *CompositeGraph) GetNode(id string) (*Node, error)
- func (c *CompositeGraph) Invalidate(id string)
- func (c *CompositeGraph) ListChildStats(id string) ([]NodeStat, error)
- func (c *CompositeGraph) ListChildren(id string) ([]string, error)
- func (c *CompositeGraph) LookupDef(token string) []string
- func (c *CompositeGraph) Mount(prefix string, g Graph) error
- func (c *CompositeGraph) MountPrefixOf(id string) string
- func (c *CompositeGraph) ReadContent(id string, buf []byte, offset int64) (int, error)
- func (c *CompositeGraph) SearchDefs(pattern string, limit int) map[string][]string
- func (c *CompositeGraph) SetCallExtractor(fn CallExtractor)
- func (c *CompositeGraph) SetCallExtractorPicker(picker func(local Graph) CallExtractor)
- func (c *CompositeGraph) Unmount(prefix string) error
- type ContentCache
- type ContentRef
- type ContentResolverFunc
- type DBPathProvider
- type DefTokener
- type DefsLookuper
- type DefsMapper
- type DefsNodeLookuper
- type DefsSearcher
- type ErrGraphNotCached
- type Granularity
- type Graph
- type GraphCache
- func (c *GraphCache) AppendChild(parentID, childID string) bool
- func (c *GraphCache) Batch(fn func(*MemoryStore)) error
- func (c *GraphCache) ClearChildren(id string) bool
- func (c *GraphCache) Close() error
- func (c *GraphCache) GetData(id string) ([]byte, bool)
- func (c *GraphCache) GetNode(id string) (*Node, bool)
- func (c *GraphCache) ListChildren(id string) ([]string, bool)
- func (c *GraphCache) Persist() error
- func (c *GraphCache) PutDir(id string, isRoot bool) bool
- func (c *GraphCache) PutFile(id string, data []byte)
- func (c *GraphCache) RemoveChild(parentID, childID string) bool
- func (c *GraphCache) RootIDs() []string
- func (c *GraphCache) Store() *MemoryStore
- type GraphMeta
- type GraphStore
- type HotSwapGraph
- func (h *HotSwapGraph) Act(id, action, payload string) (*ActionResult, error)
- func (h *HotSwapGraph) GetCallees(id string) ([]*Node, error)
- func (h *HotSwapGraph) GetCallers(token string) ([]*Node, error)
- func (h *HotSwapGraph) GetNode(id string) (*Node, error)
- func (h *HotSwapGraph) Invalidate(id string)
- func (h *HotSwapGraph) ListChildStats(id string) ([]NodeStat, error)
- func (h *HotSwapGraph) ListChildren(id string) ([]string, error)
- func (h *HotSwapGraph) ReadContent(id string, buf []byte, offset int64) (int, error)
- func (h *HotSwapGraph) Swap(newGraph Graph)
- type IndexStaleness
- type LocalStore
- func (s *LocalStore) Delete(_ context.Context, repo string) error
- func (s *LocalStore) Get(_ context.Context, repo string) (io.ReadCloser, uint64, error)
- func (s *LocalStore) Head(_ context.Context, repo string) (*GraphMeta, error)
- func (s *LocalStore) List(_ context.Context) ([]GraphMeta, error)
- func (s *LocalStore) Put(_ context.Context, repo string, generation uint64, r io.Reader) error
- type MemoryStore
- func (s *MemoryStore) Act(id, action, payload string) (*ActionResult, error)
- func (s *MemoryStore) AddDef(token, dirID string) error
- func (s *MemoryStore) AddFileChildren(parent *Node, files []*Node)
- func (s *MemoryStore) AddNode(n *Node)
- func (s *MemoryStore) AddRef(token, nodeID string) error
- func (s *MemoryStore) AddRoot(n *Node)
- func (s *MemoryStore) Close() error
- func (s *MemoryStore) DefsMap() map[string][]string
- func (s *MemoryStore) DeleteFileNodes(filePath string)
- func (s *MemoryStore) DeleteNodes(ids []string)
- func (s *MemoryStore) FileMtime(filePath string) time.Time
- func (s *MemoryStore) FlushRefs() error
- func (s *MemoryStore) ForgetFile(filePath string)
- func (s *MemoryStore) GetCallees(id string) ([]*Node, error)
- func (s *MemoryStore) GetCallers(token string) ([]*Node, error)
- func (s *MemoryStore) GetNode(id string) (*Node, error)
- func (s *MemoryStore) InitRefsDB() error
- func (s *MemoryStore) Invalidate(id string)
- func (s *MemoryStore) IsFileStale(filePath string) bool
- func (s *MemoryStore) ListChildStats(id string) ([]NodeStat, error)
- func (s *MemoryStore) ListChildren(id string) ([]string, error)
- func (s *MemoryStore) LookupDef(token string) []string
- func (s *MemoryStore) NodesForPath(filePath string) []string
- func (s *MemoryStore) QueryRefs(query string, args ...any) (*sql.Rows, error)
- func (s *MemoryStore) ReadContent(id string, buf []byte, offset int64) (int, error)
- func (s *MemoryStore) RecordFileMtime(filePath string, mtime time.Time)
- func (s *MemoryStore) RefsMap() map[string][]string
- func (s *MemoryStore) ReplaceFileNodes(filePath string, newNodes []*Node)
- func (s *MemoryStore) RootIDs() []string
- func (s *MemoryStore) SearchDefs(pattern string, limit int) map[string][]string
- func (s *MemoryStore) SearchRefs(pattern, segment string, limit int) ([]RefHit, error)
- func (s *MemoryStore) SetCallExtractor(fn CallExtractor)
- func (s *MemoryStore) SetRefresher(fn func(filePath string) error)
- func (s *MemoryStore) SetResolver(fn ContentResolverFunc)
- func (s *MemoryStore) SetScopedCallExtractor(fn ScopedCallExtractor)
- func (s *MemoryStore) ShiftOrigins(filePath string, afterByte uint32, delta int32)
- func (s *MemoryStore) UpdateNodeContent(id string, data []byte, origin *SourceOrigin, modTime time.Time) error
- func (s *MemoryStore) UpdateNodeContext(id string, ctx []byte) error
- type MermaidOpts
- type MountPrefixer
- type Node
- type NodeStat
- type NodesForPathProvider
- type NodesTableReader
- func (r *NodesTableReader) Close()
- func (r *NodesTableReader) DB() *sql.DB
- func (r *NodesTableReader) GetCallers(token string) ([]*Node, error)
- func (r *NodesTableReader) GetNode(id string) (*Node, error)
- func (r *NodesTableReader) Invalidate(id string)
- func (r *NodesTableReader) ListChildStats(id string) ([]NodeStat, error)
- func (r *NodesTableReader) ListChildren(id string) ([]string, error)
- func (r *NodesTableReader) ReadContent(id string, buf []byte, offset int64) (int, error)
- type PrunedToken
- type QualifiedCall
- type QuotientEdge
- type QuotientGraph
- type R2Store
- type RefHit
- type RefRange
- type RefResolution
- type RefResolver
- type RefSearcher
- type RefTarget
- type RefsMapper
- type RefsQuerier
- type SQLiteGraph
- func (g *SQLiteGraph) ASTKindChain(id string) ([]string, bool, error)
- func (g *SQLiteGraph) Act(id, action, payload string) (*ActionResult, error)
- func (g *SQLiteGraph) AddDef(token, dirID string) error
- func (g *SQLiteGraph) AddRef(token, nodeID string) error
- func (g *SQLiteGraph) Close() error
- func (g *SQLiteGraph) DB() *sql.DB
- func (g *SQLiteGraph) DBPath() string
- func (g *SQLiteGraph) DefTokenOf(id string) (string, bool, error)
- func (g *SQLiteGraph) DefsMap() map[string][]string
- func (g *SQLiteGraph) EagerScan() error
- func (g *SQLiteGraph) FlushRefs() error
- func (g *SQLiteGraph) GetCallees(id string) ([]*Node, error)
- func (g *SQLiteGraph) GetCallers(token string) ([]*Node, error)
- func (g *SQLiteGraph) GetNode(id string) (*Node, error)
- func (g *SQLiteGraph) IndexStaleness() (IndexStaleness, bool)
- func (g *SQLiteGraph) Invalidate(id string)
- func (g *SQLiteGraph) ListChildStats(id string) ([]NodeStat, error)
- func (g *SQLiteGraph) ListChildren(id string) ([]string, error)
- func (g *SQLiteGraph) LookupDef(token string) []string
- func (g *SQLiteGraph) LookupDefNodes(token string) ([]*Node, error)
- func (g *SQLiteGraph) QueryRefs(query string, args ...any) (*sql.Rows, error)
- func (g *SQLiteGraph) ReadContent(id string, buf []byte, offset int64) (int, error)
- func (g *SQLiteGraph) RefRangeOf(nodeID string) (*RefRange, error)
- func (g *SQLiteGraph) RefsMap() map[string][]string
- func (g *SQLiteGraph) ResolveRef(nodeID string) (*RefTarget, error)
- func (g *SQLiteGraph) SearchDefs(pattern string, limit int) map[string][]string
- func (g *SQLiteGraph) SearchRefs(pattern, segment string, limit int) ([]RefHit, error)
- func (g *SQLiteGraph) SetCallExtractor(fn CallExtractor)
- func (g *SQLiteGraph) SetScopedCallExtractor(fn ScopedCallExtractor)
- type SQLiteResolver
- type ScopedCallExtractor
- type SheafBackend
- type SheafInvalidator
- func (si *SheafInvalidator) CommunityResultForRouting() *CommunityResult
- func (si *SheafInvalidator) HasResult() bool
- func (si *SheafInvalidator) InvalidateAllScoped() int
- func (si *SheafInvalidator) InvalidateNodesWithCascade(ids []string) int
- func (si *SheafInvalidator) InvalidateWithCascade(id string, membership map[string]int) int
- func (si *SheafInvalidator) SetCommunityResult(cr *CommunityResult)
- func (si *SheafInvalidator) SetSheaf(b SheafBackend) (prior SheafBackend)
- func (si *SheafInvalidator) SetState(result *CommunityResult, sheaf SheafBackend) (prior SheafBackend)
- type SourceOrigin
- type StalenessReporter
- type TemplateRenderer
- type WritableGraph
- func (g *WritableGraph) Act(id, action, payload string) (*ActionResult, error)
- func (g *WritableGraph) Close() error
- func (g *WritableGraph) DBPath() string
- func (g *WritableGraph) Flush()
- func (g *WritableGraph) FlushNow() error
- func (g *WritableGraph) GetCallees(id string) ([]*Node, error)
- func (g *WritableGraph) GetCallers(token string) ([]*Node, error)
- func (g *WritableGraph) GetNode(id string) (*Node, error)
- func (g *WritableGraph) Invalidate(id string)
- func (g *WritableGraph) ListChildStats(id string) ([]NodeStat, error)
- func (g *WritableGraph) ListChildren(id string) ([]string, error)
- func (g *WritableGraph) ReadContent(id string, buf []byte, offset int64) (int, error)
- func (g *WritableGraph) UpdateRecord(id string, content []byte) error
Constants ¶
const ( ArenaHeaderSize = 4096 ArenaMagic = 0x4C455930 // ArenaVersion is the current wire-format. v1 arenas inferred payload // length from the SQLite header (db page_count × page_size); v2 // records DataSize explicitly so readers stay format-agnostic. ArenaVersion = 2 )
const ( NodeKindFile = 0 NodeKindDir = 1 )
NodeKindFile and NodeKindDir are the integer values stored in the nodes table's `kind` column (the wire format produced by mache build / leyline parse). They are NOT a substitute for *Node.Mode at runtime — Mode uses fs.FileMode and carries the IsDir() predicate. The kind constants are used by ingestion writers when populating the column, by readers when interpreting it, and as a comment marker next to bare-int SQL literals like `kind = 1` (SQL can't reference Go constants directly).
These values are part of the on-disk schema and must not change.
Bead mache-e7e9e5.
const ( SchemaDotJSON = "_schema.json" DiagnosticsDir = "_diagnostics" ContextFile = "context" LocationFile = "location" PromptFile = "PROMPT.txt" CallersDir = "callers" CalleesDir = "callees" DiagLastWrite = "last-write-status" DiagASTErrors = "ast-errors" DiagLint = "lint" )
Well-known virtual directory and file names.
const DefaultMaxFanIn = 100
DefaultMaxFanIn is the per-token fan-in above which a token is treated as a hub and excluded from the co-reference projection.
The projection connects every PAIR of nodes sharing a token, so a token referenced by K nodes costs K(K-1)/2 edges. Measured on mache itself: 12,275 tokens produce 84,813,280 edge insertions, and the single token `t` (the ubiquitous *testing.T receiver, K=9,903) accounts for 49M of them — 58% of the total. The top ten tokens — t, err, NoError, require.NoError, Equal, len — are 86%.
Cost is only half the reason. Those hubs also DESTROY the answer: `t` joins 9,903 nodes into a near-complete subgraph, so Louvain collapses everything that touches *testing.T into one blob. A token that nearly everything references is not evidence of shared purpose; it is the co-occurrence equivalent of a stopword.
100 keeps 12,091 of 12,275 tokens (98.5%) and removes 99.2% of the edge work. The threshold is a heuristic, exposed as a parameter — it is not a constant anyone should have to edit source to change, which is exactly the mistake the old refs-count cap made.
const DefaultMaxPairs = 5_000_000
DefaultMaxPairs bounds the projection's total edge insertions — Σ K(K-1)/2 over surviving tokens — as a backstop behind the fan-in cap.
Fan-in pruning removes the DOMINANT term, not the whole cost. With a cap of 100, a pathological repo where every one of 12,275 tokens sits exactly at the cap would still project 60.8M pairs; mache measures 681K only because real fan-in is heavily skewed. A budget keyed on the quantity that actually costs is the guard the old refs-count cap was reaching for and missed by ~7000×.
5,000,000 is ~7x mache's own measured cost, so a repo an order of magnitude larger still runs unclamped.
const FileLevelSentinelPrefix = "_file_level:"
FileLevelSentinelPrefix marks a node_refs row that records a FILE-level reference rather than a reference from a construct.
The engine emits these for patterns like cobra's `RunE: handler`, where a token is referenced at file scope with no enclosing function. dead_code needs them: its alive-set check is token-only, and without the sentinel every handler assigned that way looks dead. Attributing the ref to some arbitrary function in the file instead would be worse — it made every function in a cobra file appear to call the callback (mache-02r9).
So the row is correct. What it is NOT is user content: `_file_level:` ids name no construct anyone can open, refactor, or reason about, and they carry an ABSOLUTE path, so they are not even portable. They are bookkeeping, and they belong to the producer side of the schema.
const ProjectionSchemaVersionKey = "projection_schema_version"
ProjectionSchemaVersionKey is the `_meta` row ley-line-open stamps with the shape of the projection it wrote.
Variables ¶
var ErrActNotSupported = errors.New("act not supported by this graph")
ErrActNotSupported is returned by Graph implementations that do not support actions.
var ErrNotFound = errors.New("node not found")
var ErrStalePropsSchema = errors.New("nodes table predates the props column")
ErrStalePropsSchema is returned for a nodes table mache itself wrote before the props column existed. Exposed so callers can distinguish "rebuild this" from a genuine open failure.
Functions ¶
func AggregateRefs ¶ added in v0.22.0
AggregateRefs rewrites refs (token → node ids) to the requested granularity and applies an optional path-prefix scope.
Scope BEFORE aggregation, on the raw id: scoping to "cmd/" must keep cmd/serve.go's nodes whether the caller asked for construct, file, or dir cells.
Aggregation DEDUPES within a token: a token referenced 40 times inside one file is ONE occurrence of that file, not 40 — otherwise buildProjection would inflate pair weights quadratically for chatty files, and the hub problem this pipeline just removed at token level would reappear at cell level. Order is preserved first-seen so identical input keeps producing an identical projection (mache-ff7e31).
A token whose occurrences all collapse into a single cell contributes no pairs and is dropped by buildProjection's len<2 skip — correctly: a symbol used in only one file says nothing about which files belong together.
func ColumnExists ¶ added in v0.22.0
ColumnExists reports whether table has a column named col. Readers use it to stay compatible with nodes tables written before a column was added (e.g. `context`, mache-b8fe72) or produced by leyline, and writers use it to decide whether an ALTER is needed.
Deliberately pragma_table_xinfo, NOT pragma_table_info: table_info omits GENERATED columns entirely, so it answers "missing" for a column that is present, readable, indexed, and returned by SELECT *. ley-line-open already ships one (`source_blobs.byte_len` STORED) and projection-v4 makes `nodes.parent_id` another (mache-bc6ca3). Under table_info a reader would silently downgrade to a compatibility path for a column it can read, and a writer would emit an ALTER that fails with "duplicate column name". xinfo reports the same rows as info plus generated and hidden ones, so no existing probe changes its answer.
func ConnectedComponents ¶ added in v0.22.0
ConnectedComponents finds connected components in the refs graph projection. Simpler than Louvain — useful as a baseline or when modularity optimization is overkill (e.g., disconnected subgraphs).
func CreateArena ¶ added in v0.22.0
CreateArena creates a fresh double-buffered arena file from a .db file. Layout: [Header (4KB)] [Buffer0 = dbBytes] [Buffer1 = zeros] Buffer size is the .db file size rounded up to the next 4KB page boundary.
func DecodeProps ¶ added in v0.22.0
func DecodeProps(raw []byte) map[string]json.RawMessage
DecodeProps unmarshals a serialized Properties blob as read from SQLite. Returns nil when raw is empty or is not a property map — a node carrying inline content rather than properties decodes to nil, which is expected.
Every SQL read path goes through this rather than unmarshaling inline, so the on-disk encoding stays knowable from this file alone.
func DefTokenFor ¶ added in v0.22.0
DefTokenFor returns the symbol a node defines.
Callers that need "what is this node called?" must NOT reach for filepath.Base(nodeID). That works only on the standalone mache schema, where a node ID ends in the symbol. On a leyline projection — mache's primary backend — IDs end in a tree-sitter construct kind plus an ordinal:
a.go/function_declaration_0 not a.go/Alpha pkg/type_spec not pkg/Greeter
Measured over a real projection of this repo: of 10,409 rows in node_defs, ZERO have a node_id ending in their own token. So the path-segment guess is not merely lossy there, it is never right (mache-cb4369).
That mattered because it did not fail closed. GetCallers("function_declaration") matches that literal string wherever it appears, and leyline indexes markdown too — so get_impact answered with code spans from docs/ARCHITECTURE.md presented as call sites. Wrong answers, not empty ones.
node_defs is the authority: it maps a node to the token that node declares, asked through the graph's DefTokener so this file names none of the producer's columns (projection-v6 renamed them, mache-90cb9f).
Every surface that names a construct's callers asks this: MCP's list_directory, get_impact and get_dataflow, and the mount's callers/. The filepath.Base fallback is kept for the standalone mache schema (no node_defs table, or a backend with no SQL handle at all), where it IS the symbol — and only for that case.
func ExportSQLite ¶ added in v0.4.0
func ExportSQLite(store *MemoryStore, dbPath string) error
ExportSQLite writes all nodes from a MemoryStore to a SQLite database. Creates the nodes table if it doesn't exist. Existing entries are overwritten. The resulting file uses the standard mache nodes table schema.
func ExtractActiveDB ¶ added in v0.22.0
ExtractActiveDB extracts the active SQLite database from the arena to a temp file. Returns the path to the temp file.
func FilterTestRefs ¶ added in v0.22.0
FilterTestRefs returns a copy of refs with test-related content removed. Filters both test nodes (IDs containing "_test", "Test", "Benchmark") and test tokens (env vars with "TEST" in the name, tokens starting with "Test").
func FindSourceChild ¶ added in v0.22.0
FindSourceChild finds the "source" file child of a directory node. Returns the full source ID or "" if not found. Used by callees/ to resolve a callee directory to its source content.
func IsCalleesPath ¶ added in v0.22.0
IsCalleesPath returns true if the path contains a /callees segment boundary.
func IsCallersPath ¶ added in v0.22.0
IsCallersPath returns true if the path contains a /callers segment boundary.
func IsDiagPath ¶ added in v0.22.0
IsDiagPath returns true if the path contains a /_diagnostics segment.
func IsFileLevelSentinel ¶ added in v0.22.0
IsFileLevelSentinel reports whether a node id is engine bookkeeping rather than a construct a consumer can act on.
func NormalizeID ¶ added in v0.22.0
NormalizeID strips a leading slash from node IDs. FUSE/NFS paths use "/foo/source" but graph keys are "foo/source". Only strips one leading slash; "//double" becomes "/double".
func ParseCalleesPath ¶ added in v0.22.0
ParseCalleesPath splits a callees path into (parentDir, entryName). E.g. "/funcs/Foo/callees/funcs_Bar_source" → ("/funcs/Foo", "funcs_Bar_source")
func ParseCallersPath ¶ added in v0.22.0
ParseCallersPath splits a callers path into (parentDir, entryName). E.g. "/funcs/Foo/callers/funcs_Bar_source" → ("/funcs/Foo", "funcs_Bar_source") Returns ("", "") if not a valid callers path.
func ParseDiagPath ¶ added in v0.22.0
ParseDiagPath splits a diagnostics path into (parentDir, fileName). E.g. "/vulns/func_a/_diagnostics/last-write-status" → ("/vulns/func_a", "last-write-status") Returns ("", "") if not a valid diagnostics path.
func ProjectionVersion ¶ added in v0.22.0
ProjectionVersion returns the projection shape an artifact declares and whether it declares one at all.
A missing `_meta` table, a missing row and an empty value are all reported the same way — undeclared — because a reader cannot act on the difference: each means this artifact will not tell you its shape.
func PropString ¶ added in v0.20.0
PropString returns a string-valued property, or "" when the node, the map, or the key is absent, or the value is not a JSON string.
func RequireProps ¶ added in v0.22.0
RequireProps rejects a nodes table that mache wrote before the props column existed (mache-90b89b). Such a table has Properties base64'd into `record`, a format no current code reads — serving it would silently yield constructs with no lang/pkg/imports rather than failing.
The check is scoped by producer. mache's SQLiteWriter always has `context` (it is in CREATE TABLE, and ALTERed in for older tables); leyline-produced tables stop at source_file and carry no Properties at all. So `context` present with `props` absent is exactly "stale mache-written", and is the only case refused — a blanket refusal would reject every leyline .db, which since v0.18.0 is mache's primary input.
func SetPropRaw ¶ added in v0.20.0
SetPropRaw sets a property's raw JSON bytes, allocating Properties if nil. The caller is responsible for raw being valid JSON.
func SetPropString ¶ added in v0.20.0
SetPropString sets a string-valued property, allocating Properties if nil.
func SliceContent ¶ added in v0.22.0
SliceContent copies content bytes into buf at the given offset. Returns the number of bytes copied. Shared by all ReadContent implementations.
func VDirSymlinkTarget ¶ added in v0.22.0
VDirSymlinkTarget computes the relative symlink target from a virtual dir entry back to the target node in the graph. Works for both callers/ and callees/.
func WriteArenaHeader ¶ added in v0.22.0
func WriteArenaHeader(f *os.File, h *ArenaHeader) error
WriteArenaHeader serializes an ArenaHeader to the first 24 bytes at offset 0.
Types ¶
type ASTKinder ¶ added in v0.22.0
type ASTKinder interface {
// ASTKindChain returns the kind of the node at id, followed by the kinds
// of those of its ancestors that have one, nearest first. ok is false
// when the node itself has no _ast row.
ASTKindChain(id string) (chain []string, ok bool, err error)
}
ASTKinder reports the tree-sitter kinds of a node and its ancestors: the question a kind filter asks ("is this a function, and is it inside a class?"). It exists so the asker does not spell the producer's columns, which projection-v6 renamed: _ast and nodes are keyed by node_id/id on v4 and by nid on v6 (mache-90cb9f).
type ActionResult ¶ added in v0.5.0
type ActionResult struct {
NodeID string `json:"node_id"` // mache ID of the acted-upon node
Action string `json:"action"` // "click", "type", "enter", "focus"
Path string `json:"path"` // filesystem path
Payload string `json:"payload,omitempty"` // optional (e.g., typed text)
}
ActionResult is returned when an action is performed on a graph node. Used by interactive graphs (browser DOM, iTerm2 sessions, macOS AX elements).
type ArenaFlusher ¶ added in v0.22.0
type ArenaFlusher struct {
// contains filtered or unexported fields
}
ArenaFlusher writes a serialized .db file into the double-buffered arena and atomically flips the header so readers see the new version.
Flush is O(N) where N = DB size — the entire .db is copied to the inactive buffer on every flush. To amortize this cost, callers should use RequestFlush (coalesced) instead of FlushNow (synchronous) for writes. The coalescing goroutine batches rapid writes into a single flush per tick interval (default 100ms), so N concurrent agent writes within a tick produce 1 flush instead of N.
Benchmarked on M3 Max (BenchmarkArenaFlush):
108 KB → ~4ms (fsync-dominated floor) 1 MB → ~5ms 10 MB → ~10-25ms
Flush uses page-level diff: only changed SQLite pages are written to the inactive buffer, avoiding a full-DB copy on every swap.
func NewArenaFlusher ¶ added in v0.22.0
func NewArenaFlusher(arenaPath, masterDBPath string, ctrl *control.Controller) *ArenaFlusher
NewArenaFlusher creates a flusher that targets the given arena file and updates the control block on each flush. The masterDBPath is the writable temp file that WritableGraph mutates.
Call Start() to begin the coalescing goroutine, and Close() to stop it and perform a final flush.
func (*ArenaFlusher) Close ¶ added in v0.22.0
func (f *ArenaFlusher) Close() error
Close stops the coalescing goroutine and performs a final synchronous flush if dirty.
func (*ArenaFlusher) FlushNow ¶ added in v0.22.0
func (f *ArenaFlusher) FlushNow() error
FlushNow performs a synchronous flush. Use for final flush on unmount or when the caller needs to guarantee the arena is up-to-date.
func (*ArenaFlusher) RequestFlush ¶ added in v0.22.0
func (f *ArenaFlusher) RequestFlush()
RequestFlush marks the flusher as dirty. The coalescing goroutine will perform the actual flush on the next tick. Non-blocking, O(1).
func (*ArenaFlusher) Start ¶ added in v0.22.0
func (f *ArenaFlusher) Start(interval time.Duration)
Start begins the coalescing goroutine that flushes at most once per interval when dirty. Safe to call multiple times (idempotent).
type ArenaHeader ¶ added in v0.22.0
type ArenaHeader struct {
Magic uint32
Version uint8
ActiveBuffer uint8
Padding [2]byte
Sequence uint64
// DataSize is the exact byte length of the live payload in the
// active buffer. Lets a reader hash `buf[..DataSize]` and compare
// against the controller's current_root without parsing the
// payload's own format (e.g. SQLite page count).
DataSize uint64
}
ArenaHeader mirrors `rs/ll-core/core/src/layout.rs::ArenaHeader` in LLO. Byte layout: magic u32 | version u8 | active_buffer u8 | padding [2]u8 | sequence u64 | data_size u64 — total 24 bytes, aligned to a 4096-byte page.
func ReadArenaHeader ¶ added in v0.22.0
func ReadArenaHeader(f *os.File) (*ArenaHeader, error)
ReadArenaHeader reads the on-disk header from the file.
func (*ArenaHeader) CalculateActiveOffset ¶ added in v0.22.0
func (h *ArenaHeader) CalculateActiveOffset(fileSize int64) (int64, error)
CalculateArenaOffset returns the byte offset of the active buffer.
type CallExtractor ¶
type CallExtractor func(content []byte, path, langName string) ([]QualifiedCall, error)
CallExtractor parses source code and returns qualified function call tokens. Used for on-demand "callees/" resolution. langName is the tree-sitter language identifier (e.g. "go", "python").
type Class ¶ added in v0.22.0
type Class struct {
ID int
Label string // emergent: most-referenced token among members
Members []string // constituent node IDs
InternalW float64 // total internal edge weight (density measure)
}
Class is an equivalence class of nodes (a community collapsed into one unit).
type Community ¶ added in v0.22.0
type Community struct {
ID int // Community identifier
Members []string // Node IDs belonging to this community
}
Community represents a cluster of densely-connected nodes detected by Louvain.
type CommunityResult ¶ added in v0.22.0
type CommunityResult struct {
Communities []Community // Detected communities, sorted by size descending
Membership map[string]int // Node ID → community ID
Modularity float64 // Final modularity score (0 to 1, higher = better partition)
NumNodes int // Total nodes in the graph
NumEdges int // Total edges (undirected)
// PrunedTokens are hub tokens excluded from the projection, most-referenced
// first. Reported rather than dropped silently: a caller reading these
// communities is entitled to know which references did not shape them.
PrunedTokens []PrunedToken
// EffectiveMaxFanIn is the cap actually applied. It differs from the
// requested one when the pair budget forced a tighter cap — reported so a
// caller can tell "these are your communities" from "these are your
// communities under a budget I had to impose".
EffectiveMaxFanIn int
// ProjectedPairs is the edge-insertion count the projection performed.
ProjectedPairs int
}
CommunityResult holds the output of community detection.
func DetectCommunities ¶ added in v0.22.0
func DetectCommunities(refs map[string][]string, minCommunitySize int) *CommunityResult
DetectCommunities runs Louvain community detection on a bipartite refs graph. Input: refs maps token → []nodeID (which nodes reference that token). The algorithm projects this into a unipartite graph where two nodes are connected if they share at least one token, with edge weight = number of shared tokens. Returns communities of nodes that are densely co-referencing.
minCommunitySize filters out communities smaller than this (default 2 if 0).
func DetectCommunitiesWithFanIn ¶ added in v0.22.0
func DetectCommunitiesWithFanIn(refs map[string][]string, minCommunitySize, maxFanIn int) *CommunityResult
DetectCommunitiesWithFanIn is DetectCommunities with an explicit hub cap. maxFanIn <= 0 disables pruning and restores the pre-pruning behaviour, which is quadratic in the hottest token's fan-in — see DefaultMaxFanIn.
type CompositeGraph ¶ added in v0.5.0
type CompositeGraph struct {
// contains filtered or unexported fields
}
CompositeGraph multiplexes multiple Graph backends under path prefixes. Mount "browser" → paths under /browser/ route to that sub-graph. Mount "iterm" → paths under /iterm/ route to that sub-graph. Root ListChildren returns the list of mount point names.
func NewCompositeGraph ¶ added in v0.5.0
func NewCompositeGraph() *CompositeGraph
NewCompositeGraph creates an empty composite graph.
func (*CompositeGraph) Act ¶ added in v0.22.0
func (c *CompositeGraph) Act(id, action, payload string) (*ActionResult, error)
Act implements Graph. Routes to the appropriate sub-graph.
func (*CompositeGraph) DefsMap ¶ added in v0.22.0
func (c *CompositeGraph) DefsMap() map[string][]string
DefsMap aggregates token → dir IDs across every mount that implements DefsMap(), prefixing each dir ID with its mount name. Sub-graphs that don't expose a defs index are skipped.
Lets find_definition and search work correctly on a composite graph: a token defined in mount A and mount B returns ["A/path/to/def", "B/path/to/def"] — agents see all definitions across mounts in one query.
func (*CompositeGraph) GetCallees ¶ added in v0.22.0
func (c *CompositeGraph) GetCallees(id string) ([]*Node, error)
GetCallees implements Graph. Routes to the local mount for in-mount resolution; if a cross-mount CallExtractor is configured (via SetCallExtractor), additionally resolves each extracted call against the federated DefsMap so callees living in OTHER mounts surface in the response. Local results win on dedupe; cross-mount results supplement.
func (*CompositeGraph) GetCallers ¶ added in v0.22.0
func (c *CompositeGraph) GetCallers(token string) ([]*Node, error)
GetCallers implements Graph. Searches all mounted sub-graphs.
func (*CompositeGraph) GetNode ¶ added in v0.22.0
func (c *CompositeGraph) GetNode(id string) (*Node, error)
GetNode implements Graph.
func (*CompositeGraph) Invalidate ¶ added in v0.22.0
func (c *CompositeGraph) Invalidate(id string)
Invalidate implements Graph.
func (*CompositeGraph) ListChildStats ¶ added in v0.22.0
func (c *CompositeGraph) ListChildStats(id string) ([]NodeStat, error)
ListChildStats implements Graph.
func (*CompositeGraph) ListChildren ¶ added in v0.22.0
func (c *CompositeGraph) ListChildren(id string) ([]string, error)
ListChildren implements Graph.
func (*CompositeGraph) LookupDef ¶ added in v0.22.0
func (c *CompositeGraph) LookupDef(token string) []string
LookupDef federates LookupDef across mounts: returns all dir IDs across every mount that defines `token`, prefixed with the mount name. Cheaper than DefsMap when only one token is wanted — MemoryStore + SQLiteGraph both expose LookupDef as O(1).
func (*CompositeGraph) Mount ¶ added in v0.22.0
func (c *CompositeGraph) Mount(prefix string, g Graph) error
Mount registers a sub-graph under the given prefix. Paths like "/<prefix>/..." are routed to this graph with the prefix stripped.
func (*CompositeGraph) MountPrefixOf ¶ added in v0.22.0
func (c *CompositeGraph) MountPrefixOf(id string) string
MountPrefixOf returns the mount-name prefix that would route id to a mounted sub-graph, or "" if id doesn't resolve to any mount (the virtual root or an unknown prefix).
MCP handlers use this to annotate cross-repo results with their mount of origin without having to parse node IDs themselves.
func (*CompositeGraph) ReadContent ¶ added in v0.22.0
ReadContent implements Graph.
func (*CompositeGraph) SearchDefs ¶ added in v0.22.0
func (c *CompositeGraph) SearchDefs(pattern string, limit int) map[string][]string
SearchDefs federates SearchDefs across every mount that implements it, then aggregates the results with mount-name prefixes applied to each node_id. Sub-graphs that don't expose SearchDefs (only DefsMap) fall back to a DefsMap iteration with sqlLikeMatch.
Without this method, find_definition / search role=definition on a CompositeGraph mounting an SQL-backed graph would silently return [] — SQLiteGraph's in-memory g.defs is empty for pre-built .db files, so aggregating DefsMap across mounts misses the real data that lives in node_defs. Each mount's SearchDefs handles its own SQL-vs-memory dispatch; this federates the results.
Bead from PR #373 post-fix audit: lazyGraph→CompositeGraph→ SQLiteGraph chain. lazyGraph satisfied the defsSearcher type assertion in the handler, but its inner (CompositeGraph) didn't implement SearchDefs, so lazyGraph returned nil. Handler fell through to DefsMap. CompositeGraph.DefsMap aggregated empty SQLiteGraph.DefsMaps. Result: [] for every pattern.
func (*CompositeGraph) SetCallExtractor ¶ added in v0.22.0
func (c *CompositeGraph) SetCallExtractor(fn CallExtractor)
SetCallExtractor wires a tree-sitter call extractor onto the composite for cross-mount callees resolution. With nil or no extractor set, GetCallees only consults the local mount's defs index — sub-graphs that already have their own extractor still resolve in-mount callees as before.
func (*CompositeGraph) SetCallExtractorPicker ¶ added in v0.22.0
func (c *CompositeGraph) SetCallExtractorPicker(picker func(local Graph) CallExtractor)
SetCallExtractorPicker wires a per-mount extractor selector. At extract time, GetCallees calls picker(localMount); a non-nil return value wins over the global extractor set via SetCallExtractor. Used to dispatch between CGO and pure-Go extractors based on the local mount's capabilities (e.g. presence of an `_ast` table). See ADR-0012.
Composing both: the picker is consulted first; if it returns nil, fall through to the SetCallExtractor-supplied global extractor. Callers that don't want per-mount dispatch can leave this unset.
func (*CompositeGraph) Unmount ¶ added in v0.22.0
func (c *CompositeGraph) Unmount(prefix string) error
Unmount removes a previously mounted sub-graph.
type ContentCache ¶ added in v0.22.0
type ContentCache struct {
// contains filtered or unexported fields
}
ContentCache is a FIFO-bounded cache for resolved content bytes. Uses RWMutex so concurrent readers (MCP tool calls) don't block each other. Replaces the inline sync.Mutex caches that SQLiteGraph and WritableGraph previously maintained — the RWMutex promotion is safe because the old code never relied on exclusive read access (check-then-set unlocked between steps). Shared by MemoryStore, SQLiteGraph, and WritableGraph.
Note: the get→miss→resolve→put sequence in resolveContent is not atomic. Under high concurrency, multiple goroutines may miss the cache simultaneously and all invoke the resolver for the same key. This is benign — the first writer wins via Put's dedup check, and subsequent resolver results are discarded. The resolver (SQLite query + template render) is idempotent.
func NewContentCache ¶ added in v0.22.0
func NewContentCache(maxSize int) *ContentCache
NewContentCache creates a FIFO-bounded content cache.
func (*ContentCache) Delete ¶ added in v0.22.0
func (c *ContentCache) Delete(key string)
Delete removes a key from both the map and the keys slice. Invariant: entries and keys are always in sync — every key in the map has exactly one entry in the keys slice, maintained by Put (dedup) and Delete (linear scan + remove). The break-after-first-match in the scan is correct because Put never creates duplicate key entries.
func (*ContentCache) Get ¶ added in v0.22.0
func (c *ContentCache) Get(key string) ([]byte, bool)
Get retrieves cached content. Safe for concurrent readers.
func (*ContentCache) Put ¶ added in v0.22.0
func (c *ContentCache) Put(key string, value []byte)
Put stores content, evicting the oldest entry if at capacity. Deduplicates: if key already exists, updates the value without adding a duplicate key entry.
type ContentRef ¶
type ContentRef struct {
DBPath string // Path to the SQLite database
RecordID string // Row ID in the results table
Template string // Content template to re-render
ContentLen int64 // Pre-computed rendered byte length
}
ContentRef is a recipe for lazily resolving file content from a backing store. Instead of storing the full byte content in RAM, we store enough info to re-fetch it on demand.
type ContentResolverFunc ¶
type ContentResolverFunc func(ref *ContentRef) ([]byte, error)
ContentResolverFunc resolves a ContentRef into byte content.
type DBPathProvider ¶ added in v0.22.0
type DBPathProvider interface {
DBPath() string
}
DBPathProvider reports the path of the .db backing this graph, or "" when the backend has no file behind it.
"" is a documented sentinel meaning "no source", not an error: consumers that locate sibling artifacts by convention (ley-line-open writes a `.bindings.capnp` reference log next to the .db) must treat it as "nothing to consult" and carry on.
type DefTokener ¶ added in v0.22.0
type DefTokener interface {
// DefTokenOf returns the token node_defs records for the node at id; ok
// is false when the node defines nothing.
DefTokenOf(id string) (token string, ok bool, err error)
}
DefTokener reports the symbol a node defines: the token node_defs records for it. A node id says nothing about its symbol on a leyline projection (it ends in a tree-sitter kind), and on projection-v6 it is an integer the caller never sees, so the answer has to come from the backend.
type DefsLookuper ¶ added in v0.22.0
DefsLookuper answers "where is this exact symbol defined?" — the cheapest question on the symbol rung, and the one worth asking first. Backends implement it instead of DefsMapper wherever a single lookup can avoid snapshotting the whole index.
Returns the node IDs declaring token, or nil when nothing declares it.
type DefsMapper ¶ added in v0.22.0
DefsMapper exposes the whole symbol index as token -> node IDs.
Prefer DefsLookuper or DefsSearcher: this copies the entire index, which is the expensive way to answer a question about one symbol. It earns its keep for whole-index work — building an overview, detecting duplicates.
type DefsNodeLookuper ¶ added in v0.22.0
DefsNodeLookuper is DefsLookuper's node-returning twin, for consumers that want the definition itself rather than its id.
Both exist deliberately. Ids are the right answer when the caller only needs identity — resolving nodes it will discard is waste — but returning ids from one accessor while Graph.GetCallers and Graph.GetCallees return nodes left every consumer writing the same conversion. That asymmetry is what drove downstream wrappers around this surface.
type DefsSearcher ¶ added in v0.22.0
DefsSearcher answers "what symbols look like this?" using SQL LIKE syntax ('%' matches any run, '_' a single character), capped at limit. The step between knowing a name and knowing only its shape.
type ErrGraphNotCached ¶ added in v0.22.0
type ErrGraphNotCached struct {
Repo string
}
ErrGraphNotCached is returned by Get/Head when no graph exists for the repo.
func (*ErrGraphNotCached) Error ¶ added in v0.22.0
func (e *ErrGraphNotCached) Error() string
type Granularity ¶ added in v0.22.0
type Granularity string
Granularity selects the cell size community detection runs over.
The filesystem hierarchy is already encoded in every node id — `cmd/serve.go/function_declaration_1/block/...` IS fs→AST — so coarser cells are a pure prefix operation on the ids mache already has. No re-ingestion, no second index.
Why this exists: at construct granularity the projection runs over every AST node, so on mache itself the largest communities were things like `CHANGELOG.md/section/...` and individual test-function bodies — bounded and well-separated (modularity 0.985) but useless for "how should cmd/ be split". That question is ABOUT files and directories, so the cells must be files or directories.
const ( // GranularityConstruct is the raw projection over AST-level node ids. GranularityConstruct Granularity = "construct" // GranularityFile aggregates every node to its source file. GranularityFile Granularity = "file" // GranularityDir aggregates every node to its file's directory. GranularityDir Granularity = "dir" )
type Graph ¶
type Graph interface {
GetNode(id string) (*Node, error)
ListChildren(id string) ([]string, error)
// ListChildStats returns stat snapshots for all children of a directory.
// Eliminates N individual GetNode calls during FUSE/NFS readdir.
// Returns []NodeStat (value types, safe to read without a lock).
ListChildStats(id string) ([]NodeStat, error)
ReadContent(id string, buf []byte, offset int64) (int, error)
GetCallers(token string) ([]*Node, error)
GetCallees(id string) ([]*Node, error)
// Invalidate evicts cached data for a node (size, content).
// Called after write-back to force re-render on next access.
Invalidate(id string)
// Act performs an action on the node at the given path.
// Interactive graphs (browser DOM, terminal sessions, macOS AX elements)
// implement real actions. Passive graphs (code, data) return ErrActNotSupported.
Act(id, action, payload string) (*ActionResult, error)
}
Graph is the interface for the FUSE layer. This allows us to swap the backend later (Memory -> SQLite -> Mmap).
type GraphCache ¶ added in v0.6.0
type GraphCache struct {
// contains filtered or unexported fields
}
GraphCache is a thread-safe MemoryStore with automatic SQLite write-through persistence. It provides tree manipulation helpers for building and querying hierarchical node structures.
Mutations acquire the write lock, modify the underlying MemoryStore, then call ExportSQLite to flush the entire graph to disk. On construction, ImportSQLite loads any previously persisted state.
Domain-specific logic (fingerprint matching, eviction policies, custom node layouts) is NOT part of GraphCache. Consumers build those on top using the tree manipulation primitives and the Store() escape hatch.
func NewGraphCache ¶ added in v0.6.0
func NewGraphCache(dbPath string) *GraphCache
NewGraphCache creates a GraphCache backed by SQLite at dbPath. If dbPath is empty, the cache is purely in-memory (suitable for tests). If dbPath points to an existing SQLite file, the previously persisted graph is loaded via ImportSQLite.
func (*GraphCache) AppendChild ¶ added in v0.6.0
func (c *GraphCache) AppendChild(parentID, childID string) bool
AppendChild adds childID to the Children list of parentID if not already present. Returns false if the parent does not exist.
Copy-on-write: publishes a new *Node into the store rather than mutating the live parent in place, so readers holding a *Node from GetNode see an immutable snapshot.
func (*GraphCache) Batch ¶ added in v0.6.0
func (c *GraphCache) Batch(fn func(*MemoryStore)) error
Batch acquires the write lock, calls fn with the underlying MemoryStore, then persists once. Use this for multi-step mutations that must be atomic (no concurrent readers see intermediate state) and persist as a single SQLite write instead of N individual writes.
This is the correct way to implement complex operations like "backup sections, clear zones, rebuild zones, restore matching sections" — the entire sequence runs under one lock with one persist at the end.
func (*GraphCache) ClearChildren ¶ added in v0.6.0
func (c *GraphCache) ClearChildren(id string) bool
ClearChildren sets the Children list of a directory node to empty. Returns false if the node does not exist. Combined with RemoveChild on the parent, this effectively "deletes" a subtree: ExportSQLite only walks from roots, so orphaned nodes are not persisted.
Copy-on-write: see AppendChild.
func (*GraphCache) Close ¶ added in v0.6.0
func (c *GraphCache) Close() error
Close is a no-op. ExportSQLite manages its own DB connections per call.
func (*GraphCache) GetData ¶ added in v0.6.0
func (c *GraphCache) GetData(id string) ([]byte, bool)
GetData returns a copy of the Data field of a file node, or (nil, false) if the node does not exist or has no data. The returned slice is detached from the live store.
func (*GraphCache) GetNode ¶ added in v0.6.0
func (c *GraphCache) GetNode(id string) (*Node, bool)
GetNode returns a shallow copy of the node at the given id, or (nil, false) if not found. The returned *Node's Children and Data slices are detached from the live store so the caller may read them without holding the cache lock and without racing against concurrent mutations.
func (*GraphCache) ListChildren ¶ added in v0.6.0
func (c *GraphCache) ListChildren(id string) ([]string, bool)
ListChildren returns a copy of the Children of a directory node, or (nil, false) if the node does not exist or is not a directory. The returned slice is detached from the live store.
func (*GraphCache) Persist ¶ added in v0.6.0
func (c *GraphCache) Persist() error
Persist flushes the entire graph to SQLite. Called automatically by mutation methods. Use explicitly after direct Store() access.
func (*GraphCache) PutDir ¶ added in v0.6.0
func (c *GraphCache) PutDir(id string, isRoot bool) bool
PutDir ensures a directory node exists at the given id. If isRoot is true and the node is new, it is registered as a root node. Returns true if a new node was created.
func (*GraphCache) PutFile ¶ added in v0.6.0
func (c *GraphCache) PutFile(id string, data []byte)
PutFile creates or overwrites a file node with the given data. The node's ModTime is set to time.Now().
func (*GraphCache) RemoveChild ¶ added in v0.6.0
func (c *GraphCache) RemoveChild(parentID, childID string) bool
RemoveChild removes childID from the Children list of parentID. Returns false if the parent does not exist.
Copy-on-write: see AppendChild.
func (*GraphCache) RootIDs ¶ added in v0.6.0
func (c *GraphCache) RootIDs() []string
RootIDs returns all root node IDs.
func (*GraphCache) Store ¶ added in v0.6.0
func (c *GraphCache) Store() *MemoryStore
Store returns the underlying MemoryStore for direct access. Use this for batch operations: mutate the store directly, then call Persist() once. Callers are responsible for coordinating with GraphCache.mu if mixing direct store access with GraphCache mutation methods.
type GraphMeta ¶ added in v0.22.0
type GraphMeta struct {
Repo string // e.g., "github.com/org/repo"
Generation uint64 // monotonic version counter
Size int64 // .db file size in bytes
ModTime time.Time // last update time
ETag string // content hash for conditional gets (optional)
}
GraphMeta is metadata about a persisted graph.
type GraphStore ¶ added in v0.22.0
type GraphStore interface {
// Put persists a graph database for the given repo.
// The reader provides the SQLite .db bytes. Generation is an
// opaque version counter (monotonically increasing).
Put(ctx context.Context, repo string, generation uint64, r io.Reader) error
// Get retrieves the latest graph database for the given repo.
// Returns the reader, generation, and modification time.
// Returns ErrGraphNotCached if no graph exists for this repo.
Get(ctx context.Context, repo string) (io.ReadCloser, uint64, error)
// Head returns metadata without fetching the full database.
// Used for cache validation (is my local copy still current?).
Head(ctx context.Context, repo string) (*GraphMeta, error)
// Delete removes a persisted graph for the given repo.
Delete(ctx context.Context, repo string) error
// List returns all repos with persisted graphs.
// Use for garbage collection and admin tooling.
List(ctx context.Context) ([]GraphMeta, error)
}
GraphStore persists and retrieves projected graph databases. The key is a repo identifier (e.g., "github.com/org/repo") and the value is a SQLite .db file containing the nodes/refs/defs tables.
type HotSwapGraph ¶ added in v0.5.2
type HotSwapGraph struct {
// contains filtered or unexported fields
}
HotSwapGraph is a thread-safe wrapper that allows swapping the underlying graph instance.
func NewHotSwapGraph ¶ added in v0.5.2
func NewHotSwapGraph(initial Graph) *HotSwapGraph
func (*HotSwapGraph) Act ¶ added in v0.22.0
func (h *HotSwapGraph) Act(id, action, payload string) (*ActionResult, error)
Act delegates to current graph.
func (*HotSwapGraph) GetCallees ¶ added in v0.22.0
func (h *HotSwapGraph) GetCallees(id string) ([]*Node, error)
GetCallees delegates to current graph.
func (*HotSwapGraph) GetCallers ¶ added in v0.22.0
func (h *HotSwapGraph) GetCallers(token string) ([]*Node, error)
GetCallers delegates to current graph.
func (*HotSwapGraph) GetNode ¶ added in v0.22.0
func (h *HotSwapGraph) GetNode(id string) (*Node, error)
GetNode delegates to current graph.
func (*HotSwapGraph) Invalidate ¶ added in v0.22.0
func (h *HotSwapGraph) Invalidate(id string)
Invalidate delegates to current graph.
func (*HotSwapGraph) ListChildStats ¶ added in v0.22.0
func (h *HotSwapGraph) ListChildStats(id string) ([]NodeStat, error)
ListChildStats delegates to current graph.
func (*HotSwapGraph) ListChildren ¶ added in v0.22.0
func (h *HotSwapGraph) ListChildren(id string) ([]string, error)
ListChildren delegates to current graph.
func (*HotSwapGraph) ReadContent ¶ added in v0.22.0
ReadContent delegates to current graph.
func (*HotSwapGraph) Swap ¶ added in v0.22.0
func (h *HotSwapGraph) Swap(newGraph Graph)
Swap atomically replaces the current graph with a new one. It closes the old graph (if it implements Closer, though Graph interface doesn't enforce it). Ideally, Graph implementations should be safe to close.
type IndexStaleness ¶ added in v0.22.0
type IndexStaleness struct {
// BuiltAt is when the index was produced.
BuiltAt time.Time `json:"built_at"`
// SourceRoot is the tree it was built from.
SourceRoot string `json:"source_root"`
// ModifiedSince is how many source files changed after BuiltAt, capped at
// staleScanCap — the point is "should I care", not a manifest.
ModifiedSince int `json:"modified_since"`
// DeletedSince counts indexed source files that no longer exist on disk.
// Exact, not heuristic: the .db records every file it indexed
// (nodes.source_file), so a missing path is a fact. A rename counts here
// too — the indexed path IS gone, and the mtime walk cannot see it
// (mv preserves mtimes, so the new path does not read as modified
// either; without this field a rename was doubly invisible).
DeletedSince int `json:"deleted_since,omitempty"`
// Capped reports that a count hit the cap and the true number is larger.
Capped bool `json:"capped,omitempty"`
}
IndexStaleness is how far a projected graph has drifted from the tree it was built from — the honest answer to "can I trust this index?".
Why it exists (mache-6c9e1d): the flagship serve path parses the repo ONCE at session start and serves that frozen .db for the session's whole life. Every edit after that is invisible to search/find_definition, and the longer and more productive a session, the wronger the answers — with nothing anywhere saying so. Until the live-reparse path exists, the warn-don't-lie floor is to REPORT the drift.
type LocalStore ¶ added in v0.22.0
type LocalStore struct {
BaseDir string
}
LocalStore persists graphs to a local directory. Each repo gets a subdirectory: {base}/{repo-hash}/graph.db
func NewLocalStore ¶ added in v0.22.0
func NewLocalStore(baseDir string) *LocalStore
NewLocalStore creates a LocalStore backed by the given directory.
func (*LocalStore) Delete ¶ added in v0.22.0
func (s *LocalStore) Delete(_ context.Context, repo string) error
Delete removes a persisted graph from local disk.
func (*LocalStore) Get ¶ added in v0.22.0
func (s *LocalStore) Get(_ context.Context, repo string) (io.ReadCloser, uint64, error)
Get retrieves the graph database from local disk.
type MemoryStore ¶
type MemoryStore struct {
// Diagnostics: last write status per node path (for _diagnostics/ virtual dir).
WriteStatus sync.Map // node path (string) → error message (string)
// contains filtered or unexported fields
}
func ImportSQLite ¶ added in v0.4.0
func ImportSQLite(dbPath string) (*MemoryStore, error)
ImportSQLite reads nodes from a SQLite database into a new MemoryStore. The database must have a nodes table in the standard mache format.
This ONLY replicates the node tree (GetNode/ListChildren/ReadContent work on the result). It does not read node_defs/node_refs, so the returned MemoryStore's LookupDef returns nil for every token and QueryRefs errors "refsDB not initialized" — those indices live in MemoryStore's own AddDef/AddRef/InitRefsDB+FlushRefs machinery, which this function never calls. For a mache-produced .db (mache build / leyline parse) where you need LookupDef/QueryRefs/GetCallers working, use Open instead: it opens a *SQLiteGraph, which answers those directly against the file's own node_defs/node_refs tables with no import step at all.
func NewMemoryStore ¶
func NewMemoryStore() *MemoryStore
func (*MemoryStore) Act ¶ added in v0.22.0
func (s *MemoryStore) Act(id, action, payload string) (*ActionResult, error)
Act returns ErrActNotSupported — MemoryStore is a passive code graph.
func (*MemoryStore) AddDef ¶ added in v0.22.0
func (s *MemoryStore) AddDef(token, dirID string) error
AddDef records that a construct (dirID) defines the given token. Used by callees/ resolution: token → where it is defined. Uses copy-on-write: creates a new slice instead of appending to the existing one, so concurrent readers (GetCallees holds RLock) never see a partially-updated slice.
func (*MemoryStore) AddFileChildren ¶ added in v0.22.0
func (s *MemoryStore) AddFileChildren(parent *Node, files []*Node)
AddFileChildren atomically adds file nodes and appends their IDs to the parent directory's Children slice. Single lock acquisition for the batch.
Copy-on-write: publishes a NEW *Node into the map rather than mutating the caller's parent in place. Readers that obtained the previous *Node pointer from GetNode see an immutable snapshot of the old Children slice; subsequent GetNode callers see the updated node. This avoids a data race where a reader holds a *Node pointer (lock released) while a writer appends to the same Children slice.
Side effect: the caller's `parent` pointer is stale after this call — further mutations via that pointer are not reflected in the store.
func (*MemoryStore) AddNode ¶ added in v0.22.0
func (s *MemoryStore) AddNode(n *Node)
AddNode adds a non-root node to the store.
func (*MemoryStore) AddRef ¶ added in v0.22.0
func (s *MemoryStore) AddRef(token, nodeID string) error
AddRef records a reference from a file (nodeID) to a token.
func (*MemoryStore) AddRoot ¶ added in v0.22.0
func (s *MemoryStore) AddRoot(n *Node)
AddRoot registers a node as a top-level root and adds it to the store. Callers must explicitly declare roots — there is no heuristic.
func (*MemoryStore) Close ¶ added in v0.22.0
func (s *MemoryStore) Close() error
Close closes the refs database and removes the temp file.
func (*MemoryStore) DefsMap ¶ added in v0.22.0
func (s *MemoryStore) DefsMap() map[string][]string
DefsMap returns a snapshot of the token→dirIDs definition map. Used by find_definition's case-insensitive and fuzzy paths and by search role=definition — anywhere callers need to iterate. The anchored-exact path in find_definition should use LookupDef instead to avoid the O(N) snapshot copy.
Memoized — see RefsMap for the cache contract. Same read-only constraint applies. Callers that need a mutable copy should either deep-copy the result themselves or use LookupDef for per-token lookups.
func (*MemoryStore) DeleteFileNodes ¶ added in v0.22.0
func (s *MemoryStore) DeleteFileNodes(filePath string)
DeleteFileNodes removes all nodes that originated from the given source file. Uses the roaring bitmap index for O(k) lookup instead of O(N) full scan.
func (*MemoryStore) DeleteNodes ¶ added in v0.22.0
func (s *MemoryStore) DeleteNodes(ids []string)
DeleteNodes removes the named nodes and everything that pointed at them, whatever file they came from.
DeleteFileNodes cannot do this job. It finds nodes through the file→nodes bitmap, which indexNode only populates for nodes carrying an Origin — and a CONSTRUCT DIRECTORY has no Origin, only its source leaf does. So a re-ingest replaced `fns/alpha/source` and left `fns/alpha` behind as an empty husk, forever, one per edit (mache-399c25). The engine knows exactly which construct IDs a file claimed; this is how it hands them back.
func (*MemoryStore) FileMtime ¶ added in v0.22.0
func (s *MemoryStore) FileMtime(filePath string) time.Time
FileMtime returns the tracked mtime for a source file. Returns zero time if the file is not tracked.
func (*MemoryStore) FlushRefs ¶ added in v0.22.0
func (s *MemoryStore) FlushRefs() error
FlushRefs writes all accumulated refs (from AddRef) into the in-memory SQLite sidecar as roaring bitmaps. Guarded by sync.Once — safe to call multiple times; only the first call performs the flush.
func (*MemoryStore) ForgetFile ¶ added in v0.22.0
func (s *MemoryStore) ForgetFile(filePath string)
ForgetFile drops path's tracked mtime, so a later incremental pass does not treat a file that is gone as merely unchanged. The counterpart of RecordFileMtime; the nodes themselves come out through DeleteFileNodes and DeleteNodes (mache-31abc0).
func (*MemoryStore) GetCallees ¶ added in v0.22.0
func (s *MemoryStore) GetCallees(id string) ([]*Node, error)
GetCallees implements Graph. It extracts calls made by the construct, then looks up those tokens in the defs index to find definitions.
Three extraction paths are supported:
- AST-scoped (preferred): when the construct's Properties carry "ast_source_id"/"ast_scope_id" (persisted at projection time by engine_walk.go — see ingest.ASTScope) AND a scoped extractor is wired (requires a LIVE `_ast` table), GetCallees queries the pre-parsed `_ast` table directly, scoped to this construct. This is the fix for bead mache-fd9982: the legacy path below fed the GRAPH node id (e.g. "cmd/functions/evalOrAbs") as if it were the `_ast` source_id, which matched zero rows every time.
- node_refs fallback: when #1 is unavailable but the construct has a "source" file child, resolve its calls from s.refs (token -> []nodeID) instead — the same index GetCallers reads, inverted. s.refs already holds every call token ScopeCalls found for this construct at projection time (engine_walk.go's store.AddRef(token, sourceFileID)). Bare-token only (no qualifier fidelity) — strictly better than returning [] (bead mache-6fbaf1).
- Legacy content-based extractor: re-parses the construct's "source" file content. Kept for non-AST backends (or .dbs projected before this fix that lack the ast_source_id/ast_scope_id Properties AND have no node_refs coverage).
func (*MemoryStore) GetCallers ¶ added in v0.22.0
func (s *MemoryStore) GetCallers(token string) ([]*Node, error)
GetCallers implements Graph.
func (*MemoryStore) GetNode ¶ added in v0.22.0
func (s *MemoryStore) GetNode(id string) (*Node, error)
GetNode implements Graph.
func (*MemoryStore) InitRefsDB ¶ added in v0.22.0
func (s *MemoryStore) InitRefsDB() error
InitRefsDB opens an in-memory SQLite database with the same schema as SQLiteGraph's sidecar (node_refs + file_ids + mache_refs vtab). Must be called before FlushRefs. Safe to call multiple times (idempotent).
func (*MemoryStore) Invalidate ¶ added in v0.22.0
func (s *MemoryStore) Invalidate(id string)
Invalidate is a no-op for MemoryStore — nodes are updated in-place.
func (*MemoryStore) IsFileStale ¶ added in v0.22.0
func (s *MemoryStore) IsFileStale(filePath string) bool
IsFileStale returns true if the source file's current mtime differs from the tracked mtime (i.e., the file has been modified since indexing).
func (*MemoryStore) ListChildStats ¶ added in v0.22.0
func (s *MemoryStore) ListChildStats(id string) ([]NodeStat, error)
ListChildStats returns stat snapshots for all children under a single RLock. Returns []NodeStat (value types, no aliasing) — safe to read without a lock. Eliminates N individual GetNode calls during FUSE/NFS readdir. Missing children are silently skipped.
func (*MemoryStore) ListChildren ¶ added in v0.22.0
func (s *MemoryStore) ListChildren(id string) ([]string, error)
ListChildren implements Graph.
func (*MemoryStore) LookupDef ¶ added in v0.22.0
func (s *MemoryStore) LookupDef(token string) []string
LookupDef returns the dir IDs that define `token` without snapshotting the entire defs map. Returns nil for unknown tokens. O(1) under the read lock; the returned slice is a copy so callers can mutate it without races. Pair with DefsMap when the caller needs to iterate (case-insensitive matching, fuzzy substring, etc.); use this when one specific token's IDs are wanted.
func (*MemoryStore) NodesForPath ¶ added in v0.22.0
func (s *MemoryStore) NodesForPath(filePath string) []string
NodesForPath returns the IDs of every node whose origin file is filePath, in deterministic order. The path is canonicalised via filepath.EvalSymlinks to match the bookkeeping Ingest does. Returns an empty slice when no nodes are indexed under that path.
O(k) via the file→nodes bitmap; falls back to a full O(N) scan only when a path was added before the bitmap was populated (the same fallback path DeleteFileNodes uses, kept for parity).
Used by the file-watcher → SheafInvalidator wiring (cmd/serve.go) so the sheaf cascade has a node ID to look up the region for. Exposed via the NodesForPathProvider interface so non-MemoryStore backends can plug in without leaking *MemoryStore type assertions into serve.go.
func (*MemoryStore) QueryRefs ¶ added in v0.22.0
QueryRefs executes a SQL query against the in-memory refs database, which includes the mache_refs virtual table.
func (*MemoryStore) ReadContent ¶ added in v0.22.0
ReadContent implements Graph. It handles both inline and lazy content. If the node has a SourceOrigin and the source file's mtime has changed, the refresher callback is invoked to re-ingest the file before reading.
func (*MemoryStore) RecordFileMtime ¶ added in v0.22.0
func (s *MemoryStore) RecordFileMtime(filePath string, mtime time.Time)
RecordFileMtime explicitly records the mtime for a source file. Called after re-ingestion to update the tracked mtime.
func (*MemoryStore) RefsMap ¶ added in v0.22.0
func (s *MemoryStore) RefsMap() map[string][]string
RefsMap returns a snapshot of the token→nodeIDs reference map. Used by community detection to build the co-reference graph.
Memoized: the first call after an invalidation builds the deep- copy snapshot; subsequent calls return the same instance until the next AddRef / DeleteFileNodes. Callers MUST treat the returned map as read-only — mutating it corrupts the cache for the next caller. Every existing caller (community detection, composite graph wiring) is read-only.
func (*MemoryStore) ReplaceFileNodes ¶ added in v0.22.0
func (s *MemoryStore) ReplaceFileNodes(filePath string, newNodes []*Node)
ReplaceFileNodes atomically replaces all nodes from a file with a new set. This prevents race conditions where files disappear during re-ingestion.
func (*MemoryStore) RootIDs ¶ added in v0.22.0
func (s *MemoryStore) RootIDs() []string
RootIDs returns a copy of the top-level root node IDs.
func (*MemoryStore) SearchDefs ¶ added in v0.22.0
func (s *MemoryStore) SearchDefs(pattern string, limit int) map[string][]string
SearchDefs returns up to `limit` token→nodeIDs entries whose token matches the SQL LIKE pattern. Mirrors SQLiteGraph.SearchDefs for dispatch uniformity — every backend that has a defs map should also be searchable, so the handler's defsSearcher check resolves the same regardless of backend (bead from PR #373 post-fix audit: lazyGraph wrapping a MemoryStore previously returned nil from SearchDefs because MemoryStore didn't implement it, and the handler's defsMapProvider fallback was unreachable).
func (*MemoryStore) SearchRefs ¶ added in v0.22.0
func (s *MemoryStore) SearchRefs(pattern, segment string, limit int) ([]RefHit, error)
SearchRefs implements RefSearcher over the mache_refs sidecar.
func (*MemoryStore) SetCallExtractor ¶ added in v0.22.0
func (s *MemoryStore) SetCallExtractor(fn CallExtractor)
SetCallExtractor configures the parser for on-demand callee resolution.
func (*MemoryStore) SetRefresher ¶ added in v0.22.0
func (s *MemoryStore) SetRefresher(fn func(filePath string) error)
SetRefresher configures a callback invoked when a source file is stale. The callback should re-ingest the file and update the store.
func (*MemoryStore) SetResolver ¶ added in v0.22.0
func (s *MemoryStore) SetResolver(fn ContentResolverFunc)
SetResolver configures lazy content resolution for nodes with ContentRef. Cache size scales with node count: 25% of nodes, floor 1024, ceiling 16384.
func (*MemoryStore) SetScopedCallExtractor ¶ added in v0.22.0
func (s *MemoryStore) SetScopedCallExtractor(fn ScopedCallExtractor)
SetScopedCallExtractor configures the AST-scoped extractor GetCallees prefers when a construct carries ast_source_id/ast_scope_id Properties.
func (*MemoryStore) ShiftOrigins ¶ added in v0.22.0
func (s *MemoryStore) ShiftOrigins(filePath string, afterByte uint32, delta int32)
ShiftOrigins adjusts StartByte/EndByte for all nodes from filePath whose origin starts at or after afterByte. delta is the signed byte count change (positive = content grew, negative = content shrank). Called after splice, BEFORE re-ingest, to keep sibling offsets correct.
func (*MemoryStore) UpdateNodeContent ¶ added in v0.22.0
func (s *MemoryStore) UpdateNodeContent(id string, data []byte, origin *SourceOrigin, modTime time.Time) error
UpdateNodeContent surgically updates a node's content and origin in-place. Preserves Children, Context, Properties, and Ref. Clears DraftData on success.
func (*MemoryStore) UpdateNodeContext ¶ added in v0.22.0
func (s *MemoryStore) UpdateNodeContext(id string, ctx []byte) error
UpdateNodeContext updates the Context field on a node (e.g., imports/package).
type MermaidOpts ¶ added in v0.22.0
type MermaidOpts struct {
Layout string // "TD", "LR", "BT", "RL" (default "TD")
Compact bool // omit member listings inside subgraphs
}
MermaidOpts controls diagram rendering.
type MountPrefixer ¶ added in v0.22.0
MountPrefixer reports which mount a node ID came from, or "" for the base graph — the disambiguation a composite graph needs once a single query can answer across more than one repository.
type Node ¶
type Node struct {
ID string
Mode fs.FileMode // fs.ModeDir for directories, 0 for regular files
ModTime time.Time // Modification time
Data []byte // Inline content (small files, nil for lazy nodes)
Context []byte // Context content (imports/globals, for virtual 'context' file)
DraftData []byte // Draft content (uncommitted/invalid edits)
Ref *ContentRef // Lazy content reference (large files, nil for inline nodes)
// Properties holds node metadata as JSON values (lang/pkg/imports/location/
// ast_*). Access it only via PropString/PropRaw and their setters — the
// encoding is their business (mache-90b89b).
Properties map[string]json.RawMessage
Children []string // Child node IDs (directories only)
Origin *SourceOrigin // Source byte range (nil for dirs, JSON, SQLite nodes)
}
Node is the universal primitive. The Mode field explicitly declares whether this is a file or directory.
func (*Node) ContentSize ¶ added in v0.22.0
ContentSize returns the byte length of this node's content, regardless of whether it is inline or lazy.
type NodeStat ¶ added in v0.22.0
type NodeStat struct {
ID string
IsDir bool
ContentSize int64
ModTime time.Time
HasOrigin bool // true if write-back is possible (Origin != nil)
}
NodeStat holds the immutable stat fields needed for directory listing. Value type — no pointers to mutable store data. Copying a NodeStat gives the caller a frozen snapshot that is safe to read without any lock.
type NodesForPathProvider ¶ added in v0.22.0
NodesForPathProvider is implemented by graph backends that can answer "which node IDs originated from this file?". MemoryStore implements it; other backends (SQLiteGraph, CompositeGraph) may either implement it directly or compose it via type assertion on their inner store.
Kept as a separate interface (rather than added to Graph) so consumers that don't need this query — most graph readers — aren't forced to implement it. Watcher-driven invalidation paths type-assert and degrade gracefully when the backing store doesn't satisfy the interface.
type NodesTableReader ¶ added in v0.22.0
type NodesTableReader struct {
// contains filtered or unexported fields
}
NodesTableReader provides read methods for the nodes-table schema. Shared by SQLiteGraph (nodes-table fast path) and WritableGraph.
Parameterized by FileMode/DirMode so the same SQL queries produce read-only (0o444/0o555) or writable (0o644/0o755) nodes.
The caller owns the *sql.DB lifecycle — NodesTableReader holds a reference but does not close it.
func NewNodesTableReader ¶ added in v0.22.0
func NewNodesTableReader(db *sql.DB, tableName string, render TemplateRenderer, levels []*schemaLevel, fileMode, dirMode os.FileMode, cacheSize int, ) *NodesTableReader
NewNodesTableReader creates a reader for the nodes-table schema.
func (*NodesTableReader) Close ¶ added in v0.22.0
func (r *NodesTableReader) Close()
Close releases the reader's compiled statements. It does NOT close the *sql.DB, which the caller owns; call it before closing the db so the statements are released against a live connection.
func (*NodesTableReader) DB ¶ added in v0.22.0
func (r *NodesTableReader) DB() *sql.DB
DB returns the underlying database connection. Used by WritableGraph for write operations and Close.
func (*NodesTableReader) GetCallers ¶ added in v0.22.0
func (r *NodesTableReader) GetCallers(token string) ([]*Node, error)
GetCallers returns nodes that reference the given token via node_refs table.
func (*NodesTableReader) GetNode ¶ added in v0.22.0
func (r *NodesTableReader) GetNode(id string) (*Node, error)
GetNode returns a node by ID from the nodes table.
func (*NodesTableReader) Invalidate ¶ added in v0.22.0
func (r *NodesTableReader) Invalidate(id string)
Invalidate evicts cached content and size for a node.
func (*NodesTableReader) ListChildStats ¶ added in v0.22.0
func (r *NodesTableReader) ListChildStats(id string) ([]NodeStat, error)
ListChildStats returns stat snapshots for all children without rendering content.
func (*NodesTableReader) ListChildren ¶ added in v0.22.0
func (r *NodesTableReader) ListChildren(id string) ([]string, error)
ListChildren returns child IDs for a directory from the nodes table.
Empty-result contract: when the directory has no children, the returned slice is nil (not an empty slice). All callers must handle nil correctly — len(nil) is 0, range over nil is a no-op, and JSON-encoding nil produces null. Don't change to []string{} without auditing JSON consumers (the MCP serve_handlers list_directory shapes its own non-nil response).
Bead mache-0375fe.
func (*NodesTableReader) ReadContent ¶ added in v0.22.0
ReadContent resolves content and copies into buf at offset.
type PrunedToken ¶ added in v0.22.0
PrunedToken is a token excluded for exceeding the fan-in cap.
type QualifiedCall ¶
type QualifiedCall struct {
Token string // Function/method name (e.g., "Validate")
Qualifier string // Package qualifier (e.g., "auth"); empty for unqualified calls
}
QualifiedCall represents a function call with optional package qualifier.
type QuotientEdge ¶ added in v0.22.0
type QuotientEdge struct {
From int // class index (invariant: From < To)
To int // class index
Weight float64 // aggregated cross-class coupling
Tokens []string // boundary tokens creating this edge
TokenWeights map[string]float64 // per-token contribution to Weight
}
QuotientEdge is an aggregated edge between two classes.
type QuotientGraph ¶ added in v0.22.0
type QuotientGraph struct {
Classes []Class
Edges []QuotientEdge
ClassOf map[string]int // node ID → class index
}
QuotientGraph compresses a full node graph into a diagram-scale object by collapsing nodes into equivalence classes (communities) and aggregating inter-class edges. The quotient G/P is uniquely determined by the partition P.
func ComputeQuotient ¶ added in v0.22.0
func ComputeQuotient(cr *CommunityResult, refs map[string][]string) *QuotientGraph
ComputeQuotient builds a QuotientGraph from a community partition and refs.
The algorithm mirrors buildRestrictions in sheaf.go: for each ref token referenced by nodes in multiple communities, create an inter-class edge with weight = product of member counts (bipartite projection weight).
Class labels are derived from the most-referenced token among class members. Ties are broken lexicographically for determinism.
func (*QuotientGraph) Mermaid ¶ added in v0.22.0
func (q *QuotientGraph) Mermaid(layout string) string
Mermaid renders the quotient graph as mermaid syntax.
Each Class becomes a subgraph (or a plain node if it has a single member). Each QuotientEdge becomes an arrow. Edge annotations show boundary tokens that contribute above the mean weight for that edge.
func (*QuotientGraph) MermaidWithOpts ¶ added in v0.22.0
func (q *QuotientGraph) MermaidWithOpts(opts MermaidOpts) string
MermaidWithOpts renders the quotient graph with full control over output.
type R2Store ¶ added in v0.22.0
R2Store persists graphs to Cloudflare R2. Key format: graphs/{repo-hash}/graph.db Debounce writes to avoid thrashing (caller's responsibility).
type RefHit ¶ added in v0.22.0
RefHit is one reference search result: the token referenced and the node that references it.
type RefRange ¶ added in v0.22.0
type RefRange struct {
SourceID string
StartLine uint32
StartCol uint32
EndLine uint32
EndCol uint32
StartByte uint32
EndByte uint32
}
RefRange is a reference's span in its source file. Lines and columns are 1-BASED, matching what an editor shows; the underlying `_ast` rows are tree-sitter's 0-based values and are converted here so no consumer has to remember which convention it is holding.
type RefResolution ¶ added in v0.22.0
type RefResolution string
Resolution says WHY a reference did or did not reach a definition. It exists because "no node id" has three different meanings for a consumer walking a blast radius, and collapsing them is how a tool reports the wrong radius with full confidence.
Measured on this repo's own projection (123,117 refs), the split is not close: 10.8% resolve uniquely, 13.2% are external, 17.8% are genuinely ambiguous, and 58.1% have no local definition at all. A boolean "resolved?" would put 89% of references in one undifferentiated bucket.
const ( // RefResolved: exactly one definition in this projection. Follow it. RefResolved RefResolution = "resolved" // RefExternal: the reference is qualified by an import that leaves this // projection (stdlib, a third-party module). There is deliberately no // local target, and the local blast radius is genuinely zero — this is an // ANSWER, not a failure. Matching such a token against a same-named local // definition is the bug this value exists to prevent. RefExternal RefResolution = "external" // RefAmbiguous: several definitions share the token and nothing available // narrows it. Candidates are returned; the caller decides, because only // the caller knows whether a guess is acceptable. RefAmbiguous RefResolution = "ambiguous" // RefNoTarget: no definition anywhere in the projection. Language // builtins, locals, field names — node_refs records every identifier, not // only calls to definable symbols. RefNoTarget RefResolution = "no-target" )
type RefResolver ¶ added in v0.22.0
type RefResolver interface {
// RefRangeOf reports where a reference node sits, or nil when the
// projection carries no spans.
RefRangeOf(nodeID string) (*RefRange, error)
// ResolveRef reports what a reference points at, classified.
ResolveRef(nodeID string) (*RefTarget, error)
}
RefResolver answers the two questions a reference-walking consumer asks that Graph does not: where is this reference in the file, and what definition does it point at.
Both were previously reachable only by hand-writing SQL against node_refs, _ast and _imports — the join every consumer rewrote, and the bare-token matching that cannot tell a local Join from filepath.Join. See RefTarget.Resolution for why "did not resolve" is four distinct answers rather than a boolean.
type RefSearcher ¶ added in v0.22.0
RefSearcher answers `search role=reference`: references whose token matches a SQL LIKE pattern, optionally only those whose node sits under a path segment, at most limit of them. Engine sentinels (FileLevelSentinelPrefix) are never hits, as GetCallers does not return them either.
It is a capability rather than SQL in the handler because where references live depends on the backend: the mache_refs sidecar for MemoryStore and the legacy path, node_refs for a nodes-table .db, keyed by node_id on projection-v4 and by nid on projection-v6 (mache-90cb9f).
type RefTarget ¶ added in v0.22.0
type RefTarget struct {
Resolution RefResolution
// NodeID is the single definition, empty unless Resolution is RefResolved.
NodeID string
// Candidates lists the competing definitions when RefAmbiguous.
Candidates []string
// Via names the import path when RefExternal — the module the call leaves
// through, which is what a dependency-aware consumer wants to record.
Via string
}
RefTarget is where a reference points. NodeID is set ONLY for RefResolved: a caller cannot accidentally consume a guess, because an unresolved target has nothing to consume.
type RefsMapper ¶ added in v0.22.0
RefsMapper exposes the reference index as node ID -> referenced node IDs, the edge set community detection and impact analysis run over.
type RefsQuerier ¶ added in v0.22.0
RefsQuerier runs SQL directly against the backing database — the escape hatch for questions the typed accessors above do not cover, including the `_ast` and `_lsp*` tables ley-line-open produces.
The tables available depend on who produced the .db; see docs/ARCHITECTURE.md for the capability matrix.
type SQLiteGraph ¶ added in v0.22.0
type SQLiteGraph struct {
// contains filtered or unexported fields
}
SQLiteGraph implements Graph by querying the source SQLite database directly. No index copy, no ingestion step — the source DB's B+ tree IS the index.
Design: directory structure is derived lazily from schema + DB on first access, then cached in sync.Maps for lock-free concurrent reads from FUSE callbacks.
The scan is single-threaded and streaming: one sequential pass over all records, rendering name templates to build parent→child path relationships. This avoids the deadlock risk and channel overhead of a worker pool — SQLite sequential reads are I/O-bound and template rendering for name fields is cheap.
Memory model after scan:
- dirChildren: sorted []string slices (one per directory), read-only post-scan
- recordIDs: leaf dir path → DB row ID, for on-demand content resolution
- contentCache: FIFO-bounded rendered content (avoids re-fetching hot files)
Content is never loaded during scan — only on FUSE read via resolveContent, which does a primary key lookup + template render + FIFO cache.
Cross-references (token → file bitmap) are stored in a sidecar database (<dbpath>.refs.db) to keep the source DB immutable. Refs are accumulated in-memory during ingestion and flushed once via FlushRefs.
func Open ¶ added in v0.22.0
func Open(dbPath string) (*SQLiteGraph, error)
Open opens a mache .db file for querying, with the same defaults mache's own CLI uses for a bare .db source (empty schema — auto-detected from the db's own tables — and mache's pure-Go template renderer). Returns a *SQLiteGraph, which implements Graph plus LookupDef/QueryRefs/GetCallers directly against the file — no import/populate step required.
This is the fix for the trap MemoryStore + ImportSQLite falls into: a MemoryStore built by ImportSQLite has an empty defs map and an uninitialized refs sidecar (ImportSQLite only replicates the node tree), so LookupDef silently returns nil and QueryRefs errors "refsDB not initialized" — not because the .db lacks the data, but because it was never copied into MemoryStore's separate in-memory indices. Open sidesteps the whole class by not copying anything.
OpenSQLiteGraph is the lower-level form, for callers supplying their own schema and template renderer.
func OpenSQLiteGraph ¶ added in v0.22.0
func OpenSQLiteGraph(dbPath string, schema *api.Topology, render TemplateRenderer) (*SQLiteGraph, error)
OpenSQLiteGraph opens a connection to the source DB and compiles the schema.
func (*SQLiteGraph) ASTKindChain ¶ added in v0.22.0
func (g *SQLiteGraph) ASTKindChain(id string) ([]string, bool, error)
ASTKindChain implements ASTKinder: one recursive walk up the nodes table, joined to _ast through lloschema's ASTSelect so the kinds read the same on either projection. Depth is capped at 256, far past any real nesting, so a cyclic parent chain in a malformed table cannot spin forever.
func (*SQLiteGraph) Act ¶ added in v0.22.0
func (g *SQLiteGraph) Act(id, action, payload string) (*ActionResult, error)
Act returns ErrActNotSupported — SQLiteGraph is a passive data graph.
func (*SQLiteGraph) AddDef ¶ added in v0.22.0
func (g *SQLiteGraph) AddDef(token, dirID string) error
AddDef records that a construct (dirID) defines the given token.
func (*SQLiteGraph) AddRef ¶ added in v0.22.0
func (g *SQLiteGraph) AddRef(token, nodeID string) error
AddRef accumulates a reference in-memory. No SQL is issued until FlushRefs. This eliminates the read-modify-write cycle per call — all bitmap mutations happen in RAM, and FlushRefs writes them in a single transaction. Not used for nodes-table path (refs already in main DB from mache build).
func (*SQLiteGraph) Close ¶ added in v0.22.0
func (g *SQLiteGraph) Close() error
Close closes both the source and sidecar database connections.
func (*SQLiteGraph) DB ¶ added in v0.22.0
func (g *SQLiteGraph) DB() *sql.DB
DB returns the main SQLite handle. Exposed so callers can query non-refs tables like `_ast` / `_source` / `_lsp*` that ley-line-built .db files carry. Mirrors NodesTableReader.DB() — same shape, same purpose. The returned handle is owned by SQLiteGraph; do not Close it, and queries should be read-only.
func (*SQLiteGraph) DBPath ¶ added in v0.22.0
func (g *SQLiteGraph) DBPath() string
DBPath returns the source .db file path. Implements the cmd-side dbPathProvider opt-in (see cmd/serve_find_smells.go) so canonical- view setup can locate the sibling .bindings.capnp event log next to this .db (mache-190508 step 3).
func (*SQLiteGraph) DefTokenOf ¶ added in v0.22.0
func (g *SQLiteGraph) DefTokenOf(id string) (string, bool, error)
DefTokenOf implements DefTokener.
func (*SQLiteGraph) DefsMap ¶ added in v0.22.0
func (g *SQLiteGraph) DefsMap() map[string][]string
DefsMap returns a snapshot of the token→dirIDs definition map.
On pre-built .db files (the `mache build` / `leyline parse` output) the in-memory g.defs map is empty — definitions live in the node_defs SQL table populated at build time. Falls through to a SQL scan so downstream consumers (get_impact, get_architecture, and the find_callees resolver fallback) see the full def set on both fresh in-process ingestion and pre-built .db sources.
In-memory entries take precedence on token collision (live ingestion is authoritative over the frozen-at-build snapshot), matching LookupDef's behavior and the SearchDefs pattern below.
inMemoryTokens guards multi-row accumulation: without it, the cp[token] existence check would skip every node_defs row past the first for any token (e.g. a method like Close defined across multiple types would only return one node_id). Bead mache-655e98.
func (*SQLiteGraph) EagerScan ¶ added in v0.22.0
func (g *SQLiteGraph) EagerScan() error
EagerScan pre-scans all root nodes so no FUSE callback ever blocks on a scan. Call this before mounting — fuse-t's NFS transport times out if a callback takes >2s.
func (*SQLiteGraph) FlushRefs ¶ added in v0.22.0
func (g *SQLiteGraph) FlushRefs() error
FlushRefs writes all accumulated refs to the sidecar database in a single transaction. Call once after ingestion is complete. Batches all inserts (len(fileIDMap) + len(pendingRefs)) into one transaction.
Guarded by sync.Once — safe to call multiple times; only the first call performs the flush. This prevents the double-call bug where a second flush would reset nextFileID to 0, causing ID collisions in file_ids.
func (*SQLiteGraph) GetCallees ¶ added in v0.22.0
func (g *SQLiteGraph) GetCallees(id string) ([]*Node, error)
GetCallees implements Graph. It resolves each call made by the construct to one or more definition node IDs using a fallback chain:
- Qualified lookup (e.g. "auth.Validate") via in-memory defs.
- SQL fallback to node_defs (pre-built .db only).
- Bare-token lookup via in-memory defs.
- Bare-token SQL lookup against node_defs.
- Suffix-match in node_defs ("*.token") for unambiguous receiver-method calls — see bead mache-9ca6af for the receiver-method root cause.
- Final fallback: name match in the nodes table.
Calls are extracted one of three ways:
- AST-scoped (preferred): when the construct's node Properties carry "ast_source_id"/"ast_scope_id" (persisted at projection time — see ingest.ASTScope) AND a scoped extractor is wired (requires a LIVE `_ast` table), GetCallees queries the pre-parsed `_ast` table directly, scoped to this construct. Fixes bead mache-fd9982: the legacy path below fed the GRAPH node id (e.g. "cmd/functions/evalOrAbs") as if it were the `_ast` source_id, which matched zero rows every time — find_callees silently returned nothing on every serve/mount .db.
- node_refs fallback: when #1 is unavailable — no `_ast` table (a `mache build --schema` .db never retains it: keeping `_ast` for a whole repo costs 228MB+162MB, prohibitive) or the construct lacks the Properties — but the construct has a "source" file child, resolve its calls from node_refs(token, node_id) instead. node_refs already holds every call token ScopeCalls found for this construct at projection time (engine_walk.go's `store.AddRef(token, sourceFileID)`), keyed by the construct's "source" file node id. This is bare-token only (no qualifier fidelity) — cross-package same-name over-match is possible, but strictly better than returning [] (bead mache-6fbaf1).
- Legacy content-based extractor: re-reads the construct's "source" file content and re-parses it. Kept for non-AST backends (or .dbs projected before this fix that lack the ast_source_id/ast_scope_id Properties AND have no node_refs coverage).
func (*SQLiteGraph) GetCallers ¶ added in v0.22.0
func (g *SQLiteGraph) GetCallers(token string) ([]*Node, error)
GetCallers returns the list of files (nodes) that reference the given token. For nodes-table path: queries main DB's node_refs (token, node_id) directly. For legacy path: reads roaring bitmaps from the sidecar refs database.
func (*SQLiteGraph) GetNode ¶ added in v0.22.0
func (g *SQLiteGraph) GetNode(id string) (*Node, error)
func (*SQLiteGraph) IndexStaleness ¶ added in v0.22.0
func (g *SQLiteGraph) IndexStaleness() (IndexStaleness, bool)
staleness derives the report from facts the artifact already carries: the .db file's own mtime IS the build time, and ley-line stamps _meta.source_root at parse. Nothing new is written anywhere — a pure read-side derivation, which also means it works retroactively on every existing .db.
Returns ok=false when the graph cannot answer (no dbPath, no _meta, source root gone) — absence of a staleness report must read as "unknown", never as "fresh".
func (*SQLiteGraph) Invalidate ¶ added in v0.22.0
func (g *SQLiteGraph) Invalidate(id string)
Invalidate evicts cached size and content for a node. Must be called after write-back modifies a file's content to prevent stale size/data from being served on the next Getattr or Read.
func (*SQLiteGraph) ListChildStats ¶ added in v0.22.0
func (g *SQLiteGraph) ListChildStats(id string) ([]NodeStat, error)
ListChildStats returns stat snapshots for all children of a directory. For the nodes-table fast path, it queries the nodes table for children with kind, size, and mtime — no content rendering. For the legacy scan path, it uses dirChildren + schema structure. ContentSize may be 0 for unvisited files (FUSE/NFS fall back to LOOKUP).
func (*SQLiteGraph) ListChildren ¶ added in v0.22.0
func (g *SQLiteGraph) ListChildren(id string) ([]string, error)
func (*SQLiteGraph) LookupDef ¶ added in v0.22.0
func (g *SQLiteGraph) LookupDef(token string) []string
LookupDef returns the dir IDs that define `token` without snapshotting the entire defs map. Returns nil for unknown tokens. Mirrors MemoryStore.LookupDef; see that method's docstring.
For pre-built DBs (useNodesTable path) the in-memory g.defs map is usually empty because data lives in the `node_defs` SQL table. Falls back to the SQL query on in-memory miss so find_definition works against ley-line-parsed databases. Matches the SQL pattern used by GetCallees for parity.
func (*SQLiteGraph) LookupDefNodes ¶ added in v0.22.0
func (g *SQLiteGraph) LookupDefNodes(token string) ([]*Node, error)
LookupDefNodes returns the definition NODES for token, closing the return-type asymmetry that made every consumer write the same conversion loop:
LookupDef(token) -> []string // ids GetCallers(token) -> ([]*Node, error) // nodes GetCallees(id) -> ([]*Node, error) // nodes
Ids from one accessor and Nodes from the next is why modmap wrapped this surface in its own callerResolver. LookupDef stays as-is — ids ARE the right answer when the caller only needs identity, and fetching nodes it will throw away is waste. This is the variant for when it actually wants the node.
A definition whose node cannot be loaded is SKIPPED rather than failing the call: node_defs can outlive a node during an incremental reparse, and one stale row should not deny a caller the definitions that did resolve.
func (*SQLiteGraph) QueryRefs ¶ added in v0.22.0
QueryRefs executes a SQL query against the refs database. For nodes-table path: queries the main DB (node_refs has (token, node_id)). For legacy path: queries the sidecar (includes mache_refs virtual table).
func (*SQLiteGraph) ReadContent ¶ added in v0.22.0
func (*SQLiteGraph) RefRangeOf ¶ added in v0.22.0
func (g *SQLiteGraph) RefRangeOf(nodeID string) (*RefRange, error)
RefRangeOf returns where the reference node sits in its file.
Every consumer previously hand-wrote this join (node_refs.node_id -> _ast), which is why it is here: the join is not the interesting part of anyone's program. Returns nil when the node has no `_ast` row — a standalone mache projection carries no spans, and nil is the honest answer rather than a zero-valued range that reads as "line 0".
func (*SQLiteGraph) RefsMap ¶ added in v0.22.0
func (g *SQLiteGraph) RefsMap() map[string][]string
RefsMap returns a token→nodeIDs map for community detection. For the nodes-table path, queries node_refs (token, node_id). For the legacy bitmap path, decodes bitmaps and resolves file IDs.
Engine `_file_level:` sentinels are stripped here, for every path, because this is a CONSUMER-facing aggregation: its output feeds community detection, architecture layering and context packing, none of which can do anything with a bookkeeping id that names no construct. Leaving them in is not a cosmetic problem — measured on a schema-projected build of cmd/, 1,939 of 15,073 refs were sentinels, and they cluster together because they co-occur by construction, so they crowd out the real communities (mache-8f6abf).
Filtered at this boundary rather than in each builder below, so the two backends cannot disagree about it — leylinegraph already filtered in SQL while these two did not, which is how the same API came to return different data depending on which producer wrote the .db.
The rows stay in node_refs. dead_code reads them directly and needs them.
func (*SQLiteGraph) ResolveRef ¶ added in v0.22.0
func (g *SQLiteGraph) ResolveRef(nodeID string) (*RefTarget, error)
ResolveRef answers "what definition does this reference point at?" for the node id of a reference site.
It replaces bare-token matching against node_refs.token, which cannot distinguish a local `Join` from `filepath.Join` and will happily return the wrong one. The classification is derived, never guessed:
no local def + a known import -> RefExternal (Via names the import) no local def, no import -> RefNoTarget exactly one local def -> RefResolved several, narrowed to one -> RefResolved several, still several -> RefAmbiguous (candidates returned)
Note what is NOT here: no attempt to decide whether an import path "looks external" by inspecting its shape. Whether github.com/you/yours is inside your module is something the caller knows and mache does not, so mache reports the import path and lets the caller judge.
func (*SQLiteGraph) SearchDefs ¶ added in v0.22.0
func (g *SQLiteGraph) SearchDefs(pattern string, limit int) map[string][]string
SearchDefs returns up to `limit` token→nodeIDs entries whose token matches the SQL LIKE pattern. Used by `search role=definition` to avoid materializing the full defs map.
For pre-built .db files the in-memory g.defs map is empty; this pushes the filter down to SQL against node_defs. Mirrors LookupDef's fallback structure: in-memory first (with sqlLikeMatch-style check), then SQL. Bead mache-9cba08.
func (*SQLiteGraph) SearchRefs ¶ added in v0.22.0
func (g *SQLiteGraph) SearchRefs(pattern, segment string, limit int) ([]RefHit, error)
SearchRefs implements RefSearcher.
func (*SQLiteGraph) SetCallExtractor ¶ added in v0.22.0
func (g *SQLiteGraph) SetCallExtractor(fn CallExtractor)
SetCallExtractor configures the parser for on-demand callee resolution.
func (*SQLiteGraph) SetScopedCallExtractor ¶ added in v0.22.0
func (g *SQLiteGraph) SetScopedCallExtractor(fn ScopedCallExtractor)
SetScopedCallExtractor configures the AST-scoped extractor GetCallees prefers when a construct carries ast_source_id/ast_scope_id Properties.
type SQLiteResolver ¶ added in v0.6.7
type SQLiteResolver struct {
// contains filtered or unexported fields
}
SQLiteResolver resolves ContentRef entries by fetching records from SQLite and re-rendering their content templates.
func NewSQLiteResolver ¶ added in v0.6.7
func NewSQLiteResolver(render TemplateRenderer) *SQLiteResolver
NewSQLiteResolver creates a resolver that uses the given template renderer to render content from SQLite records.
func (*SQLiteResolver) Close ¶ added in v0.22.0
func (r *SQLiteResolver) Close()
Close closes all open database connections and clears the pool. Subsequent Resolve calls will re-open databases as needed.
func (*SQLiteResolver) Resolve ¶ added in v0.22.0
func (r *SQLiteResolver) Resolve(ref *ContentRef) ([]byte, error)
Resolve fetches a record from SQLite and renders its content template.
type ScopedCallExtractor ¶ added in v0.22.0
type ScopedCallExtractor func(sourceID, scopeID, langName string) ([]QualifiedCall, error)
ScopedCallExtractor returns qualified call tokens made BY a single construct, scoped to it. Unlike CallExtractor, it is keyed on the construct's REAL `_ast`/`_source` id and `_ast` scope node id — not a graph node id and not source content — so it works directly against a pre-parsed `_ast` table without re-deriving anything from the graph.
GetCallees prefers this over the legacy content-based CallExtractor when the construct's node carries "ast_source_id"/"ast_scope_id" Properties (persisted at projection time; see ingest.ASTScope). Feeding the graph node id as a source_id into the old content-based path matched zero `_ast` rows every time — the root cause of find_callees silently returning nothing on the serve/mount path (bead mache-fd9982).
type SheafBackend ¶ added in v0.22.0
SheafBackend is the subset of SheafClient that SheafInvalidator needs. Defined as an interface to allow testing without a real daemon.
type SheafInvalidator ¶ added in v0.22.0
type SheafInvalidator struct {
// contains filtered or unexported fields
}
SheafInvalidator wraps a Graph with sheaf-aware cascading invalidation. When a node is invalidated, it looks up the node's community (region), asks the ley-line daemon which regions are transitively affected, then invalidates all nodes in those affected regions.
If the SheafClient is nil or the daemon is unavailable, it falls back to plain Graph.Invalidate on the single node.
func NewSheafInvalidator ¶ added in v0.22.0
func NewSheafInvalidator(graph Graph, sheaf SheafBackend, result *CommunityResult) *SheafInvalidator
NewSheafInvalidator creates a SheafInvalidator. All parameters are optional:
- graph may be nil (all operations become no-ops)
- sheaf may be nil (falls back to single-node invalidation)
- result may be nil (falls back to single-node invalidation)
func (*SheafInvalidator) CommunityResultForRouting ¶ added in v0.22.0
func (si *SheafInvalidator) CommunityResultForRouting() *CommunityResult
CommunityResultForRouting returns the current CommunityResult under a read lock. The pointer is returned without cloning — callers MUST treat it as read-only (the membership map is replaced wholesale by future SetCommunityResult / SetState writers, which install a NEW pointer rather than mutating in place, so a stable snapshot is safe to iterate without further locking).
Exists for the sheaf event subscriber's router (cmd/sheaf_subscribe.go), which needs to walk membership to convert region IDs into node IDs for invalidation. Named "ForRouting" rather than the obvious CommunityResult() to signal the read-only contract.
func (*SheafInvalidator) HasResult ¶ added in v0.22.0
func (si *SheafInvalidator) HasResult() bool
HasResult reports whether the invalidator has a CommunityResult to look up regions in. When false, InvalidateWithCascade degrades to single-node invalidate (still correct, just no cascade). Callers (e.g. the watcher) use this to decide whether to skip the lookup entirely vs. fall through.
func (*SheafInvalidator) InvalidateAllScoped ¶ added in v0.22.0
func (si *SheafInvalidator) InvalidateAllScoped() int
InvalidateAllScoped evicts every node the invalidator's CommunityResult claims membership for, WITHOUT calling the sheaf backend to compute a cascade. Returns the number of nodes evicted.
This is the coarse-v1 "invalidate everything" path used by the watcher-driven `daemon.sheaf.invalidate` topic (LLO PR #140, LLO v0.6+). The daemon has already declared its entire working set stale by the time it emits `scope: "all-known"`, so re-cascading per-region against the daemon would be redundant chatter — we know the answer is "all of them". A future fine-grained (`scope: "changed-only"`) emit path exists (LLO bead `e40566`); it stays on the per-region cascade path via InvalidateNodesWithCascade.
No-op when the invalidator, graph, or CommunityResult is nil (same null-safety contract as InvalidateNodesWithCascade). Skipping the backend also means this method does not touch si.sheaf, so it's safe to call even when no SheafBackend is installed — the membership snapshot is enough.
func (*SheafInvalidator) InvalidateNodesWithCascade ¶ added in v0.22.0
func (si *SheafInvalidator) InvalidateNodesWithCascade(ids []string) int
InvalidateNodesWithCascade is the batched-by-region variant of InvalidateWithCascade. The watcher passes every node touched by a file edit; this method dedupes them to the underlying set of unique region IDs and calls the daemon exactly once per unique region.
Why this exists (PR #383 Copilot #7): the naive `for _, id := range ids { InvalidateWithCascade(id) }` loop hits the daemon once per node. On a large file with 50 functions all in the same community, that's 50 redundant region invalidations — each bumps the generation counter and re-cascades the same regions on the daemon side. This method collapses that to one call per unique region.
IDs not present in membership fall back to single-node Graph.Invalidate (same fallback as InvalidateWithCascade for unknown nodes). When sheaf or membership is nil, the entire input falls back to per-id Graph.Invalidate.
Returns total nodes invalidated across all paths (cascades + fallbacks).
func (*SheafInvalidator) InvalidateWithCascade ¶ added in v0.22.0
func (si *SheafInvalidator) InvalidateWithCascade(id string, membership map[string]int) int
InvalidateWithCascade invalidates a node and, if sheaf is available, cascades the invalidation to all nodes in transitively affected regions.
The membership map is used to look up which community the node belongs to. If membership is nil, si.result.Membership is used.
Returns the number of nodes invalidated.
func (*SheafInvalidator) SetCommunityResult ¶ added in v0.22.0
func (si *SheafInvalidator) SetCommunityResult(cr *CommunityResult)
SetCommunityResult updates the community detection result used for lookups. Call this after re-running community detection.
func (*SheafInvalidator) SetSheaf ¶ added in v0.22.0
func (si *SheafInvalidator) SetSheaf(b SheafBackend) (prior SheafBackend)
SetSheaf swaps the sheaf backend. Pass nil to disable cascades (the invalidator falls back to single-node Graph.Invalidate). Used by the serve startup path to attach a SheafClient lazily — the file watcher can install the invalidator before the ley-line daemon is reachable, then swap in a real backend once a connection is established.
Returns the prior backend so callers that own SheafBackend resources (e.g. a SheafClient wrapping a UDS socket) can close the prior one without leaking. Returns nil if no prior backend was installed.
func (*SheafInvalidator) SetState ¶ added in v0.22.0
func (si *SheafInvalidator) SetState(result *CommunityResult, sheaf SheafBackend) (prior SheafBackend)
SetState atomically swaps both the CommunityResult and SheafBackend under a single Lock. Use this when both pieces are being replaced together (e.g. from get_communities after PushTopology succeeds). The standalone SetSheaf / SetCommunityResult paths open a window where the watcher can observe new membership paired with old sheaf (or vice versa) — see TestSheafInvalidator_SetState_AtomicSwap and PR #383 Copilot #6 for the cascade-mismatch failure mode.
Returns the prior backend so the caller can close it without leaking. Returns nil if no prior backend was installed.
type SourceOrigin ¶
type SourceOrigin struct {
FilePath string `json:"file"`
StartByte uint32 `json:"start_byte"`
EndByte uint32 `json:"end_byte"`
// Line/column of the same range, 1-based, or 0 when unknown.
//
// Byte offsets are the right primitive for write-back — a splice needs
// bytes — but they are the wrong unit for a READER. No editor, terminal,
// or LLM context references source by byte; everything downstream speaks
// "path:line". Carrying both means a consumer never has to re-read the
// file and count newlines to act on a result (mache-e57065).
//
// 1-based because that is what every consumer of a line number expects.
// ley-line-open's `_ast` stores tree-sitter's 0-based rows, so the
// producer side adds one; 0 here therefore means "not known", never
// "first line". Columns follow the same convention.
StartLine uint32 `json:"start_line,omitempty"`
StartCol uint32 `json:"start_col,omitempty"`
EndLine uint32 `json:"end_line,omitempty"`
EndCol uint32 `json:"end_col,omitempty"`
}
SourceOrigin tracks the byte range of a construct in its source file. Used by write-back to splice edits into the original source.
type StalenessReporter ¶ added in v0.22.0
type StalenessReporter interface {
IndexStaleness() (IndexStaleness, bool)
}
StalenessReporter is the capability get_overview probes for. On graphs that cannot answer (MemoryStore, composites) the overview simply omits the block.
type TemplateRenderer ¶ added in v0.6.7
TemplateRenderer renders a Go text/template string with the given values map.
type WritableGraph ¶ added in v0.22.0
type WritableGraph struct {
// contains filtered or unexported fields
}
WritableGraph is a read-write SQLite backend for arena-mode mounts. Read methods delegate to NodesTableReader (shared with SQLiteGraph). Write methods mutate the nodes table and flush to the arena.
func OpenWritableGraph ¶ added in v0.22.0
func OpenWritableGraph(masterDBPath string, schema *api.Topology, render TemplateRenderer, flusher *ArenaFlusher) (*WritableGraph, error)
OpenWritableGraph opens a writable connection to the master .db. The DB must have a nodes table (created by mache build).
func (*WritableGraph) Act ¶ added in v0.22.0
func (g *WritableGraph) Act(id, action, payload string) (*ActionResult, error)
func (*WritableGraph) Close ¶ added in v0.22.0
func (g *WritableGraph) Close() error
Close closes the database connection.
func (*WritableGraph) DBPath ¶ added in v0.22.0
func (g *WritableGraph) DBPath() string
DBPath returns the path to the writable master database.
func (*WritableGraph) Flush ¶ added in v0.22.0
func (g *WritableGraph) Flush()
Flush requests a coalesced arena flush. Non-blocking.
func (*WritableGraph) FlushNow ¶ added in v0.22.0
func (g *WritableGraph) FlushNow() error
FlushNow performs a synchronous arena flush.
func (*WritableGraph) GetCallees ¶ added in v0.22.0
func (g *WritableGraph) GetCallees(id string) ([]*Node, error)
func (*WritableGraph) GetCallers ¶ added in v0.22.0
func (g *WritableGraph) GetCallers(token string) ([]*Node, error)
func (*WritableGraph) GetNode ¶ added in v0.22.0
func (g *WritableGraph) GetNode(id string) (*Node, error)
func (*WritableGraph) Invalidate ¶ added in v0.22.0
func (g *WritableGraph) Invalidate(id string)
func (*WritableGraph) ListChildStats ¶ added in v0.22.0
func (g *WritableGraph) ListChildStats(id string) ([]NodeStat, error)
func (*WritableGraph) ListChildren ¶ added in v0.22.0
func (g *WritableGraph) ListChildren(id string) ([]string, error)
func (*WritableGraph) ReadContent ¶ added in v0.22.0
func (*WritableGraph) UpdateRecord ¶ added in v0.22.0
func (g *WritableGraph) UpdateRecord(id string, content []byte) error
UpdateRecord updates a file node's content in the nodes table.
Source Files
¶
- arena.go
- arena_writer.go
- cache.go
- capabilities.go
- community.go
- community_granularity.go
- composite.go
- content_cache.go
- def_token.go
- graph.go
- hotswap.go
- index_staleness.go
- memstore.go
- memstore_callees.go
- memstore_delete.go
- memstore_livegraph.go
- memstore_refs.go
- memstore_refsdb.go
- memstore_write.go
- nodes_row.go
- nodes_schema.go
- nodes_shape.go
- nodes_table_reader.go
- open.go
- projection_version.go
- props.go
- props_compat.go
- quotient.go
- ref_search.go
- refs_resolve.go
- sentinel.go
- sheaf_invalidate.go
- sliceutil.go
- sqlite.go
- sqlite_graph.go
- sqlite_graph_callees.go
- sqlite_graph_ids.go
- sqlite_graph_refs.go
- sqlite_graph_scan.go
- sqlite_graph_schema.go
- sqlite_resolver.go
- store.go
- store_local.go
- vdirpath.go
- writable_graph.go