store

package
v0.9.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 6, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// ConfigAutoIndex controls whether the server auto-indexes on startup.
	// Default: false (off). Enable with: code-graph config set auto_index true
	ConfigAutoIndex = "auto_index"

	// ConfigReportSkipPrefix prefixes per-project sticky skip_report
	// preferences ("report.skip.<project>" = "true"/"false"), persisted by
	// handleIndexRepository when skip_report is explicitly provided and
	// inherited by calls that omit the argument.
	ConfigReportSkipPrefix = "report.skip."

	// ConfigGraphPrecisionTierPrefix stores the explicit per-project graph
	// precision choice ("heuristic" or "scip"). ConfigGraphSCIPPathPrefix
	// stores the corresponding compiler index path, and
	// ConfigGraphPrecisionStatusPrefix stores the last machine-readable ingest
	// outcome surfaced by index_status.
	ConfigGraphPrecisionTierPrefix   = "graph.precision.tier."
	ConfigGraphSCIPPathPrefix        = "graph.precision.scip_path."
	ConfigGraphPrecisionStatusPrefix = "graph.precision.status."

	// ConfigAutoIndexLimit is the max file count for auto-indexing new projects.
	// Default: 50000. Projects above this limit require explicit index_repository.
	ConfigAutoIndexLimit = "auto_index_limit"

	// ConfigMemLimit sets GOMEMLIMIT for the server process.
	// Accepts human-readable sizes: "2G", "512M", "4096M".
	// Default: empty (no limit). Applied on server startup.
	ConfigMemLimit = "mem_limit"
)

Known config keys and their defaults.

View Source
const (
	// Structure.
	EdgeContains     = "CONTAINS"
	EdgeContainsFile = "CONTAINS_FILE"
	EdgeDefines      = "DEFINES"
	EdgeDefinesMeth  = "DEFINES_METHOD"
	EdgeDefinesField = "DEFINES_FIELD"
	EdgeMemberOf     = "MEMBER_OF"
	EdgeParameterOf  = "PARAMETER_OF"

	// Calls family.
	EdgeCalls         = "CALLS"
	EdgeCallReference = "CALL_REFERENCE"
	EdgeCallsExternal = "CALLS_EXTERNAL"
	EdgeCallsPseudo   = "CALLS_PSEUDO"
	EdgeIndirectCalls = "INDIRECT_CALLS"
	EdgeHTTPCalls     = "HTTP_CALLS"
	EdgeAsyncCalls    = "ASYNC_CALLS"
	EdgeHandles       = "HANDLES"

	// Types and inheritance.
	EdgeImports    = "IMPORTS"
	EdgeImplements = "IMPLEMENTS"
	EdgeInherits   = "INHERITS"
	EdgeOverride   = "OVERRIDE"
	EdgeUsesType   = "USES_TYPE"
	EdgeUsage      = "USAGE"
	EdgeDecorates  = "DECORATES"

	// Data flow and errors.
	EdgeReads  = "READS"
	EdgeWrites = "WRITES"
	EdgeThrows = "THROWS"
	EdgeRaises = "RAISES"

	// Tests.
	EdgeTests     = "TESTS"
	EdgeTestsFile = "TESTS_FILE"

	// Configuration, infrastructure, and policy.
	EdgeConfigures  = "CONFIGURES"
	EdgeDependsOn   = "DEPENDS_ON"
	EdgeReadsEnv    = "READS_ENV"
	EdgePolicyGates = "POLICY_GATES"
	EdgeRunsBinary  = "RUNS_BINARY"

	// Messaging.
	EdgePublishesTo  = "PUBLISHES_TO"
	EdgeSubscribesTo = "SUBSCRIBES_TO"
	EdgeQueries      = "QUERIES"
	EdgeAnswers      = "ANSWERS"

	// Derived.
	EdgeFileChangesWith       = "FILE_CHANGES_WITH"
	EdgeSemanticallySimilarTo = "SEMANTICALLY_SIMILAR_TO"
	EdgeRationaleFor          = "RATIONALE_FOR"
)

Edge type constants. Every relationship kind the graph can hold is declared here once; passes emit edges with these constants, and EdgeTypes documents each one. A test in this package fails when production code introduces a new `Type: "LITERAL"` outside this table, so the schema stays enumerable for get_graph_schema, docs/edge-types.md, and downstream consumers.

View Source
const (
	ConfidenceExtracted = "EXTRACTED"
	ConfidenceInferred  = "INFERRED"
	ConfidenceAmbiguous = "AMBIGUOUS"
)

Edge confidence tier. Stored in Edge.Properties["confidence_tier"]; the generated column `confidence_tier_gen` on the edges table (see store.go initSchema) surfaces this value as a first-class, indexable field so Cypher queries can filter with `WHERE r.confidence_tier = 'X'`. Absent properties default to EXTRACTED at the column level via COALESCE.

Note on naming: the property key is `confidence_tier` (not `confidence`) to avoid collision with the pre-existing numeric `confidence` property used by configlink_strategies.go for per-strategy heuristic scores. The tier is categorical; the legacy score is continuous.

EXTRACTED

Direct, source-proven relationship. The AST literally says the
edge exists (a function call expression, an import statement, a
class definition). This is the default for any edge whose creator
does not set a confidence_tier property.

INFERRED

Relationship deduced via static reasoning beyond the raw AST:
interface satisfaction by method-set matching, inherited methods
across class hierarchies, an HTTP caller matched to a route
handler, a test function matched to its production target via a
naming heuristic. Still high-signal, but a grammar change or a
refactor could invalidate it.

AMBIGUOUS

Relationship asserted via a fuzzy match that may be wrong. A
config file whose values happen to match a variable name, a git
file-coupling metric below a high-confidence threshold, a
parameterized URL path that could match multiple routes. Useful
for suggestion-surface tools; filter these out for automated
blast-radius calculations.
View Source
const AutoRecoveryEnvVar = "CODE_GRAPH_AUTO_RECOVERY"

AutoRecoveryEnvVar is the env var name that opt-in for auto-recovery. Default behavior (env var unset) preserves the existing "structured error → operator decides" flow.

View Source
const FormatVersion = 1

FormatVersion is the on-disk index format this build reads and writes. It is stored in SQLite's user_version pragma. Bump it whenever a schema or semantic change makes databases written by this build unreadable by the previous release, and document the bump in docs/index-format.md.

View Source
const MinSupportedFormatVersion = 1

MinSupportedFormatVersion is the oldest format this build still opens. Databases below it must be rebuilt with index_repository.

Variables

View Source
var EdgeTypes = func() []EdgeTypeInfo {
	t := []EdgeTypeInfo{
		{EdgeContains, "structure", "Directory or Package", "Directory, Package, or File", "Filesystem and package containment produced by the structure pass."},
		{EdgeContainsFile, "structure", "Directory", "File", "Direct file containment used for fast per-directory listings."},
		{EdgeDefines, "structure", "File or Module", "Function, Class, or Variable", "A file or module defines a top-level symbol."},
		{EdgeDefinesMeth, "structure", "Class, Struct, or Trait", "Method", "A type defines a method."},
		{EdgeDefinesField, "structure", "Class or Struct", "Field", "A type defines a field."},
		{EdgeMemberOf, "structure", "Symbol", "Community", "Louvain community membership computed after indexing."},
		{EdgeParameterOf, "structure", "Parameter", "Function", "A parameter belongs to a function; used by data-flow reachability."},

		{EdgeCalls, "calls", "Function", "Function", "A resolved call. Properties carry resolver rule, strategy, confidence, and, for SCIP-derived edges, the artifact digest."},
		{EdgeCallsExternal, "calls", "Function", "External stub", "A call whose target lives outside the indexed repository."},
		{EdgeCallsPseudo, "calls", "Function", "Pseudo target", "A call to a language construct modelled as a pseudo node (modal dispatch, builtins)."},
		{EdgeIndirectCalls, "calls", "Function", "Function", "A call reached through a function value, callback, or dispatch table."},
		{EdgeHTTPCalls, "calls", "Function", "Route handler", "A cross-service HTTP call matched to the handler that serves the route."},
		{EdgeAsyncCalls, "calls", "Function", "Function", "A call across an async boundary (task spawn, message dispatch)."},
		{EdgeHandles, "calls", "Route", "Function", "A route node is served by a handler function."},
		{EdgeCallReference, "calls", "Function or Module", "Function or Method", "A callable referenced at a value site (assignment, collection literal, argument) that resolves to exactly one target; not an invocation. Aligned with upstream codebase-memory-mcp: CALL_REFERENCE is the proven-single-target counterpart of USAGE."},

		{EdgeImports, "types", "Module or File", "Module", "An import statement, normalized for relative imports."},
		{EdgeImplements, "types", "Type", "Interface or Trait", "A type implements an interface or trait."},
		{EdgeInherits, "types", "Class", "Class", "Class inheritance."},
		{EdgeOverride, "types", "Method", "Method", "A method overrides a parent or interface method."},
		{EdgeUsesType, "types", "Function or Field", "Type", "A signature or field references a type."},
		{EdgeUsage, "types", "Function or Module", "Variable, Constant, Type, or Function", "An identifier used at a value site where no unique callable target is proven (a non-callable symbol, or an ambiguous or fuzzy resolution). The unproven counterpart of CALL_REFERENCE."},
		{EdgeDecorates, "types", "Decorator", "Function or Class", "A decorator or attribute applied to a definition."},

		{EdgeReads, "dataflow", "Function", "Variable or Field", "A read of a variable or field."},
		{EdgeWrites, "dataflow", "Function", "Variable or Field", "A write to a variable or field."},
		{EdgeThrows, "dataflow", "Function", "Type", "A function throws an exception type (statically typed languages)."},
		{EdgeRaises, "dataflow", "Function", "Type", "A function raises an exception type (Python)."},

		{EdgeTests, "tests", "Test function", "Function", "A test exercises a production function."},
		{EdgeTestsFile, "tests", "Test file", "File", "A test file covers a production file by convention."},

		{EdgeConfigures, "config", "Config file or Service", "Service, Function, or Variable", "Configuration links a config artifact to the code it configures."},
		{EdgeDependsOn, "config", "Package or Service", "Package or Service", "A declared dependency from a lockfile, manifest, or infrastructure module."},
		{EdgeReadsEnv, "config", "Function", "EnvVar", "Code reads an environment variable."},
		{EdgePolicyGates, "config", "Policy", "Function or Route", "An OPA policy gates the target."},
		{EdgeRunsBinary, "config", "Service", "Binary", "A declared service runs a binary (Nix modules)."},

		{EdgePublishesTo, "messaging", "Service or Function", "Topic", "Publishes to a topic (Zenoh, Nix service declarations)."},
		{EdgeSubscribesTo, "messaging", "Service or Function", "Topic", "Subscribes to a topic."},
		{EdgeQueries, "messaging", "Function", "Topic", "Issues a query on a topic (Zenoh)."},
		{EdgeAnswers, "messaging", "Function", "Topic", "Answers queries on a topic (Zenoh queryable)."},

		{EdgeFileChangesWith, "derived", "File", "File", "Co-change coupling mined from git history."},
		{EdgeSemanticallySimilarTo, "derived", "Function", "Function", "Embedding cosine similarity above threshold (opt-in)."},
		{EdgeRationaleFor, "derived", "Rationale", "Function or Class", "A WHY/SAFETY/NOTE annotation explains the target."},
	}
	sort.Slice(t, func(i, j int) bool { return t[i].Type < t[j].Type })
	return t
}()

EdgeTypes is the documented table of every edge kind, sorted by type.

View Source
var ErrCorruptDatabase = errors.New("corrupt database")

ErrCorruptDatabase is a sentinel error type for Mode 4 (corrupt header). Provided for callers that want errors.Is matching instead of substring. Currently unused upstream; reserved for future migration.

View Source
var ErrIndexFormatTooNew = errors.New("index format is newer than this code-graph build")

ErrIndexFormatTooNew is returned when a database was written by a newer code-graph than the one opening it.

View Source
var ErrIndexFormatUnsupported = errors.New("index format is no longer supported")

ErrIndexFormatUnsupported is returned when a database's format is older than this build can read; the fix is to rebuild the index.

Functions

func CacheDir

func CacheDir() (string, error)

CacheDir resolves the default cache directory (CODE_GRAPH_CACHE_DIR, then ~/.cache/code-graph, then the legacy location), creating it if needed.

func CanonicalSectionNames

func CanonicalSectionNames() []string

CanonicalSectionNames returns the ordered list of canonical ADR section names.

func IsSurfaceableCodeNode

func IsSurfaceableCodeNode(label, filePath string) bool

IsSurfaceableCodeNode reports whether a node represents concrete, openable source code — i.e. a legitimate result for the localization, ranking, and security-surface tools. It excludes two node classes that otherwise pollute result lists with entries the caller cannot act on:

  • Community pseudo-nodes: Louvain cluster aggregates (label "Community", no file). BFS over MEMBER_OF edges reaches them, and rank/localize surfaced them directly (observed live 2026-07-04: code_localize returned a "Getwd_cluster" Community node).

  • External-dependency stubs: CALLS_EXTERNAL targets carry Function/Method labels but have no file_path (e.g. os.WriteFile, github.com/DeusData/.../Executor.Execute). They are not code in THIS repo, so they cannot be opened or investigated.

A legitimate first-party symbol always has a non-empty file_path, so the empty-file_path check captures external stubs, and the label check captures Community aggregates (which also happen to have no file). Module / File nodes with real paths are retained — they are surfaceable.

func KnownEdgeType

func KnownEdgeType(t string) bool

KnownEdgeType reports whether t is a documented edge type.

func MaxADRLength

func MaxADRLength() int

MaxADRLength returns the maximum allowed ADR length for use by tool handlers.

func Now

func Now() string

Now returns the current time in ISO 8601 format.

func OpenPathWithAutoRecovery

func OpenPathWithAutoRecovery(dbPath, name string) (*Store, RecoveryEvent, error)

OpenPathWithAutoRecovery wraps OpenPath with optional auto-recovery for the three manual-recovery modes (Mode 4 corrupt header, Mode 5 orphan sidecar, Mode 7 BulkWrite crash). See bench/research/2026-05-10-corruption-recovery-classification.md for the safety analysis underlying this behavior.

Behavior:

  • Default (CODE_GRAPH_AUTO_RECOVERY unset): identical to OpenPath. The structured error propagates; the operator decides recovery.
  • Opt-in (CODE_GRAPH_AUTO_RECOVERY=1): when OpenPath returns an auto-feasible error shape, the function (a) removes the corrupt on-disk artifacts (.db, .db-wal, .db-shm), (b) re-runs OpenPath on the now-clean path, and (c) returns the fresh Store + RecoveryEvent identifying which mode was recovered. Caller must re-index from source — the returned Store is empty.

Auto-recovery is logged via slog.Warn so the operator sees what happened. Pass the project's `name` if known (used purely for logging — pass "" to skip).

Errors that do NOT match the three auto-feasible shapes propagate as-is regardless of the env var. Auto-recovery never fires for errors outside the documented feasibility set.

func ParseADRSections

func ParseADRSections(content string) map[string]string

ParseADRSections splits ADR content by canonical section headers. Only canonical headers (PURPOSE, STACK, ARCHITECTURE, PATTERNS, TRADEOFFS, PHILOSOPHY) are recognized as split boundaries. Other ## headers within content are treated as literal text within the current section.

func RenderADR

func RenderADR(sections map[string]string) string

RenderADR joins sections into markdown with canonical sections first (in order), followed by any non-canonical sections alphabetically.

func SkipsSidecarPath added in v0.9.1

func SkipsSidecarPath(project string) (string, error)

SkipsSidecarPath returns the sidecar path for a project database.

func UnmarshalProps

func UnmarshalProps(data string) map[string]any

UnmarshalProps deserializes JSON properties. Exported for use by cypher executor.

func ValidateADRContent

func ValidateADRContent(content string) error

ValidateADRContent checks that content contains all 6 canonical sections. Returns an error listing any missing sections.

func ValidateADRSectionKeys

func ValidateADRSectionKeys(sections map[string]string) error

ValidateADRSectionKeys checks that all keys in the map are canonical section names. Returns an error listing any invalid keys.

func WriteSkips added in v0.9.1

func WriteSkips(project string, files []SkippedFile) error

WriteSkips replaces the project's skip sidecar. An empty list removes it so a clean reindex leaves no stale report behind.

Types

type ADRecord

type ADRecord struct {
	Project   string `json:"project"`
	Content   string `json:"content"`
	CreatedAt string `json:"created_at"`
	UpdatedAt string `json:"updated_at"`
}

ADRecord holds a stored Architecture Decision Record.

type ArchitectureInfo

type ArchitectureInfo struct {
	TotalNodes  int                `json:"total_nodes,omitempty"`
	TotalEdges  int                `json:"total_edges,omitempty"`
	NodeLabels  []LabelCount       `json:"node_labels,omitempty"`
	EdgeTypes   []TypeCount        `json:"edge_types,omitempty"`
	Languages   []LanguageCount    `json:"languages,omitempty"`
	Packages    []PackageSummary   `json:"packages,omitempty"`
	EntryPoints []EntryPointInfo   `json:"entry_points,omitempty"`
	Routes      []RouteInfo        `json:"routes,omitempty"`
	Hotspots    []HotspotFunction  `json:"hotspots,omitempty"`
	Boundaries  []CrossPkgBoundary `json:"boundaries,omitempty"`
	Services    []ServiceLink      `json:"services,omitempty"`
	Layers      []PackageLayer     `json:"layers,omitempty"`
	Clusters    []ClusterInfo      `json:"clusters,omitempty"`
	FileTree    []FileTreeEntry    `json:"file_tree,omitempty"`
}

ArchitectureInfo holds the result of a codebase architecture analysis.

The summary fields (TotalNodes, TotalEdges, NodeLabels, EdgeTypes) populate when the caller requests the "summary" aspect (default since 2026-05-12). Detail aspects populate when requested individually or via "all".

type ClusterInfo

type ClusterInfo struct {
	ID        int      `json:"id"`
	Label     string   `json:"label"`
	Members   int      `json:"members"`
	Cohesion  float64  `json:"cohesion"`
	TopNodes  []string `json:"top_nodes"`
	Packages  []string `json:"packages"`
	EdgeTypes []string `json:"edge_types"`
}

ClusterInfo describes a community detected by the Louvain algorithm.

type ConfigStore

type ConfigStore struct {
	// contains filtered or unexported fields
}

ConfigStore provides persistent key-value configuration backed by SQLite. Stored in _config.db in the cache directory (separate from per-project DBs).

func OpenConfig

func OpenConfig() (*ConfigStore, error)

OpenConfig opens or creates the global config database.

func OpenConfigInDir

func OpenConfigInDir(dir string) (*ConfigStore, error)

OpenConfigInDir opens the config database in a specific directory (for testing).

func (*ConfigStore) All

func (c *ConfigStore) All() (map[string]string, error)

All returns all config key-value pairs.

func (*ConfigStore) Close

func (c *ConfigStore) Close() error

Close closes the config database.

func (*ConfigStore) Delete

func (c *ConfigStore) Delete(key string) error

Delete removes a config key.

func (*ConfigStore) Get

func (c *ConfigStore) Get(key, defaultVal string) string

Get returns the value for a key, or defaultVal if not set.

func (*ConfigStore) GetBool

func (c *ConfigStore) GetBool(key string, defaultVal bool) bool

GetBool returns a boolean config value (stored as "true"/"false").

func (*ConfigStore) GetInt

func (c *ConfigStore) GetInt(key string, defaultVal int) int

GetInt returns an integer config value.

func (*ConfigStore) Set

func (c *ConfigStore) Set(key, value string) error

Set stores a key-value pair (upsert).

type CrossPkgBoundary

type CrossPkgBoundary struct {
	From      string `json:"from"`
	To        string `json:"to"`
	CallCount int    `json:"call_count"`
}

CrossPkgBoundary represents cross-package call volume.

type Edge

type Edge struct {
	ID         int64
	Project    string
	SourceID   int64
	TargetID   int64
	Type       string
	Properties map[string]any
}

Edge represents a graph edge stored in SQLite.

func (*Edge) ConfidenceTier

func (e *Edge) ConfidenceTier() string

ConfidenceTier returns the stored confidence tier for an edge, defaulting to EXTRACTED when the property is absent (e.g. edges created before this column was introduced, or by passes that do not set it).

type EdgeEndpoint

type EdgeEndpoint struct {
	SourceID int64
	TargetID int64
	Type     string
}

EdgeEndpoint is the lightweight edge shape needed by topology-only consumers. It intentionally omits IDs, project names, and properties so a graph walk does not allocate full Edge objects or decode unrelated JSON.

type EdgeInfo

type EdgeInfo struct {
	FromName      string
	ToName        string
	Type          string
	Confidence    float64
	HasConfidence bool
}

EdgeInfo is a simplified edge for output.

type EdgeTypeInfo

type EdgeTypeInfo struct {
	Type string
	// Family groups related kinds for filtering: structure, calls, types,
	// dataflow, tests, config, messaging, derived.
	Family string
	// Source and Target name the node roles in "source -> target" order.
	Source, Target string
	Doc            string
}

EdgeTypeInfo documents one edge type.

type EmbeddingResult

type EmbeddingResult struct {
	NodeID   int64   `json:"node_id"`
	Name     string  `json:"name"`
	QName    string  `json:"qualified_name"`
	Label    string  `json:"label"`
	FilePath string  `json:"file_path"`
	Score    float64 `json:"score"`
}

EmbeddingResult holds a node ID and its cosine similarity score.

type EntryPointInfo

type EntryPointInfo struct {
	Name          string `json:"name"`
	QualifiedName string `json:"qualified_name"`
	File          string `json:"file"`
}

EntryPointInfo describes an entry point function.

type FileHash

type FileHash struct {
	Project string
	RelPath string
	SHA256  string
	MtimeNs int64 // file mtime in nanoseconds (for stat pre-filter)
	Size    int64 // file size in bytes (for stat pre-filter)
}

FileHash represents a stored file content hash with stat metadata for incremental reindex.

type FileTreeEntry

type FileTreeEntry struct {
	Path     string `json:"path"`
	Type     string `json:"type"`
	Children int    `json:"children"`
}

FileTreeEntry describes a node in the condensed file tree.

type HotspotFunction

type HotspotFunction struct {
	Name          string `json:"name"`
	QualifiedName string `json:"qualified_name"`
	FanIn         int    `json:"fan_in"`
}

HotspotFunction is a function with high fan-in.

type ImpactSummary

type ImpactSummary struct {
	Critical        int  `json:"critical"`
	High            int  `json:"high"`
	Medium          int  `json:"medium"`
	Low             int  `json:"low"`
	Total           int  `json:"total"`
	HasCrossService bool `json:"has_cross_service"`
}

ImpactSummary aggregates risk counts from a BFS traversal.

func BuildImpactSummary

func BuildImpactSummary(hops []*NodeHop, edges []EdgeInfo) ImpactSummary

BuildImpactSummary computes risk distribution from deduplicated node hops.

type LabelCount

type LabelCount struct {
	Label string `json:"label"`
	Count int    `json:"count"`
}

LabelCount is a label with its count.

type LanguageCount

type LanguageCount struct {
	Language  string `json:"language"`
	FileCount int    `json:"file_count"`
}

LanguageCount counts files per language.

type Node

type Node struct {
	ID            int64
	Project       string
	Label         string
	Name          string
	QualifiedName string
	FilePath      string
	StartLine     int
	EndLine       int
	Properties    map[string]any
}

Node represents a graph node stored in SQLite.

type NodeHop

type NodeHop struct {
	Node *Node
	Hop  int
}

NodeHop is a node with its BFS hop distance.

func DeduplicateHops

func DeduplicateHops(hops []*NodeHop) []*NodeHop

DeduplicateHops removes duplicate nodes from BFS results, keeping the minimum hop (highest risk) for each node.

type PackageLayer

type PackageLayer struct {
	Name   string `json:"name"`
	Layer  string `json:"layer"`
	Reason string `json:"reason"`
}

PackageLayer classifies a package into an architectural layer.

type PackageSummary

type PackageSummary struct {
	Name      string `json:"name"`
	NodeCount int    `json:"node_count"`
	FanIn     int    `json:"fan_in"`
	FanOut    int    `json:"fan_out"`
}

PackageSummary summarizes a package with its connectivity.

type PartialBatchInsertError

type PartialBatchInsertError struct {
	Dropped int
	Total   int
}

InsertEdgeBatch inserts multiple edges in batched multi-row INSERTs. PartialBatchInsertError is returned by InsertEdgeBatch when the per-edge fallback path skipped one or more edges (typically due to FK constraint violations from stale source/target IDs). The function completes — successful edges are inserted — but the error signals data loss so callers can choose to log, retry, or escalate.

PR #334 closed the most-common upstream cause of these FK violations (InsertEdge LastInsertId race); this error type exists so any remaining cause (resolver pointing at uncreated phantoms, concurrent deletes) surfaces explicitly rather than via Info-level logs that no caller checks. Use errors.As to inspect counts:

var partial *PartialBatchInsertError
if errors.As(err, &partial) { ... }

func (*PartialBatchInsertError) Error

func (e *PartialBatchInsertError) Error() string

type Project

type Project struct {
	Name              string
	IndexedAt         string
	RootPath          string
	EnrichmentVersion string
}

Project represents an indexed project.

type ProjectInfo

type ProjectInfo struct {
	Name     string
	DBPath   string
	RootPath string
}

ProjectInfo holds metadata about a discovered project database.

type Querier

type Querier interface {
	Exec(query string, args ...any) (sql.Result, error)
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
	Query(query string, args ...any) (*sql.Rows, error)
	QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
	QueryRow(query string, args ...any) *sql.Row
	QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
	Prepare(query string) (*sql.Stmt, error)
	PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
}

Querier abstracts *sql.DB and *sql.Tx so store methods work in both contexts. Both variants support the Context-accepting counterparts; we expose them so callers inside a WithTransaction block can honor caller cancellation without reaching past the tx (which would ask the single-connection pool for a second connection and deadlock on the write lock).

type QueryCache

type QueryCache struct {
	// contains filtered or unexported fields
}

QueryCache is a thread-safe LRU cache for query results. Eviction order is maintained by a doubly-linked list so promote-to-MRU and oldest-eviction are O(1) regardless of cache size. LRU is updated on Set (re-insert promotes), not on Get — preserving prior semantics.

func NewQueryCache

func NewQueryCache(maxSize int, ttl time.Duration) *QueryCache

NewQueryCache creates a cache with the given max entries and TTL.

func (*QueryCache) Get

func (c *QueryCache) Get(key string) (any, bool)

Get returns a cached result and true if found and not expired. Get does NOT promote — promotion happens on Set only.

func (*QueryCache) Invalidate

func (c *QueryCache) Invalidate()

Invalidate clears all cached entries.

func (*QueryCache) Len

func (c *QueryCache) Len() int

Len returns the number of cached entries.

func (*QueryCache) Set

func (c *QueryCache) Set(key string, result any)

Set stores a result in the cache.

type RecoveryEvent

type RecoveryEvent int

RecoveryEvent classifies what auto-recovery did, if anything.

Callers use this to decide whether to schedule a fresh re-index after the recovery cleared corrupt artifacts. See `OpenPathWithAutoRecovery`.

const (
	// RecoveryNone means OpenPath succeeded without recovery; no action needed.
	RecoveryNone RecoveryEvent = iota
	// RecoveryCorruptHeader means Mode 4 (corrupt header) was detected and
	// the on-disk artifacts were removed. The returned Store is a fresh
	// empty DB; caller must re-index from source.
	RecoveryCorruptHeader
	// RecoveryOrphanSidecar means Mode 5 (main DB missing + orphan WAL/SHM)
	// was detected and the orphan sidecars were removed. The returned Store
	// is a fresh empty DB; caller must re-index from source.
	RecoveryOrphanSidecar
	// RecoveryBulkWriteCrash means Mode 7 (BulkWrite/MEMORY-journal crash)
	// was detected and the inconsistent DB + sidecars were removed. The
	// returned Store is a fresh empty DB; caller must re-index from source.
	RecoveryBulkWriteCrash
)

func (RecoveryEvent) String

func (r RecoveryEvent) String() string

String returns the human-readable name for the event, used in logs and the operator-facing slog records.

type ReleaseFunc

type ReleaseFunc func()

ReleaseFunc must be called when the caller is done with the store.

type RiskLevel

type RiskLevel string

RiskLevel classifies impact based on BFS hop depth.

const (
	RiskCritical RiskLevel = "CRITICAL"
	RiskHigh     RiskLevel = "HIGH"
	RiskMedium   RiskLevel = "MEDIUM"
	RiskLow      RiskLevel = "LOW"
)

func HopToRisk

func HopToRisk(hop int) RiskLevel

HopToRisk maps a BFS hop depth to a risk level.

type RouteInfo

type RouteInfo struct {
	Method  string `json:"method"`
	Path    string `json:"path"`
	Handler string `json:"handler"`
}

RouteInfo describes an HTTP route.

type SchemaInfo

type SchemaInfo struct {
	NodeLabels           []LabelCount `json:"node_labels"`
	RelationshipTypes    []TypeCount  `json:"relationship_types"`
	RelationshipPatterns []string     `json:"relationship_patterns"`
	SampleFunctionNames  []string     `json:"sample_function_names"`
	SampleClassNames     []string     `json:"sample_class_names"`
	SampleQualifiedNames []string     `json:"sample_qualified_names"`
}

SchemaInfo contains graph schema statistics.

type SearchOutput

type SearchOutput struct {
	Results []*SearchResult
	Total   int
}

SearchOutput wraps search results with total count for pagination.

type SearchParams

type SearchParams struct {
	Project            string
	Label              string
	NamePattern        string // regex matched against short name only
	QNPattern          string // regex matched against qualified name only
	FilePattern        string
	Relationship       string
	Direction          string // "inbound", "outbound", "any"
	MinDegree          int
	MaxDegree          int
	MinComplexity      int // -1 = no filter; else require node's `complexity` property >= this
	MaxComplexity      int // -1 = no filter; else require node's `complexity` property <= this
	Limit              int
	Offset             int
	ExcludeEntryPoints bool     // when true, exclude nodes with is_entry_point=true
	IncludeConnected   bool     // when true, load connected node names (expensive, off by default)
	ExcludeLabels      []string // labels to exclude from results
	SortBy             string   // "relevance" (default), "name", "degree"
	CaseSensitive      bool     // false (zero value) = case-insensitive by default
}

SearchParams defines structured search parameters.

type SearchResult

type SearchResult struct {
	Node           *Node
	InDegree       int
	OutDegree      int
	ConnectedNames []string
}

SearchResult is a node with edge degree info.

type ServiceLink struct {
	From  string `json:"from"`
	To    string `json:"to"`
	Type  string `json:"type"`
	Count int    `json:"count"`
}

ServiceLink represents a cross-service link (HTTP or async).

type SkippedFile added in v0.9.1

type SkippedFile struct {
	Path   string `json:"path"`
	Reason string `json:"reason"` // "crash" or "timeout"
	Detail string `json:"detail,omitempty"`
}

SkippedFile records a source file the extraction supervisor could not index because the worker crashed or hung on it. Skips live in a sidecar next to the project database rather than in the schema, so recording one never changes the index format.

func ReadSkips added in v0.9.1

func ReadSkips(project string) ([]SkippedFile, error)

ReadSkips returns the project's recorded skips, or an empty slice when the sidecar does not exist.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store wraps a SQLite connection for graph storage.

func Open

func Open(project string) (*Store, error)

Open opens or creates a SQLite database for the given project in the default cache dir.

func OpenInDir

func OpenInDir(dir, project string) (*Store, error)

OpenInDir opens or creates a SQLite database for the given project in a specific directory.

func OpenMemory

func OpenMemory() (*Store, error)

OpenMemory opens an in-memory SQLite database (for testing).

func OpenPath

func OpenPath(dbPath string) (*Store, error)

OpenPath opens a SQLite database at the given path.

If the main .db file is missing but sidecar files (-wal or -shm) exist, this returns a structured error instead of silently re-creating an empty DB. The orphan-sidecar condition usually means the operator deleted the .db accidentally; silent re-create would be data loss with no signal. Recovery: call DeleteProject (or remove the sidecars manually) and re-index.

If neither main DB nor sidecars exist, this is a normal fresh-create.

func (*Store) AllEdges

func (s *Store) AllEdges(project string) ([]*Edge, error)

AllEdges returns every edge in a project. Used by whole-graph algorithms (PageRank, centrality) that need to load the full edge set into memory. For large projects consumers should prefer streaming or type-scoped reads.

func (*Store) AllNodes

func (s *Store) AllNodes(project string) ([]*Node, error)

AllNodes returns all nodes for a project.

func (*Store) BFS

func (s *Store) BFS(startNodeID int64, direction string, edgeTypes []string, maxDepth, maxResults int) (*TraverseResult, error)

BFS performs breadth-first traversal following edges of given types using a recursive CTE, replacing the previous per-node Go-side loop with a single SQL round-trip. direction: "outbound" follows source->target, "inbound" follows target->source. maxDepth caps the BFS depth, maxResults caps total visited nodes (clipping is reported via TraverseResult.Truncated). An empty edgeTypes list traverses ALL edge types — consistent with FindEdgesBySourceIDs / FindEdgesByTargetIDs. (Previously empty silently defaulted to CALLS; every caller passes an explicit list, and the one caller that wants untyped traversal — Cypher variable-length patterns with no relationship type — was silently narrowed.)

func (*Store) BFSNodes

func (s *Store) BFSNodes(startNodeID int64, direction string, edgeTypes []string, maxDepth, maxResults int) (*TraverseResult, error)

BFSNodes is BFS without the edge-collection query. The Cypher executor's variable-length expansion only consumes Visited/Truncated, and the edge CTE is as expensive as the node CTE — skipping it halves the per-source traversal cost for `-[*..]->` patterns.

func (*Store) BFSWithMinConfidence

func (s *Store) BFSWithMinConfidence(startNodeID int64, direction string, edgeTypes []string, maxDepth, maxResults int, minConfidence float64) (*TraverseResult, error)

BFSWithMinConfidence performs BFS while treating edges below minConfidence as absent. Edges with a missing or null confidence value remain traversable; an explicit numeric zero is filtered when minConfidence is positive. Applying the threshold to the recursive frontier guarantees every visited node has a retained path from the root.

func (*Store) BeginBulkWrite

func (s *Store) BeginBulkWrite(ctx context.Context)

BeginBulkWrite switches to MEMORY journal mode for faster bulk writes. Also boosts cache to 64 MB for write throughput. Call EndBulkWrite when done to restore WAL mode and adaptive cache.

Writes a Mode 7 crash-marker (RECOVERY_TAXONOMY.md) before switching journal mode. EndBulkWrite removes the marker. If the process is killed inside this window, the marker survives and the next OpenPath will run PRAGMA quick_check to detect MEMORY-journal-corruption that the missing on-disk journal can't recover.

func (*Store) BulkInsertEdges

func (s *Store) BulkInsertEdges(ctx context.Context, edges []*Edge) error

BulkInsertEdges inserts edges in batches using plain INSERT (no ON CONFLICT). Assumes no duplicates exist for the project after a prior DELETE. Honors ctx the same way BulkInsertNodes does.

func (*Store) BulkInsertNodes

func (s *Store) BulkInsertNodes(ctx context.Context, nodes []*Node) error

BulkInsertNodes inserts nodes in batches using plain INSERT (no ON CONFLICT). Assumes no duplicates exist for the project after a prior DELETE. Honors ctx — at the start of each chunk we check ctx.Err() so a cancel stops the next batch even before SQL would notice. The chunk itself also uses ExecContext so the in-flight statement aborts.

func (*Store) CallsConfidenceTierStats

func (s *Store) CallsConfidenceTierStats(project string) (map[string]int, error)

CallsConfidenceTierStats returns CALLS edge counts grouped by confidence_tier (EXTRACTED, HIGH, MEDIUM, LOW, SPECULATIVE, …). Same shape as CallsResolverRuleStats; uses the indexed confidence_tier_gen generated column. Gives operators a quick "how risky is this graph?" summary — a graph dominated by SPECULATIVE has different reliability characteristics than one dominated by HIGH, even if total edge count is the same.

func (*Store) CallsResolutionStats

func (s *Store) CallsResolutionStats(project string) (map[string]int, error)

CallsResolutionStats returns CALLS edge counts grouped by resolution_strategy.

func (*Store) CallsResolverRuleStats

func (s *Store) CallsResolverRuleStats(project string) (map[string]int, error)

CallsResolverRuleStats returns CALLS edge counts grouped by resolver_rule. Reads the indexed resolver_rule_gen generated column, so the aggregation stays cheap (O(distinct rules) seeks) even on graphs with millions of edges. Rows whose resolver_rule property is missing land in the "unset" bucket — these are typically pre-migration edges from older indexes or edges emitted by passes that don't categorize (HTTP_CALLS, INHERITS, etc., which CALLS rows shouldn't contain). The output is suitable for surfacing in index_health so operators can see precision-distribution at a glance ("how many edges came from cross-package-suffix vs cross-package-import-map?") without scanning JSON properties.

func (*Store) Checkpoint

func (s *Store) Checkpoint(ctx context.Context)

Checkpoint forces a WAL checkpoint, moving pages from WAL to the main DB, then runs PRAGMA optimize so the query planner has up-to-date statistics. PRAGMA optimize (SQLite 3.46+) auto-limits sampling per index, only re-analyzing stale stats. Cost is absorbed during indexing rather than the first read query.

func (*Store) Close

func (s *Store) Close() error

Close closes the database connection.

func (*Store) CosineSearch

func (s *Store) CosineSearch(project string, queryVec []float32, limit int) ([]EmbeddingResult, error)

CosineSearch finds the top-k nodes most similar to the query vector. Uses in-memory dot product (vectors are L2-normalized).

func (*Store) CountEdges

func (s *Store) CountEdges(project string) (int, error)

CountEdges returns the number of edges in a project.

func (*Store) CountEdgesByType

func (s *Store) CountEdgesByType(project, edgeType string) (int, error)

CountEdgesByType returns the number of edges of a given type for a project.

func (*Store) CountFileHashes added in v0.9.1

func (s *Store) CountFileHashes(project string) (int, error)

CountFileHashes returns the number of tracked files for a project.

func (*Store) CountNodes

func (s *Store) CountNodes(project string) (int, error)

CountNodes returns the number of nodes in a project.

func (*Store) CreateUserIndexes

func (s *Store) CreateUserIndexes(ctx context.Context) error

CreateUserIndexes recreates all user-created indexes (single sorted pass, O(N)). Honors ctx so a slow CREATE INDEX on a large table is cancellable.

func (*Store) DB

func (s *Store) DB() *sql.DB

DB returns the underlying sql.DB (for advanced queries).

Prefer Q() for query execution inside pipeline passes: Q() returns the active querier (either *sql.DB or *sql.Tx), which honors the surrounding transaction. Using DB() from code running under WithTransaction bypasses the tx and asks the single-connection pool for another connection, which blocks indefinitely on the write lock held by the outer tx.

func (*Store) DBPath

func (s *Store) DBPath() string

DBPath returns the filesystem path to the SQLite database.

func (*Store) DeleteADR

func (s *Store) DeleteADR(project string) error

DeleteADR removes a stored ADR for a project.

func (*Store) DeleteEdgesBetweenFiles

func (s *Store) DeleteEdgesBetweenFiles(project, edgeType string, srcFiles, tgtFiles []string) (int64, error)

DeleteEdgesBetweenFiles deletes edges of the given type whose SOURCE node lives in srcFiles AND whose TARGET node lives in tgtFiles, for the project. Used by the SCIP ingest pass to replace heuristic CALLS edges only where the index can actually re-derive them: requiring BOTH endpoints to be index-covered keeps heuristic edges into files the indexer cannot see (CGO sources, platform-gated files) — ground-truth eval showed source-only deletion cost 210 real edges on this repo. Returns the number of edges deleted.

func (*Store) DeleteEdgesByProject

func (s *Store) DeleteEdgesByProject(project string) error

DeleteEdgesByProject deletes all edges for a project.

func (*Store) DeleteEdgesBySourceFile

func (s *Store) DeleteEdgesBySourceFile(project, filePath, edgeType string) error

DeleteEdgesBySourceFile deletes edges of a given type where the source node belongs to a specific file. Used for incremental re-indexing of CALLS edges.

func (*Store) DeleteEdgesByType

func (s *Store) DeleteEdgesByType(project, edgeType string) error

DeleteEdgesByType deletes all edges of a given type for a project.

func (*Store) DeleteFileHash

func (s *Store) DeleteFileHash(project, relPath string) error

DeleteFileHash deletes a single file hash entry.

func (*Store) DeleteFileHashes

func (s *Store) DeleteFileHashes(project string) error

DeleteFileHashes deletes all file hashes for a project.

func (*Store) DeleteNodesByFile

func (s *Store) DeleteNodesByFile(project, filePath string) error

DeleteNodesByFile deletes all nodes for a specific file in a project.

func (*Store) DeleteNodesByLabel

func (s *Store) DeleteNodesByLabel(project, label string) error

DeleteNodesByLabel deletes all nodes with a given label in a project.

func (*Store) DeleteNodesByProject

func (s *Store) DeleteNodesByProject(project string) error

DeleteNodesByProject deletes all nodes for a project.

func (*Store) DeleteOrphanNodesByLabel

func (s *Store) DeleteOrphanNodesByLabel(project, label string) error

DeleteOrphanNodesByLabel deletes labeled nodes with no incoming or outgoing edges. Incremental passes use this after FK cascade removes changed sources.

func (*Store) DeleteProject

func (s *Store) DeleteProject(name string) error

DeleteProject deletes a project and all associated data (CASCADE).

func (*Store) DropUserIndexes

func (s *Store) DropUserIndexes(ctx context.Context) error

DropUserIndexes drops all user-created indexes for faster bulk writes. Honors ctx — callers cancelling a long-running bulk pass need the SQL to abort, not run to completion.

func (*Store) EdgeCountsByType

func (s *Store) EdgeCountsByType(project string) (map[string]int, error)

EdgeCountsByType returns edge counts grouped by edge type.

func (*Store) EmbeddingCount

func (s *Store) EmbeddingCount(project string) (int, error)

EmbeddingCount returns the number of embeddings stored for a project. Uses s.q so callers inside Store.WithTransaction don't deadlock on the single-connection pool.

func (*Store) EmbeddingModelCounts

func (s *Store) EmbeddingModelCounts(project string) (map[string]int, error)

EmbeddingModelCounts returns the observed stored embedding inventory for a project, grouped by the model recorded alongside each vector.

func (*Store) EndBulkWrite

func (s *Store) EndBulkWrite(ctx context.Context)

EndBulkWrite restores WAL journal mode, NORMAL synchronous, and adaptive cache.

Removes the Mode 7 crash-marker. Order matters: switch back to WAL (which forces a checkpoint) BEFORE removing the marker, so a crash between mode-switch and marker-removal still leaves a valid signal.

func (*Store) FindArchitectureDocs

func (s *Store) FindArchitectureDocs(project string) ([]string, error)

FindArchitectureDocs discovers existing architecture documentation files in a project. Returns file paths matching common architecture doc patterns.

func (*Store) FindCallerFilesOfTargetsInFiles

func (s *Store) FindCallerFilesOfTargetsInFiles(project string, targetFilePaths, edgeTypes []string) ([]string, error)

FindCallerFilesOfTargetsInFiles returns the set of file paths containing source nodes for any edge of one of the given types whose target node's file_path is in targetFilePaths.

This is the building block for Plan 1 Phase 3's invalidation rewrite. Today findDependentFiles only walks one hop of the import graph (callers of changed modules); it misses cases where a call site's resolution depended on a changed function in a different module that the caller's module DIDN'T import directly (transitive callers, type-based dispatch, stranded handlers). This helper lets the pipeline ask the question directly: "Which files contain edges pointing AT functions in the changed files?" The result is the union of those caller-files; if their target's resolution may have shifted, we re-resolve them.

Uses idx_nodes_file (project, file_path) to scope the target side and idx_edges_target_type (project, target_id, type) on the edge join — both present from initial schema, no migration needed. Edge types are filtered explicitly rather than IN() so the planner can use the composite index; the typical caller passes ["CALLS", "USAGE", "HTTP_CALLS", "ASYNC_CALLS", "INDIRECT_CALLS"].

Returns a deduped, sorted slice of relative file paths. Empty targetFilePaths or edgeTypes returns nil without querying.

func (*Store) FindEdgeEndpointsByTypes

func (s *Store) FindEdgeEndpointsByTypes(project string, edgeTypes []string) ([]EdgeEndpoint, error)

FindEdgeEndpointsByTypes returns only endpoint columns for the requested edge types. Callers that need topology but not edge metadata should prefer this over AllEdges: filtering happens in SQLite and properties are never materialized or decoded.

func (*Store) FindEdgesBySource

func (s *Store) FindEdgesBySource(sourceID int64) ([]*Edge, error)

FindEdgesBySource finds all edges from a given source node.

func (*Store) FindEdgesBySourceAndType

func (s *Store) FindEdgesBySourceAndType(sourceID int64, edgeType string) ([]*Edge, error)

FindEdgesBySourceAndType finds edges from a source with a specific type.

func (*Store) FindEdgesBySourceIDs

func (s *Store) FindEdgesBySourceIDs(sourceIDs []int64, edgeTypes []string) (map[int64][]*Edge, error)

FindEdgesBySourceIDs returns all edges where source_id is in the given set, optionally filtered by edge types. Groups results by source_id for efficient lookup.

func (*Store) FindEdgesByTarget

func (s *Store) FindEdgesByTarget(targetID int64) ([]*Edge, error)

FindEdgesByTarget finds all edges to a given target node.

func (*Store) FindEdgesByTargetAndType

func (s *Store) FindEdgesByTargetAndType(targetID int64, edgeType string) ([]*Edge, error)

FindEdgesByTargetAndType finds edges to a target with a specific type.

func (*Store) FindEdgesByTargetIDs

func (s *Store) FindEdgesByTargetIDs(targetIDs []int64, edgeTypes []string) (map[int64][]*Edge, error)

FindEdgesByTargetIDs returns all edges where target_id is in the given set, optionally filtered by edge types. Groups results by target_id.

func (*Store) FindEdgesByType

func (s *Store) FindEdgesByType(project, edgeType string) ([]*Edge, error)

FindEdgesByType returns all edges of a given type for a project.

func (*Store) FindEdgesByURLPath

func (s *Store) FindEdgesByURLPath(project, pathSubstring string) ([]*Edge, error)

FindEdgesByURLPath returns edges where url_path contains the given substring. Uses the generated column index for prefix matches, falls back to json_extract for substring.

func (*Store) FindNodeByID

func (s *Store) FindNodeByID(id int64) (*Node, error)

FindNodeByID finds a node by its primary key ID.

func (*Store) FindNodeByQN

func (s *Store) FindNodeByQN(project, qualifiedName string) (*Node, error)

FindNodeByQN finds a node by project and qualified name.

func (*Store) FindNodeIDsByQNs

func (s *Store) FindNodeIDsByQNs(project string, qns []string) (map[string]int64, error)

FindNodeIDsByQNs returns a map of qualifiedName → ID for the given QNs in a project.

func (*Store) FindNodeLabelsByQNs

func (s *Store) FindNodeLabelsByQNs(project string, qns []string) (map[string]string, error)

FindNodeLabelsByQNs returns a map of qualifiedName → label for the given QNs. Used by the CALLS pass to filter out edges targeting Variable/Class/File nodes (a CALLS edge should only target Function or Method). Before this existed, 38% of CALLS edges on some Rust projects pointed at Variable nodes generated from config files (diesel.toml entries, Cargo.toml), inflating false-positive rate with zero semantic value (see 2026-04-24 accuracy harness incident).

func (*Store) FindNodeSeedCandidates

func (s *Store) FindNodeSeedCandidates(project string, exactNameTokens, qnTokens []string) ([]*Node, error)

FindNodeSeedCandidates returns the minimal node projection that could match any exact-name or qualified-name token. It is used by substring seed selection to avoid loading every project node and decoding properties that cannot affect the match.

func (*Store) FindNodesByFile

func (s *Store) FindNodesByFile(project, filePath string) ([]*Node, error)

FindNodesByFile finds all nodes in a given file.

func (*Store) FindNodesByFileOverlap

func (s *Store) FindNodesByFileOverlap(project, fileSuffix string, startLine, endLine int) ([]*Node, error)

FindNodesByFileOverlap returns nodes whose line range overlaps [startLine, endLine]. The fileSuffix is matched with LIKE '%' || ? against the file_path column to handle relative/absolute path differences.

func (*Store) FindNodesByIDs

func (s *Store) FindNodesByIDs(ids []int64) (map[int64]*Node, error)

FindNodesByIDs returns a map of nodeID → *Node for the given IDs.

func (*Store) FindNodesByLabel

func (s *Store) FindNodesByLabel(project, label string) ([]*Node, error)

FindNodesByLabel finds all nodes with a given label in a project.

func (*Store) FindNodesByName

func (s *Store) FindNodesByName(project, name string) ([]*Node, error)

FindNodesByName finds nodes by project and name.

func (*Store) FindNodesByProperty

func (s *Store) FindNodesByProperty(project, label, propKey, propValue string) ([]*Node, error)

FindNodesByProperty finds nodes with a specific JSON property value. If label is non-empty, also filters by label.

func (*Store) FindNodesByQNSuffix

func (s *Store) FindNodesByQNSuffix(project, suffix string) ([]*Node, error)

FindNodesByQNSuffix finds nodes whose qualified_name ends with "."+suffix. Matches at QN segment boundaries to prevent partial word matches.

func (*Store) FindSimilarNodes

func (s *Store) FindSimilarNodes(project string, nodeID int64, limit int) ([]EmbeddingResult, error)

FindSimilarNodes returns the top-k embedded nodes most cosine-similar to the given node's own embedding, excluding the node itself. Returns nil, nil when the node has no embedding or no other embeddings exist for the project.

Reuses the same in-memory, L2-normalized embedding cache that CosineSearch uses, so repeated calls across all nodes in a pass do not re-scan SQLite. The cost is O(N * dim) per query where N is embedded nodes in the project — ~546 nodes × 2048-dim embeddings on rmf-corsair measures at <1ms per query on a modern CPU.

func (*Store) FormatVersionOf

func (s *Store) FormatVersionOf() (int, error)

FormatVersionOf reports the format version recorded in this store's database.

func (*Store) GetADR

func (s *Store) GetADR(project string) (*ADRecord, error)

GetADR retrieves a stored ADR for a project.

func (*Store) GetArchitecture

func (s *Store) GetArchitecture(project string, aspects []string) (*ArchitectureInfo, error)

GetArchitecture computes architecture aspects for a project. An empty/nil aspects slice defaults to the compact "summary" aspect only — see buildAspectSet for the full set of names. Callers that want detail fields (Languages / Packages / EntryPoints / Routes / Hotspots / Boundaries / Services / Layers / Clusters / FileTree) must pass them explicitly, or pass `[]string{"all"}` for everything.

The "empty = summary only" default landed in PR #301 (2026-05-12) so the cheap path is the default; the older "empty = all aspects" contract is removed. Callers that relied on the old contract must pass `[]string{"all"}` instead.

func (*Store) GetFileHashes

func (s *Store) GetFileHashes(project string) (map[string]FileHash, error)

GetFileHashes returns all file hashes with stat metadata for a project.

func (*Store) GetIncrementalsSinceFull

func (s *Store) GetIncrementalsSinceFull(project string) (int, error)

GetIncrementalsSinceFull returns how many incremental reindexes have run since the last full reindex for this project. The pipeline uses this to enforce a periodic-full-reindex sentinel (Plan 1 Phase 1), bounding the staleness window of any edges that the incremental dependency-discovery heuristic missed.

func (*Store) GetIndexIdentity

func (s *Store) GetIndexIdentity(project string) (*indexidentity.Record, error)

GetIndexIdentity returns the persisted envelope state. A legacy project with no identity row is represented explicitly as missing.

func (*Store) GetIndexProgress

func (s *Store) GetIndexProgress(name string) (phase string, pct int, detail string, err error)

GetIndexProgress returns the live index progress for a project.

func (*Store) GetProject

func (s *Store) GetProject(name string) (*Project, error)

GetProject returns a project by name.

func (*Store) GetSchema

func (s *Store) GetSchema(project string) (*SchemaInfo, error)

GetSchema returns graph schema statistics for a project.

func (*Store) IncrementIncrementalsSinceFull

func (s *Store) IncrementIncrementalsSinceFull(project string) error

IncrementIncrementalsSinceFull bumps the counter by 1. Called after a successful incremental reindex.

func (*Store) InsertEdge

func (s *Store) InsertEdge(e *Edge) (int64, error)

InsertEdge inserts an edge (dedup by source_id, target_id, type) and returns its row id.

Uses SQLite's RETURNING clause (available since 3.35, March 2021) so the id is read directly from the row written or updated by this statement. The pre-RETURNING path used LastInsertId(), which after `ON CONFLICT DO UPDATE` can return a STALE non-zero id — the most-recent-insert rowid the AUTOINCREMENT counter would have used, not the rowid of the conflict-targeted row. Callers that operated on the returned id (downstream node lookups, dedup) silently pointed at the wrong edge. The InsertEdgeBatch comment at edges.go:416 still names this as the upstream cause of its FK-violation fallback path.

Mirrors the fix applied to UpsertNode in PR #332 / commit 2085f8f.

func (*Store) InsertEdgeBatch

func (s *Store) InsertEdgeBatch(edges []*Edge) error

func (*Store) InvalidateEmbeddingCache

func (s *Store) InvalidateEmbeddingCache()

InvalidateEmbeddingCache clears the in-memory cache (call after reindex).

func (*Store) IterEmbeddedNodeIDs

func (s *Store) IterEmbeddedNodeIDs(project string) ([]int64, error)

IterEmbeddedNodeIDs returns the list of node IDs that currently have embeddings for this project, loading the cache if needed. Enables the similarity pass to enumerate every embedded node without re-querying SQLite.

func (*Store) ListFilesForProject

func (s *Store) ListFilesForProject(project string) ([]string, error)

ListFilesForProject returns source-file paths tracked by the incremental index. Node file_path also contains directory paths for Folder/Package nodes, so it is not a valid source of truth for deleted-file detection.

func (*Store) ListProjects

func (s *Store) ListProjects() ([]*Project, error)

ListProjects returns all indexed projects.

func (*Store) LoadNodeIDMap

func (s *Store) LoadNodeIDMap(ctx context.Context, project string) (map[string]int64, error)

LoadNodeIDMap returns a map of qualified_name → SQLite ID for all nodes in a project.

func (*Store) NodeDegree

func (s *Store) NodeDegree(nodeID int64) (inbound, outbound int)

NodeDegree returns inbound and outbound CALLS-family edge counts for a node. Includes CALLS, CALLS_EXTERNAL (real-to-stub), and CALLS_PSEUDO (module-default caller) so degree reflects the same surface users saw before the type split.

func (*Store) NodeNeighborNames

func (s *Store) NodeNeighborNames(nodeID int64, limit int) (callerNames, calleeNames []string)

NodeNeighborNames returns the names of callers and callees for a node, considering CALLS-family (CALLS, CALLS_EXTERNAL, CALLS_PSEUDO), HTTP_CALLS, and ASYNC_CALLS edge types.

func (*Store) Q

func (s *Store) Q() Querier

Q returns the active Querier. Inside WithTransaction, this is the tx; at other times, it is the raw *sql.DB. Passes should use Q for ad-hoc queries so they participate in whatever transaction their caller set up.

func (*Store) ReachableExcluding

func (s *Store) ReachableExcluding(startID, targetID int64, direction string, edgeTypes []string, maxDepth int, exclude map[int64]bool) (bool, error)

ReachableExcluding reports whether targetID is reachable from startID within maxDepth hops, following edges of the given types in the given direction, WITHOUT routing through any node in the exclude set. A node in exclude is a cut point: it is never entered, so no path may pass through it (the target itself is still detectable even if excluded).

Taint analysis uses this to decide sanitization soundly: a (source, sink) pair is "sanitized" iff the sink is NOT reachable from the source once every sanitizer/auth_boundary node is excluded — i.e. EVERY path from source to sink crosses a sanitizer. If any sanitizer-free path exists, the pair is unsanitized. This replaces the prior heuristic, which flagged a pair sanitized whenever a sanitizer merely appeared anywhere in the BFS-reachable set and so produced false "sanitized" verdicts when the sanitizer sat on an unrelated branch.

func (*Store) ResetIncrementalsSinceFull

func (s *Store) ResetIncrementalsSinceFull(project string) error

ResetIncrementalsSinceFull zeroes the counter. Called after a successful full reindex.

func (*Store) RewriteProject added in v0.9.1

func (s *Store) RewriteProject(ctx context.Context, oldName, newName, newRoot string) error

RewriteProject renames a project in place: the projects row, every table with a project column, the project prefix of node qualified names, and qualified names embedded in node/edge properties JSON. Used when an exported graph artifact is imported for a checkout at a different path (project names are derived from the absolute repository path).

func (*Store) Search

func (s *Store) Search(params *SearchParams) (*SearchOutput, error)

Search executes a parameterized search query with pagination support.

func (*Store) SetEnrichmentVersion

func (s *Store) SetEnrichmentVersion(name, version string) error

SetEnrichmentVersion updates the enrichment_version for a project.

func (*Store) SetIndexIdentity

func (s *Store) SetIndexIdentity(project string, identity *indexidentity.Envelope) error

SetIndexIdentity persists a successfully captured checkout identity.

func (*Store) SetIndexIdentityState

func (s *Store) SetIndexIdentityState(project, status, reason string) error

SetIndexIdentityState invalidates any previous envelope and records why a coherent identity is not currently available.

func (*Store) SetIndexProgress

func (s *Store) SetIndexProgress(name, phase string, pct int, detail string) error

SetIndexProgress updates the live index progress for a project.

func (*Store) SetNodeIntProperty

func (s *Store) SetNodeIntProperty(project, qualifiedName, key string, value int) (int64, error)

SetNodeIntProperty atomically sets a single integer property on a node by project + qualified_name. Uses SQLite's json_set so the rest of the properties map is preserved. Returns the number of rows updated (0 if the node doesn't exist).

Added 2026-05-02 for the unresolved_call_count diagnostic. Generalized so future int counters (e.g. cyclomatic_call_count, dispatch_count) can use the same path without re-implementing read-modify-write.

func (*Store) ShortestPath

func (s *Store) ShortestPath(startID, targetID int64, direction string, edgeTypes []string, maxDepth int) ([]int64, error)

ShortestPath returns the node-ID sequence of a shortest path (fewest hops) from startID to targetID following edges of the given types in the given direction, bounded by maxDepth, or nil if no such path exists within the bound. It is a BFS that records one parent pointer per discovered node and reconstructs the path by walking parents back from the target. It is used to produce a concrete witness path (e.g. to name the sanitizer on a sanitized taint path).

func (*Store) SnapshotTo added in v0.9.1

func (s *Store) SnapshotTo(path string) error

SnapshotTo writes a consistent copy of the database to path using `VACUUM INTO`, which produces a compact single-file image regardless of the WAL state of the live database. path must not exist.

func (*Store) StoreADR

func (s *Store) StoreADR(project, content string) error

StoreADR persists an ADR (upsert). The source_hash column is kept as a dead column to avoid ALTER TABLE issues — we write an empty string.

func (*Store) UpdateADRSections

func (s *Store) UpdateADRSections(project string, sections map[string]string) (*ADRecord, error)

UpdateADRSections merges the provided sections into the existing ADR. Unmentioned sections are preserved. Returns the updated record. Returns an error if the merged content exceeds maxADRLength.

func (*Store) UpsertEmbedding

func (s *Store) UpsertEmbedding(nodeID int64, model string, vec []float32) error

UpsertEmbedding stores or updates the embedding vector for a node. Uses s.q (the active Querier) so this can run inside Store.WithTransaction without deadlocking on the single-connection pool — same pattern as UpsertEmbeddingBatch (see comment there for the deadlock story).

func (*Store) UpsertEmbeddingBatch

func (s *Store) UpsertEmbeddingBatch(nodeIDs []int64, model string, vecs [][]float32) error

UpsertEmbeddingBatch stores embeddings for multiple nodes.

Uses the store's active Querier (s.q) rather than calling s.db.Begin() so that when this is invoked from inside Store.WithTransaction (as the whole indexing pipeline does), the writes participate in the outer tx instead of opening a nested one. A nested tx on the SetMaxOpenConns(1) pool deadlocks waiting for the write lock held by the outer tx — this was the root cause of TestMemoryStability hanging and of the multi-minute stalls observed at "phase=embeddings pct=97" during live indexing (2026-04-22).

When called outside a tx (s.q == s.db), each prepared Exec auto-commits. That is fine for this call site: UpsertEmbeddingBatch is invoked from the embeddings pass which runs inside the pipeline's WithTransaction; the only other caller would be ad-hoc tooling where atomicity across 64 rows is not a correctness requirement.

func (*Store) UpsertFileHash

func (s *Store) UpsertFileHash(project, relPath, sha256 string, mtimeNs, size int64) error

UpsertFileHash stores a file's content hash with stat metadata.

func (*Store) UpsertFileHashBatch

func (s *Store) UpsertFileHashBatch(hashes []FileHash) error

UpsertFileHashBatch inserts or updates multiple file hashes in batched multi-row INSERTs.

func (*Store) UpsertNode

func (s *Store) UpsertNode(n *Node) (int64, error)

UpsertNode inserts or replaces a node (dedup by qualified_name) and returns the node's row id.

Uses SQLite's RETURNING clause (available since 3.35, March 2021) so the id is read directly from the row written or updated by this statement. The pre-RETURNING path used LastInsertId() with a fallback SELECT when it returned 0, and accepted "occasional FK failures in downstream edge inserts" when LastInsertId returned a stale (non-zero) id on a conflict. RETURNING closes that race: SQLite emits one row per inserted or updated row, so the id is always the id of the row this statement just touched, regardless of conflict behavior.

func (*Store) UpsertNodeBatch

func (s *Store) UpsertNodeBatch(nodes []*Node) (map[string]int64, error)

UpsertNodeBatch inserts or updates multiple nodes in batched multi-row INSERTs. Returns a map of qualifiedName → ID for all upserted nodes.

func (*Store) UpsertProject

func (s *Store) UpsertProject(name, rootPath string) error

UpsertProject creates or updates a project record.

func (*Store) WALSize

func (s *Store) WALSize() int64

WALSize returns the current WAL file size in bytes, or -1 if unavailable. Useful for diagnosing memory bloat from un-checkpointed WAL files.

func (*Store) WithLargeCache

func (s *Store) WithLargeCache(ctx context.Context, fn func() error) error

WithLargeCache temporarily boosts the page cache to 64 MB for heavy read operations (e.g. GetSchema, Louvain clustering), then restores the adaptive default.

func (*Store) WithTransaction

func (s *Store) WithTransaction(ctx context.Context, fn func(txStore *Store) error) error

WithTransaction executes fn within a single SQLite transaction. The callback receives a transaction-scoped Store — all store methods called on txStore use the transaction. The receiver's q field is never mutated, so concurrent read-only handlers (using s.q == s.db) are unaffected.

type StoreRouter

type StoreRouter struct {
	// contains filtered or unexported fields
}

StoreRouter manages per-project SQLite databases. Each project gets its own .db file in the cache directory. Idle stores are evicted after idleTimeout (configurable) to free memory.

func NewRouter

func NewRouter() (*StoreRouter, error)

NewRouter creates a StoreRouter, ensuring the cache directory exists. Runs migration from single-DB layout if needed.

func NewRouterWithDir

func NewRouterWithDir(dir string) (*StoreRouter, error)

NewRouterWithDir creates a StoreRouter using a custom directory (for testing). No migration is run.

func (*StoreRouter) AcquireStore

func (r *StoreRouter) AcquireStore(project string) (*Store, ReleaseFunc, error)

AcquireStore returns a store with an incremented ref count plus a release function. The evictor will not close the store while refs > 0.

func (*StoreRouter) AllStores

func (r *StoreRouter) AllStores() map[string]*Store

AllStores opens all .db files in the cache dir and returns a name→Store map.

func (*StoreRouter) CloseAll

func (r *StoreRouter) CloseAll()

CloseAll closes all open Store connections.

func (*StoreRouter) DeleteProject

func (r *StoreRouter) DeleteProject(name string) error

DeleteProject closes the Store connection and removes the .db, WAL/SHM sidecars, and the BulkWrite crash marker. The marker must be in this set: it survives a SIGTERM'd indexing run, and leaving it orphaned means the documented Mode 7 recovery (delete_project + index_repository force=true) can still trip the crash-marker check on the recreated DB (observed 2026-06-11 on a Loc-Bench eval instance — every retry failed until the marker was removed by hand).

func (*StoreRouter) Dir

func (r *StoreRouter) Dir() string

Dir returns the cache directory path.

func (*StoreRouter) ForProject

func (r *StoreRouter) ForProject(name string) (*Store, error)

ForProject returns the Store for the given project, opening it lazily. Updates lastUsed but does NOT hold a ref: the evictor may close the returned store once it sits idle past idleTimeout (30s). Only safe for short-lived use that completes well inside that window. Operations that can run longer — indexing, agent loops, report generation on large graphs — MUST use AcquireStore/UseStore so the ref blocks eviction for their full duration (see the 2026-06-11 incident note in tools/index.go).

func (*StoreRouter) HasProject

func (r *StoreRouter) HasProject(name string) bool

HasProject checks if a .db file exists for the given project (without opening it).

func (*StoreRouter) ListProjects

func (r *StoreRouter) ListProjects() ([]*ProjectInfo, error)

ListProjects scans .db files and queries each for metadata. Uses AcquireStore to prevent the evictor from closing stores mid-query. Individual DB failures are logged and skipped (never block the full list).

func (*StoreRouter) OnDelete

func (r *StoreRouter) OnDelete(fn func(name string))

OnDelete registers a callback invoked after a project is deleted.

func (*StoreRouter) StartEvictor

func (r *StoreRouter) StartEvictor(ctx context.Context)

StartEvictor runs a background goroutine that closes idle stores. Ticks every 5 seconds. Evicts stores idle for > idleTimeout with refs == 0. Exits when ctx is cancelled.

func (*StoreRouter) UseStore

func (r *StoreRouter) UseStore(project string, fn func(*Store) error) error

UseStore opens the store for project, calls fn, then releases. Handles ref counting.

type TraverseResult

type TraverseResult struct {
	Root    *Node
	Visited []*NodeHop
	Edges   []EdgeInfo

	// Truncated is true when the visited set was clipped at maxResults —
	// the traversal had more matching nodes than the limit allowed.
	// Callers that aggregate over Visited (e.g. the Cypher executor's
	// variable-length expansion) must surface this instead of silently
	// undercounting.
	Truncated bool
}

TraverseResult holds BFS traversal results.

type TypeCount

type TypeCount struct {
	Type  string `json:"type"`
	Count int    `json:"count"`
}

TypeCount is a relationship type with its count.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL